We are evaluating jsecurity for some of our projects. Most will have non-web and web clients: ejb, spring-or-similar, swing, automated test clients, console, etc. All are using jpa (hibernate).
Right now I would be satisfied with some unit tests that exercise simple business objects and jpa/dao in a jsecurity context. Next will be to implement the business objects in a non-web client, then a j2ee web client.
Here is what I have so far:
-jpa/dao layer to access user/role/permission object
-junit tests to persist test data on test setup()
-created JpaRealm: extends AuthorizingRealm, is similar to JdbcRealm but simplified since I'm just calling userDao, roleDao, etc. find methods.
-Tried to use DefaultSecurityManager but had errors due to its dependency on servlet api
-Wrote AppSecurityManager to avoid servlet dependencies but not seeing how to manage sessions, subject creation, etc.
-Tried DefaultSecurityManager again with servlet api included...but getting null value errors in doGetAccount()
1) It seems that I should create an AppSecurityManager as a base for non-webclient types and use Default... or WebSecurityManager for web clients. But this is not clear.
2) The role sessions play in this is not clear, nor how to implement them in my own XSecurityManager...or if I even need them?
Hello, JPA is good stuff -
Hello,
JPA is good stuff - all of the JSecurity core uses it in our own projects that also use JSecurity (well, Hibernate directly via Spring, but you get the idea).
Concerning the JpaRealm: this makes perfect sense, but you may or may not need to extend AuthorizingRealm. AuthorizingRealm does caching of Account data, but this is not necessary if you're using a Hibernate 2nd-level cache to cache your JPA-aware User objects. You can subclass AuthenticatingRealm and call your DAO methods for each realm method call. If the DAO layer implements caching, you don't have to worry about EIS round-trips when doing a User lookup for an authc/authz check. I also find it beneficial to have my User class implement the Account interface, and JSecurity can just use my User objects directly - your choice if you want to do that or not, but it usually simplifies things. If you choose not to use JSecurity in the future, all that is usually required is to remove that interface from the User class's implements clause, so given the simplicity benefits, its usually an acceptable thing to do that.
As far as the DefaultSecurityManager - this is the one you should be using in non-web environments. It internally uses the org.jsecurity.util.ThreadContext class which is the only part of JSecurity core that has a dependency on the servlet .jar. It will not use any of the Servlet classes during runtime, but the class has a classloader dependency on the API. This isn't ideal, to say the least, so I've created a new issue to remove that dependency on the next release. For now though, it won't hurt including servlet-api.jar in the runtime classpath.
For the null value errors in doGetAccount() that is because your Realm implementation must return an org.jsecurity.authc.Account (or org.jsecurity.authz.AuthorizingAccount) instance that represents your User class. If you implement the Account/AuthorizingAccount interface in your User class and return the user instance directly as that method's return value, you shouldn't see any null problems anymore. If you don't want to implement the Account/AuthorizingAccount interface in your User class, you can manually instantiate, say org.jsecurity.authc.SimpleAccount or org.jsecurity.authz.SimpleAuthorizingAccount, copy over the relevant properties from your User instance into it, and return it instead. Not as 'clean' as implementing the interface in my opinion, but of course its your choice.
Sessions are exactly as you think them to be - a time-based context for holding data. You can use them in any application, web or non web if you need them, for whatever you desire. Some apps don't need them at all, and its perfectly acceptable not to use them. The apps that do use them find them useful in a number of ways, but the most popular seem to be 1) Storing sessions in a relational database so they can be accessed later, say, for reporting purposes - JSecurity makes this trivial with its SessionDAO or 2) Sharing session state between your business tier (no HTTP dependencies) and your client tiers (Web MVC, Flex, Swing, etc). In this set-up, the business tier can set Session state without ever referencing the Servlet API (as it should be) and that state can then be accessed simultaneously in a web MVC page, a Flex application, Swing app, etc, etc.
So, if you want to use them, you don't really have to do anything to access them. Just SecurityUtils.getSubject().getSession() is good enough to create one if one does not yet exist for the current subject/user.
Finally, in a web environment, you should use the JSecurityFilter, which automatically creates a DefaultWebSecurityManager. If for some reason you can't use the JSecurityFilter or choose not to, the DefaultWebSecurityManager is definitely the instance you should use. If you feel that you need to subclass one of our default SecurityManager implementations, we're probably not doing something right since they are extremely flexible. If you still can't get either to work depending on your web/non-web environments, let me know and I'll work with you to resolve it because that shouldn't happen ;)
I really need to make a Spring/Hibernate example application that shows how I implement my realm. Here's how I set my domain model up - maybe it will give you some ideas:
A User has a collection of Role objects. A Role object has a collection of Permission objects (or permission Strings, either is ok).
My User implements the JSecurity AuthorizingAccount interface and is returned from my HibernateRealm implementation's doGetAccount() call.
Because it implements AuthorizingAccount, I let my User class do its own authorization - in the hasRole("rolename") call, it basically does the following:
for( Role role : getRoles() ) {if ( role.getName().equals( rolenameMethodArg ) ) {
return true;
}
}
return false;
I.e. this is pure OO delegation, and I rely on JPA to lazily pull in the Set<Role> collection for me, either via an SQL query or a 2nd-level cache hit.
The hasPermission* methods are similar - I iterate over the Role collection, and for each one, check if ( role.hasPermission( permString) ) ) return true;
I hope this gives some insight as to how you might go about your app.
Let me know if you have any other questions!
Cheers,
Les
Round 2, Extending AuthorizingAccount and Account
Here's where I am:
- I have a domain entities User-m2m-Role-m2m-Permission set up.
- Not sure of application requirements related to fine-grained permissions so I'm focusing on Roles first
- Implementation seems straightforward (see below) except for:
- AuthorizingAccount checkRole, checkRoles (and checkPermission, checkPermissions)
- Account getCredentials, getPrincipals
Thanks!
//jsecurity AuthorizingAccount Implementation
public void checkPermission(Permission arg0) throws AuthorizationException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void checkPermissions(Collection arg0) throws AuthorizationException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void checkRole(String arg0) {
//??? What happens here?
}
public void checkRoles(Collection arg0) {
//??? What happens here?
}
public boolean hasAllRoles(Collection roleNames) {
for(String roleName: roleNames){
if(!this.hasRole(roleName)){
return false;
}
}
return true;
}
public boolean hasRole(String roleName) {
return this.getRoles().containsKey(roleName);
}
public boolean[] hasRoles(List roleNames) {
boolean[] roleMask = new boolean[roleNames.size()];
int size = roleNames.size();
for(int i=0;i permissionNames) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean isPermittedAll(Collection per) {
throw new UnsupportedOperationException("Not supported yet.");
}
// jsecurity Account Implementation
public Object getCredentials() {
Object o=null;
//??? What type of return object do I create?
return o;
}
public PrincipalCollection getPrincipals() {
SimplePrincipalCollection spc = new SimplePrincipalCollection();
//???
return spc;
}
public boolean isCredentialsExpired() {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean isLocked() {
throw new UnsupportedOperationException("Not supported yet.");
}
Re: Round 2, Extending AuthorizingAccount and Account
I forgot to ask - are you using a 2nd-level cache? (e.g. Ehcache)
Re: Round 2, Extending AuthorizingAccount and Account
Hi eptx!
Looks good so far - you're really close. I'll whip up a little Spring/Hibernate application tonight with full User/Role/Permission mappings and a corresponding Realm implementation so you can see exactly what I'm talking about. I've done this more times than I care to remember for commercial applications, so it shouldn't take me long ;)
Cheers,
Les
Sounds Great
A junit test with it would be be great to show a simple example of a non-web client accessing the same security context.
Thanks!
Re: Sounds Great
Hi eptx,
I just committed a small Spring/Hibernate sample application. You'll have to check out the project from subversion or you can browse the source tree (a .war directory structure) here: http://jsecurity.svn.sourceforge.net/viewvc/jsecurity/trunk/samples/spring-hibernate/
You'll find the User class (which implements the AuthorizingAccount interface) useful, as well as the DefaultRealm implementation.
You'll notice that I did have DefaultRealm extend AuthorizingRealm (instead of AuthenticatingRealm directly like I originally recommended). I forgot that a lot of the work was done for you already if the User class implements the AuthorizingAccount interface, as the sample app's does. You'll probably want to do the same.
You'll get the most value checking-out from the SVN repository so you can browse fully the domain classes, hibernate mapping files and applicationContext.xml.
After checking out, run ant:
> antThen you can cd into the build directory. There you'll see a
jsecurity-spring-hibernate.warthat you can drop into Tomcat, JBoss, etc. to verify it starts successfully.One notice though: I didn't include any GUI/JSP code since I didn't have time. But GUI integration is orthogonal to what you're trying to accomplish, so I didn't worry about it. Plus there is always the quickstart .war or spring-only .war sample apps that do have GUI code if you're curious how a web app would use JSecurity web support.
Finally, I didn't have time to implement the unit test that you request, but I should get to it hopefully soon (maybe this weekend).
I hope this helps!!!
Cheers,
Les