I'm writing a grails 1.3.2 application and implementing security with spring-security-core 1.0. For reasons outside the scope of this question, I'm implementing a custom FilterSecurityInterceptor in addition to the out of the box interceptors. I've started with a blog entry on the topic and attempted to adjust it for Spring Security 3 without much success.
Loosely following the blog (since it is based on an older version of Spring Security), I've created the following classes:
- A org.springframework.security.authentication.AbstractAuthenticationToken subclass to hold my credentials.
- A org.springframework.security.authentication.AuthenticationProvider subclass to implement the authenticate and supports methods for populating an Authentication instance with data from my UserDetailsService.
- A org.springframework.security.web.access.intercept.FilterSecurityInterceptor subclass to implement doFilter and afterPropertiesSet methods.
- Some configuration of beans and the spring-security-core plugin to recognize my AuthenticationProvider and insert my filter into the filter chain.
My AbstractAuthenticationToken is pretty simple:
class InterchangeAuthenticationToken extends AbstractAuthenticationToken {
String credentials
Integer name
Integer principal
String getCredentials() { //necessary or I get compilation error
return credentials
}
Integer getPrincipal() { //necessary or I get compilation error
return principal
}
}
My AuthenticationProvider is pretty simple:
class InterchangeAuthenticationProvider implements org.springframework.security.authentication.AuthenticationProvider {
Authentication authenticate(Authentication customAuth) {
def authUser = AuthUser.get(customAuth.principal)
if (authUser) {
customAuth.setAuthorities(authUser.getAuthorities())
customAuth.setAuthenticated(true)
return customAuth
} else {
return null
}
}
boolean supports(Class authentication) {
return InterchangeAuthenticationToken.class.isAssignableFrom(authentication)
}
}
I've implemented a trivial FilterSecurityInterceptor. Eventually this will do something interesting:
class InterchangeFilterSecurityInterceptor extends FilterSecurityInterceptor implements InitializingBean {
def authenticationManager
def interchangeAuthenticationProvider
def securityMetadataSource
void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
def myAuth = new InterchangeAuthenticationToken()
myAuth.setName(1680892)
myAuth.setCredentials('SDYLWUYa:nobody::27858cff')
myAuth.setPrincipal(1680892)
myAuth = authenticationManager.authenticate(myAuth);
if (myAuth) {
println "Successfully Authenticated ${userId} in object ${myAuth}"
// Store to SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(myAuth);
}
}
chain.doFilter(request, response)
}
void afterPropertiesSet() {
def providers = authenticationManager.providers
providers.add(interchangeAuthenticationProvider)
authenticationManager.providers = providers
}
}
Finally I configure some beans:
beans = {
interchangeAuthenticationProvider(com.bc.commerce.core.InterchangeAuthenticationProvider) {
}
interchangeFilterSecurityInterceptor(com.bc.commerce.core.InterchangeFilterSecurityInterceptor) {
authenticationManager = ref('authenticationManager')
interchangeAuthenticationProvider = ref('interchangeAuthenticationProvider')
securityMetadataSource = ref('objectDefinitionSource')
}
}
And do some configuration of the plugin:
grails.plugins.springsecurity.dao.hideUserNotFoundExceptions = true //not setting this causes exception
grails.plugins.springsecurity.providerNames = [
'interchangeAuthenticationProvider',
'daoAuthenticationProvider',
'anonymousAuthenticationProvider',
'rememberMeAuthenticationProvider'
]
And set the filter order in Bootstrap.groovy:
def init = {servletContext ->
//insert our custom filter just after the filter security interceptor
SpringSecurityUtils.clientRegisterFilter('interchangeFilterSecurityInterceptor', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 10)
<snip />
}
When I hit a URL, I get the following exception which stumps me:
2010-07-30 15:07:16,763 [http-8080-1] ERROR [/community-services].[default] - Servlet.service() for servlet default threw exception
java.lang.NullPointerException
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:171)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:112)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:187)
at org.codehaus.groovy.grails.plugins.springsecurity.RequestHolderAuthenticationFilter.doFilter(RequestHolderAuthenticationFilter.java:40)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
at org.codehaus.groovy.grails.plugins.springsecurity.MutableLogoutFilter.doFilter(MutableLogoutFilter.java:79)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.codehaus.groovy.grails.web.servlet.filter.GrailsReloadServletFilter.doFilterInternal(GrailsReloadServletFilter.java:104)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:67)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.codehaus.groovy.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:66)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
at java.lang.Thread.run(Thread.java:637)
So where am I messing up, or did I make this too complex and I missed something simple?