Taglib & cache?

I keep running into a problem using the jsecurity taglib tags, (jsec:hasRole, jsec:hasAnyRoles). Sometimes they work and sometimes they don't. I'm using them to conditionally display part of a JSP that both students and staff have access to so I don't have to create two separate JSPs.

It seems that after a period of time, a section in my JSP that is for student roles only simply does not appear any more, even though I'm currently logged in with the Subject with a role of STUDENT. All parts inside of the jsec tags do not display.

For example,
<jsec:hasRole name="STUDENT">
... display a table with student only data
</jsec:hasRole>
<jsec:hasRole name="STAFF">
... display a table with staff only data
</jsec:hasRole>

I am currently using the latest jsecurity.jar out of the box, without any configuration or adding of the ehcache jars. However, I have created the following classes to implement jsecurity in my webapp:
DefaultRealm extends AuthorizingRealm
Role (POJO)
User implements AuthorizingAccount

I have also implemented the following code to log users in
DefaultRealm securityRealm = new DefaultRealm();
securityRealm.init();
DefaultSecurityManager securityMgr = new DefaultSecurityManager(securityRealm);
securityMgr.init();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);

Subject currentUser = securityMgr.login(token);
if (currentUser.isAuthenticated())
... do something

Now, things seem to be working fine until the tags stop working and I was guessing that the jsecurity default internal cache was timing out and that that's where the tags were crapping out. However, this is simply a guess.

Any suggestions would be greatly appreciated.

Thanks,
Todd Kofford
tkofford@ku.edu

Hi Todd,

Hi Todd,

Good to hear from you again.

I see that you're manually constructing a DefaultSecurityManager, which doesn't support web environments.

The way to go in a web app is to use the JSecurityFilter in web.xml. This will construct the appropriate SecurityManager instance using your realm.

0.9 beta 2, which is what I'm assuming you have since it is the latest doesn't handle realm configuration as elegantly as I would like (we're fixing this as we speak though and should be out shortly). This will require you to subclass JSecurityFilter, e.g. AppSecurityFilter and override the getSecurityManager() method to do something like this:

public SecurityManager getSecurityManager() {
if ( this.securityManager == null ) {
DefaultRealm realm = new DefaultRealm();
realm.init();
DefaultWebSecurityManager dwsm = new org.jsecurity.web.DefaultWebSecurityManager(realm);
this.securityManager = dwsm;
}
return this.securityManager;
}

Then you declare your AppSecurityFilter exactly as if it was the JSecurityFilter (see the JSecurityFilter JavaDoc or the 0.9 beta2's web.xml for an example).

I'm REALLY sorry that you have to go through this subclassing garbage. That is being eliminated as quickly as possible - we're working on pure string-based config in web.xml moving forward, but also supporting environments like EJB3 and more. Watch for 0.9 RC1 for the fix so you can get rid of this custom code as soon as possible.

I'm not sure if you're using Spring or not, but if you are, you can avoid this stuff entirely and just use the SpringJSecurityFilter. That will automatically look up a bean with id "securityManager" inside your applicationContext.xml file. This allows you to configure The DefaultWebSecurityManager and any Realms and anything else in Spring instead of web.xml and then you don't have to worry about subclassing JSecurityFilter at all. If you're using Spring, please check out beta 2's included Spring sample webapp. If you're not using Spring, you're stuck with subclassing JSecurityFilter for now. But don't worry! We're doing our best to get web.xml-only configuration (no coding required) out in the next two weeks.

Please don't hesitate to ask with more questions, and once again, I apologize for the confusion. We haven't been able to update the documentation for easy/meaningful Realm configuration until the new config stuff is released in the next two weeks. Once that's out, we'll update the Quickstart so you or others won't have to worry about this again.

Regards,

Les

Oh, and once this Filter is

Oh, and once this Filter is in place, in any JSP or MVC controller, you can do this:

Subject currentUser = SecurityUtils.getSubject();

if ( !currentUser.isAuthenticated() ) {
currentUser.login( usernamePasswordToken );
}

...

In other words, it is advisable for most users to not interact with the SecurityManager for simplicity's sake (unless you're developing framework code, where using the SecurityManager directly might prove useful), and instead use SecurityUtils. The JSecurityFilter (and by extension, your subclass), will automatically make the Subject available to the calling code via the SecurityUtils.getSubject() call.

Cheers,

Les

Hi Les, Many thanks for the

Hi Les,

Many thanks for the prompt responses and excellent guidance!

No, I am not using any spring "stuff", but strictly struts 2. I find that I can do most of what spring provides via the existing mechanisms that are available, but that is simply my opinion.

I did as you suggested and switched over to using a subclass of the JSecurity filter as well as implementing the filter in my web.xml. However, I did run into a small issue (null pointer exception) with the DefaultWebSecurityManager class when giving it a realm in the constructor. One of the 1st calls is to method createSessionManager() and it subsequently calls isHttpSessionMode() which is essentially
return this.sessionMode.equals(HTTP_SESSION_MODE);
But the class level field sessionMode is null, I believe, until the constructor returns, hence the null pointer exception. I fixed this in my code by simply creating the security manager class w/out a realm and then assigning the realm after it was created. See below:
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(realm);
dwsm.init();

Anyway, things seem to be working as expected now as far as the tags are concerned.

I did have another question though, ...
In implementing my own realm I implemented a UserDao class to pull user information from our DB to implement the doGetAccount() methods in the realm class. This means that each time doGetAccount() is called (which is a lot), it hits the DB with my UserDao class. Is there an "easy" way to cache this information and pull it from cache instead of hitting the DB each time? By "easy" I mean are there mechanisms already built into jsecurity that I can utilize to cache this information.

Once again, thanks for the help. It is much appreciated.

Thanks,
Todd Kofford
tkofford@ku.edu

Hi Todd,

Hi Todd,

Thanks for making us aware of the sessionMode bug! Thankfully the new config stuff we're working on has done away with that attribute (it still exists, just specified in a different place), so this won't be an issue for the upcoming final Release Candidate.

Initializing the securityManager by calling init() is definitely the correct way to get around this problem for now.

And as far as caching, there are a number of good options:

1) If you use Hibernate or JPA, typically its good to have those products utilize 2nd-level caching on your behalf so that every time you do a lookup, their 2nd-level cache will return the value instead of a round-trip to the RDBMS.

2) If you're not using Hibernate or JPA or similar framework with built-in caching, JSecurity does provide its own caching layer, but it is really a thin wrapper around any 3rd party caching framework that you might want to use. So, if you don't go with option #1, here's how I would do it:

You already subclass AuthorizingRealm, which has built-in support for caching. So you can do one of two things:

A. Before calling realm.init(), inject it with a org.jsecurity.cache.CacheManager instance. It will use that CacheManager to acquire a Cache instance which will be used to cache the acquired Account instances pulled from the DB. Note that doing it this way makes that CacheManager available to _only_ that Realm. You would have to manually inject the CacheManager into other realms if you have more than one or want to add more later.

For example:

DefaultRealm realm = new DefaultRealm();
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.init();
realm.setCacheManager(cacheManager);
realm.init();

For a more 'hands off' approach, which doesn't require worrying about managing injecting CacheManagers for this or other potential future realms, the next approach is better:

B. The JSecurity SecurityManager implementations have built-in support for caching via a CacheManager instance, just like the Realm above. But when set on the SecurityManager (or when allowing the SecurityManager to implicitly create one automatically), that CacheManager will be used by all JSecurity components that can utilize it - the Authenticator, Authorizer, SessionManager, your Realms, etc. Because the SecurityManager delegates to these components, they all have access to the same CacheManager.

If ehcache.jar is in the classpath, the default SecurityManager implementations will try to build a CacheManager using Ehcache and then make that accessible to all of their children components (including your realm). So, if you drop ehcache.jar in the classpath, you can do this:

(first example):

DefaultRealm realm = new DefaultRealm(); //DON'T init() it yet, see below.
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm( realm );
//this next call will automatically create an EhCacheManager and inject it
//into 'myRealm'.
dwsm.init();
//now that the CacheManager has been injected into the realm,
//this next init() call will acquire the cache used by that realm for Account objects:
realm.init();

Yes, it does feel weird to init() the realm after the SecurityManager, and that will be fixed in the next release. Its just in the current release we had to ensure that the Realm would only initialize its Account cache after the SecurityManager had created the CacheManager. So maybe you see the logic behind the order.

Or if you don't want to use Ehcache, and maybe have another mechanism you'd like to use (TerraCotta, Coherence, etc), you can do the following:

(2nd example):

CacheManager cacheManager = new SomeCacheManager();
DefaultRealm realm = new DefaultRealm(); //DON'T init() it yet, see below
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setCacheManager( cacheManager );
dwsm.setRealm( realm );
dwsm.init(); //will inject the cacheManager into the realm.
//now that the CacheManager has been injected into the realm,
//this next init() call will acquire the cache used by that realm for Account objects:
realm.init();

Or, if you really have a problem with the order of inits(), just because it feels funny, this accomplishes the same thing as the first example above, its just you're manually controlling how the CacheManager is used instead of letting the SecurityManager try to automatically create one (perfectly fine - just based on your preference):

(3rd example):

//EhCacheManager is a JSecurity implementation, but
//you could provide your own utilizing another caching framework:
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.init();
DefaultRealm realm = new DefaultRealm();
realm.setCacheManager( cacheManager );
realm.init();
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setCacheManager( cacheManager );
dwsm.setRealm( realm );
dwsm.init();

Wow, that was a bit of a mouthful. You can be sure this will be going into the documentation for 0.9 final! So, this was a good exercise for me too in explaining options to folks. Thanks for the question!

Regards,

Les

Awesome Les, ... works like

Awesome Les, ... works like a charm!

OK, I do have another un-related question ...

Say, I'd like to protect everything (JSPs) under the pages directory and all my actions using the following configuration in my web.xml:

[urls]
/pages/** = authc, roles[STUDENT,STAFF]
/*.action = authc, roles[STUDENT,STAFF]

This is fine except for my Login.action URL. I'd like to exclude it from the the JSecurity filter as well as a couple more (Register.action, etc...). Is there any way to exclude a small subset of URLs from the filter?

Thanks in advance,
Todd Kofford
tkofford@ku.edu

Les' solution should work

Les' solution should work for you in the short-term.

We're looking at adding a solution for excluded URLs that look like this:
/pages/login = anon
/pages/** = authc, roles[STUDENT,STAFF]

This would allow you to exclude certain URLs by declaring that they support anonymous access. (terminology subject to change..we may call this "guest" instead)

To make the rules easier to understand, only the first matching rule would be processed. So, in this case, since /pages/login matches, only the anon/guest rule would be honored.

Please let us know what you think of this change.

Well, what I had in mind is

Well, what I had in mind is like what the "Java Web Parts" (http://javawebparts.sourceforge.net/) project, SessionInactivityFilter does. They allow for the following specification of excluded (as well as included) paths:
<init-param>
<param-name>pathSpec</param-name>
<param-value>exclude</param-value>
</init-param>
<init-param>
<param-name>pathList</param-name>
<param-value>*/Login*,*/login.action,*/Register.jsp,*/register.action</param-value>
</init-param>
Although, I'd like to see the excluded path nested under the exclude element, you still get the idea. I think that the exclusion list is a good idea because the exclusions tend to b a smaller subset than the inclusions.

Anyway, I think we have solved our issue by programmatically protecting the classes that need it (via an ancestor action class), and we simply use the JSecurityFilter to "protect" our JSPs. We have always had the JSP structure of /*.jsp for public-accessible pages and /pages/*.jsp for our "protected" pages, so this was easy to implement.

Ideally, I'd like to include the action URLs in the filter specification, but I think we're OK for now.

You guys still rock! It's so nice to have such good feedback in such a timely fashion.

Thanks again,
Todd Kofford
tkofford@ku.edu

For access to the login url,

For access to the login url, you can define that url on the authc interceptor. For example:

[interceptors]
authc.url = /login.action
...
[urls]
/pages/** = authc, roles[STUDENT,STAFF]
/*.action = authc, roles[STUDENT,STAFF]

That will make the authc interceptor allow that url since it knows that you have to access that url to log in ;)

Currently, there is no good way to define url exclusions across the board. However, this is very key to the updated configuration mechanism that we're working on at the moment, and we hope to have a viable solution for you in the next week or so. For now, you have to explicitly define those additional paths manually.

We'll get that out asap!

Cheers,

Les