Update 2010-08-08 The source code to this series can be found here
I have been struggling with spring security for some time now. It seems that all project need a complete security solution of their own and most of them assume that its some thing that can be solved in rather short time. Generally I would agree. Security is some thing which done right is solvable in very short time with tried and tested libraries. But if the library is a memoth of configuration then you will start running into dead ends. I have been moaning about lack of Spring security features in Spring-ROO, but that is some thing which is very easily solvable as an addon and with 1.1.M3 this should be doable easily with OSGi bundles, but some one still has to do it. I will give it a shot but I cannot promise any thing with my GSoC and other activities.
So instead I will post a series of Tutorials geared towards beginning ROO users to incorporate security into their application.
The first thing I want to say before I start going into any code is to clarify to the new user that ROO is not a framework, it wont do every thing for you, you will have to learn the underlying components of Spring, Spring MVC and other libraries (like Spring Security, ActiveMQ) and tools (like maven). Ofcourse you need to be fairly comfortable with Java as a language. I will not recommend ROO (or for that matter Spring) to any one who is not OK with Java code in general. Second is that an observation, Spring team is really great with documentation, but the more documentation there is, the better chance people have of making their code glue together. In spring security space I highly recommend Spring Security 3 book.
Now lets get started.
So first thing you probably want to do is to create a ROO project, create a POJO called SystemUser (not User because its a reserved word in SQL tables, you can always change the text bundle of your entity so it says User, even if it is called SystemUser). For that you need to create a folder (call it tutorial) and fire up your ROO shell (I prefer the git build
project --topLevelPackage com.hatimonline.roosec --projectName tutorial persistence setup --provider HIBERNATE --database H2_IN_MEMORY entity --class com.hatimonline.roosec.domain.SystemUser --testAutomatically field string --fieldName username --notNull field string --fieldName password --notNull logging setup --level DEBUG controller all --package ~.web perform tests (for some reaons this is failing on my box right now) perform eclipse
So if all goes well, in the above roo script we just created an entity, system user entity which has a username and password. Both are strings, nothing out of the ordinary. You can test the application by doing a
mvn tomcat:run from bash shell.
The first thing you will want is to make username unique, this is simple as putting one annotation in the SystemUser.java file. I recomment that you import your project in Eclipse (or even better STS). I usually prefer to disable the ROO support in STS since I use git based roo-dev. If you have STS then after importing your project, you will bed asked if AspectJ support should be enabled (and would require a restart). Just wait for the application to import every thing (see bottom right hand corner) and then click Yes. STS will restart. From there on hist Ctrl + Shift + R and type partially SystemUser.java (you should be able to select it and start editing it). You can let you ROO shell run outside of eclipse meanwhile.
Add the following on top of usename field.
@Column(unique=true) @Size(min = 5, max=30)
Notice that the Size annotation could also have been added via ROO. At this point you should be missing some import files, do a Ctrl + Shift + O to organize imports.
Start your tomcat server again. Try to save a username appadmin into the application, it should work the first time. If you try to do it again it would now fail with a ConstraintViolationException. This is expected because we tried to add to a unique column a non-unique value. So how can you avoid this. One way is to handle this in the Controller.
Add the following in your SystemUserController.java file (remember the shortcut Ctrl + Shift + R)
@Autowired
private Validator systemUserValidator;
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(systemUserValidator);
}
So what this snippet is doing. It’s autowiring a validator called SystemUserValidator and settig it in you initBinder (I would encourage you to see details in Spring-MVC section of Spring documentation)
At this point you need to create the validator which is autowired. I have skipped the import and package statement for brevity. You may like to put this in a validator subpackage of you source.
@Component
public class SystemUserValidator extends LocalValidatorFactoryBean implements Validator {
private static final Log logger = LogFactory.getLog(SystemUserValidator.class);
@Override
public boolean supports(Class<?> clazz) {
return SystemUser.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
super.validate(target, errors);
SystemUser user = (SystemUser) target;
if (user != null) {
rrors.rejectValue("username", "The username '"+user.getUsername()+"' is already in use", "The username '"+user.getUsername()+"' is already in use");
}
else {
errors.reject("SystemUser object not available");
}
}
}
If you follow the code you will see that this will always result in validation failing. This is not some thing you want to happen. So let’s fix that. One solution that I came up with was to put in a ROO based dynamic finder for SystemUser
finder add --finderName findSystemUsersByUsername --class com.tinyisv.tdca.domain.SystemUser
This will add the capability to search for a SystemUser entity from your database. Once done that you can now add an if condition before you return the error in the validator. This is rather ugly, please help me get a better if statement
if( SystemUser.findSystemUsersByUsername(user.getUsername()).getResultList().size() > 0 )
If you had roo shell running, you will already get a controller placed for the finder we generated above. The validation should be working now (but as we will see that we need to refine it further if we want to be able to edit our username for a persisted user, as the current condition wont let us persist the same username again)
You will also notice that if you have JSR303 annotations on your entity, their validation sould also work, thanks to @Valid.
This is all for the current tutorial. I am planning to extend the tutorial in the following direction.
- Actually add spring security, boot strap an admin user and put in UserDetails Service
- Add more fields and their validation
- Add support for basic Auditing using AOP (who created, and who last modified SystemUser or any entity)
- Add ACL schema so as to create a hieracrhy of users who can have varied permissions
My goal was to learn spring security on top of ROO and share it with others. I hope that some one much better than me is already working on a spring-roo security addon. If not then I might just give it a shot in a coming few weeks.
I will also try to share the project code on github, right now it is straight forward enough. Please comment and let me know if you have any improvements. I am still just a student


