How do I implement a custom FilterSecurityInterceptor using grails 1.3.2 and the plugin spring-security-core 1?

允我心安 提交于 2019-12-06 00:54:45

In the end, I implemented a custom filter, but not a FilterSecurityInterceptor. I inserted my filter after the OOTB rememberMe filter. Furthermore I found my authentication implementation to be somewhat resource-intensive and slow, so I made it set the rememberMe cookie on successful authentication. On the whole, this was a painful experience so I'll make an attempt to document it here.

My filter implementation was as follows:

import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.ApplicationEventPublisherAware
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.GenericFilterBean

class CustomRememberMeAuthenticationFilter extends GenericFilterBean implements ApplicationEventPublisherAware {

    def authenticationManager
    def eventPublisher
    def customService
    def rememberMeServices
    def springSecurityService

    //make certain that we've specified our beans
    void afterPropertiesSet() {
        assert authenticationManager != null, 'authenticationManager must be specified'
        assert customService != null, 'customService must be specified'
        assert rememberMeServices != null, 'rememberMeServices must be specified'
    }

    void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req
        HttpServletResponse response = (HttpServletResponse) res

        if (SecurityContextHolder.getContext().getAuthentication() == null) {           
            Authentication auth
            try {
                auth = customService.getUsernamePasswordAuthenticationToken(request)
                if (auth != null) {
                    springSecurityService.reauthenticate(auth.getPrincipal(), auth.getCredentials())
                    logger.debug("SecurityContextHolder populated with auth: '"
                        + SecurityContextHolder.getContext().getAuthentication() + "'")
                    onSuccessfulAuthentication(request, response, SecurityContextHolder.getContext().getAuthentication())
                } else {
                    logger.debug('customService did not return an authentication from the request')
                }
            } catch (AuthenticationException authenticationException) {
                logger.warn("SecurityContextHolder not populated with auth, as "
                    + "springSecurityService rejected Authentication returned by customService: '"
                    + auth + "'", authenticationException)
                onUnsuccessfulAuthentication(request, response, auth)
            } catch(e) {
                logger.warn("Unsuccessful authentication in customRememberMeAuthenticationFilter", e)
                onUnsuccessfulAuthentication(request, response, auth)
            }
        } else {
            logger.debug("SecurityContextHolder not populated with auth, as it already contained: '"
                + SecurityContextHolder.getContext().getAuthentication() + "'")
        }
        chain.doFilter(request, response)
    }

    protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
        //sets the rememberMe cookie, but cannot call "loginSuccess" because that filters out requests
        //that don't set the rememberMe cookie, like this one
        rememberMeServices.onLoginSuccess(request, response, authResult)
    }

    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
        //clear the rememberMe cookie
        rememberMeServices.loginFail(request, response)
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher
    }
}

Hopefully others find this helpful when implementing their custom authentication solutions. We found this worked for us when integrating our application with our legacy system.

Given where it's failing (fairly unrelated) I'd guess that it's the nested properties. Try

grails.plugins.springsecurity.dao.hideUserNotFoundExceptions = true
grails.plugins.springsecurity.providerNames = [
    'interchangeAuthenticationProvider',
    'daoAuthenticationProvider',
    'anonymousAuthenticationProvider',
    'rememberMeAuthenticationProvider'
]

My guess is that it's resetting the rest of the config (a Grails/ConfigSlurper quirk) and that this will merge in the properties instead. You shouldn't need to set "active = true" but I'm guessing you needed to add that since it's also getting reset.

btw - you can remove the getters from InterchangeAuthenticationToken since public fields generate getters automatically.

This appears to be a bug in the spring-security-core plugin because the securityMetadataSource isn't injected into the default spring security FilterSecurityInterceptor. Perhaps the plugin gets confused and injects the securityMetadataSource into your custom FilterSecurityInterceptor and neglects the other (default one)? Burt might be willing to look deeper info that.

Perhaps you could try replacing the default FilterSecurityInterceptor with your custom one using the grails.plugins.springsecurity.filterNames property...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!