This forum is now a read-only archive. All commenting, posting, registration services have been turned off. Those needing community support and/or wanting to ask questions should refer to the Tag/Forum map, and to http://spring.io/questions for a curated list of stackoverflow tags that Pivotal engineers, and the community, monitor.

Securing Spring-based services with Acegi

Sep 19th, 2005, 08:31 PM

Greetings!

We are currently looking into how we could leverage Acegi (0.8.3) to add method-level security to our Spring-based web services. These services are components in a large loosely coupled framework that already has several entities dealing with various aspects of security. Specifically, there is a central entity (for the purpose of this discussion, letís call it Authentication Service, or AS) that is capable of authenticating framework users against multiple authentication sources, such as LDAP, AD, etc. There is another central entity (letís call it Security Service, or SS) that is a system of record of role and permission information that is meaningful within the framework. The result of a successful authentication against the AS is a login token that among other things contains a user id and token expiration date. This login token is then used for all web service invocations within the framework and is the first argument of every service method. For existing non-Spring-based services, a service method access decision is a three-step process where a combination of the ASís facility to validate a presented login token, an SSís facility to obtain role/permission information for a given user, and a client library to evaluate roles/permissions is used within the implementation of the service.

For the Spring-based services we are naturally looking at the Acegi framework and ways to plug our security facilities into it. Acegiís AuthenticationProvider/AccessDecisionManager abstraction plays nice with our security facilities I have described above. We could validate the login token and obtain granted authorities in our custom AuthenticationProvider; a custom AccessDecisionManager would then make the access decision using our client library.

I am working on a proof of concept and have a couple of questions:

1. SecureContext population in the service scenario.
I have configured a MethodSecurityInterceptor bean to proxy access to an instance of my service bean. However, the MethodSecurityInterceptor machinery (more specifically, AbstractSecurityInterceptor.beforeInvocation()) requires a populated SecureContext. One way I was able to make it work is by creating a ďsecurity context populatorĒ proxy (with the MethodBeforeAdvice) and wiring it in front of the MethodSecurityInterceptor. While this works fine, I am not sure that this is the intended way of doing this. What is the proper way of doing this for Spring-based services with Acegi?

2. Authentication retention between service method invocations.
Out of performance considerations, I would like to make it possible to have Authentication cached between service method invocations, so that the login token validation and population of granted authorities would only happen on the very first service method invocation with a given token. I see that in the case of web applications an HttpSessionContextIntegrationFilter would do the trick. Is there anything in the framework that lets you do that for services exposed by means of Spring remoting?

If you're using HTTP Invoker or similar, you're best off presenting the credentials via BASIC authentication on each request. HttpSessionContextIntegrationFilter would be responsible for setting up the SecurityContextHolder for each request. You can use HttpSessionContextIntegrationFilter.allowSessionCr eation = false to avoid creating a HttpSession to store the Authentication on each request, but that will mean re-generating an Authentication with all of its GrantedAuthority[]s every request and that's something you expressly asked about avoiding.

If you're using RMI, ContextPropagatingRemoteInvocation will automatically set the SecurityContextHolder on the server-side during a request. A problem will be trusting the presented Authentication, as the Authentication came from a client and should have its GrantedAuthority[]s recreated.

0.9.0-SNAPSHOT (in CVS) by default will not re-authenticate an Authentication that returns true to isAuthenticated() and AbstractSecurityInterceptor.alwaysReauthenticate = false. You might like to sync up with CVS to get this new behaviour.

Comment

We have a somewhat unusual need to be able to expose a service through several remoting protocols simultaneously. This is due to the two different usage scenarios that we need to support: a) efficient internal consumption of the framework services by other framework services and clients where there are no firewalls between them, and b) consumption of firewalled services by external clients. This translates to the need to support both binary and non-binary remoting protocols. The protocols that we care most about are SOAP, RMI and Hessian/Burlap. Since we have to stay protocol-agnostic, we cannot resort to any protocol-specific ways of passing authentication info, such as BASIC auth. This is the main reason why we have adopted the convention where a login token is always the first parameter of every service method. We essentially need a mechanism to create SecureContext and preserve Authentication that is protocol-agnostic.

Comment

Nearly every half-decent remoting protocol I can think of will support BASIC authentication headers being presented, or offers a way of transferring an Object with the request such as RMI. Thus I don't believe you'll find any issues if you tried to standardise on BASIC - definitely HttpInvoker, Hessian, Burlap and SOAP all happily support it.

Having said that, if you really need an Authentication token as part of every method signature, there's no reason you couldn't have a MethodInterceptor which detects the token from the list of arguments, and then sets the SecurityContextHolder. In that case you'd simply not use HttpSessionContextIntegrationFilter or any AbstractProcessingFilter subclass in your application.