java 如何使用 grails 1.3.2 和插件 spring-security-core 1 实现自定义 FilterSecurityInterceptor?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3392552/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I implement a custom FilterSecurityInterceptor using grails 1.3.2 and the plugin spring-security-core 1?
提问by Chris Alef
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 entryon the topic and attempted to adjust it for Spring Security 3 without much success.
我正在编写 grails 1.3.2 应用程序并使用 spring-security-core 1.0 实现安全性。出于这个问题范围之外的原因,除了开箱即用的拦截器之外,我还实现了一个自定义的 FilterSecurityInterceptor。我从一篇关于该主题的博客条目开始,并尝试针对 Spring Security 3 对其进行调整,但没有取得太大成功。
Loosely following the blog (since it is based on an older version of Spring Security), I've created the following classes:
松散地关注博客(因为它基于旧版本的 Spring Security),我创建了以下类:
- 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.
- 一个 org.springframework.security.authentication.AbstractAuthenticationToken 子类来保存我的凭据。
- 一个 org.springframework.security.authentication.AuthenticationProvider 子类,用于实现身份验证并支持使用来自我的 UserDetailsService 的数据填充 Authentication 实例的方法。
- 一个 org.springframework.security.web.access.intercept.FilterSecurityInterceptor 子类来实现 doFilter 和 afterPropertiesSet 方法。
- bean 的一些配置和 spring-security-core 插件来识别我的 AuthenticationProvider 并将我的过滤器插入过滤器链。
My AbstractAuthenticationToken is pretty simple:
我的 AbstractAuthenticationToken 非常简单:
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:
我的 AuthenticationProvider 非常简单:
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:
我已经实现了一个简单的 FilterSecurityInterceptor。最终这会做一些有趣的事情:
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:
最后我配置了一些bean:
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:
并在 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:
当我点击一个 URL 时,我收到以下异常,这让我很难过:
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?
那么我在哪里搞砸了,还是我把它弄得太复杂了,我错过了一些简单的东西?
采纳答案by Chris Alef
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.
最后,我实现了一个自定义过滤器,但没有实现 FilterSecurityInterceptor。我在 OOTB rememberMe 过滤器之后插入了过滤器。此外,我发现我的身份验证实现有点资源密集型和缓慢,所以我让它在成功身份验证时设置 rememberMe cookie。总的来说,这是一次痛苦的经历,所以我将尝试在此处记录下来。
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.
希望其他人在实施他们的自定义身份验证解决方案时会发现这很有帮助。我们发现这在将我们的应用程序与我们的遗留系统集成时对我们有用。
回答by Burt Beckwith
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.
我的猜测是它正在重置配置的其余部分(Grails/ConfigSlurper 怪癖)并且这将在属性中合并。您不需要设置“active = true”,但我猜您需要添加它,因为它也正在重置。
btw - you can remove the getters from InterchangeAuthenticationToken since public fields generate getters automatically.
顺便说一句 - 您可以从 InterchangeAuthenticationToken 中删除 getter,因为公共字段会自动生成 getter。
回答by Ryan Heaton
This appears to be a bug in the spring-security-core plugin because the securityMetadataSourceisn't injected into the default spring security FilterSecurityInterceptor. Perhaps the plugin gets confused and injects the securityMetadataSourceinto your custom FilterSecurityInterceptorand neglects the other (default one)? Burt might be willing to look deeper info that.
这似乎是 spring-security-core 插件中的一个错误,因为securityMetadataSource它没有注入到默认的 spring security 中FilterSecurityInterceptor。也许插件会混淆并将其securityMetadataSource注入您的自定义FilterSecurityInterceptor并忽略另一个(默认)?伯特可能愿意查看更深入的信息。
Perhaps you could try replacingthe default FilterSecurityInterceptorwith your custom one using the grails.plugins.springsecurity.filterNamesproperty...
也许您可以尝试使用属性用自定义的替换默认值...FilterSecurityInterceptorgrails.plugins.springsecurity.filterNames

