Spring 多次调用安全自定义筛选器
Spring security custom filter called multiple times
我有一个自定义注销过滤器调用了六次。我尝试访问该应用程序时两次,当我输入 username/password 并单击 'Login' 时两次,当我单击 'logout'.
时再次尝试两次。
我做错了什么?
配置:
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN_FUNCTIONS')" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login"
authentication-success-handler-ref="customAuthenticationSuccessHandlerBean"
authentication-failure-handler-ref="customAuthenticationFailureHandlerBean" />
<logout invalidate-session="true" success-handler-ref="logoutHandlerBean" />
<session-management session-fixation-protection="migrateSession">
<concurrency-control max-sessions="1"
expired-url="/login_sessionexpired" />
</session-management>
<custom-filter before="LOGOUT_FILTER" ref="customLogoutFilter" />
</http>
<beans:bean id="customLogoutFilter" class="com.hurontg.libms.security.CustomLogoutFilter" />
过滤器:
public class CustomLogoutFilter extends OncePerRequestFilter {
/**
*
*/
private XLogger logger = XLoggerFactory
.getXLogger(CustomLogoutFilter.class.getName());
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
logger.error("========================================================================================");
logger.error("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Custom Logout Filter $$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
logger.error("========================================================================================");
chain.doFilter(req, res);
}
}
Spring版本:4.1.1
Spring 安全性:3.2.5
它很可能是为正在请求的其他 URL 而调用的。例如,如果您有任何 css、javascript 图像加载到页面上,它将为每个图像调用。尝试添加一个显示当前请求信息的日志语句,看看是否是这种情况。例如,
logger.error("URL = " + req.getRequestURL());
如果您正在使用 Spring 引导,上下文中的任何 GenericFilterBean(OncePerRequestFilter 是一个)将自动添加到过滤器链中。这意味着您上面的配置将包含相同的过滤器两次。
最简单的解决方法是在上下文中定义 FilterRegistrationBean 并将其禁用:
<beans:bean id="customLogoutFilterRegistration" class="org.springframework.boot.context.embedded.FilterRegistrationBean">
<beans:property name="filter" ref="customLogoutFilter"/>
<beans:property name="enabled" value="false"/>
</beans:bean>
编辑(2020 年 11 月 3 日):
对于在 SpringBoot 中工作并希望使用注释注册 bean 的任何人。在 Spring Boot app initializer 文件中添加以下代码(其中一个带有 @SpringBootApplication 注释):
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new YourCustomFilterClassName());
registrationBean.setEnabled(false);
return registrationBean;
}
只是分享我的案例:(
我没有在 AuthenticationProvider 中设置 authentication.setAuthenticated(true)
。
因此,AbstractPreAuthenticatedProcessingFilter
调用了一次authenticate
,然后AbstractSecurityInterceptor
也调用了authenticateIfNeeded
。
Spring 安全性有大约 12 个过滤器,其中一些会尝试检查用户是否已通过身份验证。例如,有一个名为 AnonymousAuthenticationFilter 的过滤器。
如果您提供身份验证提供程序并且您已经对请求进行了一次身份验证,则应在安全上下文中设置身份验证对象。
SecurityContextHolder.getContext().setAuthentication(authentication)
在示例中,AnonymousAuthenticationFilter 尝试从安全上下文中获取身份验证。如果没有找到,会再次调用。
我有一个自定义注销过滤器调用了六次。我尝试访问该应用程序时两次,当我输入 username/password 并单击 'Login' 时两次,当我单击 'logout'.
时再次尝试两次。我做错了什么?
配置:
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN_FUNCTIONS')" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login"
authentication-success-handler-ref="customAuthenticationSuccessHandlerBean"
authentication-failure-handler-ref="customAuthenticationFailureHandlerBean" />
<logout invalidate-session="true" success-handler-ref="logoutHandlerBean" />
<session-management session-fixation-protection="migrateSession">
<concurrency-control max-sessions="1"
expired-url="/login_sessionexpired" />
</session-management>
<custom-filter before="LOGOUT_FILTER" ref="customLogoutFilter" />
</http>
<beans:bean id="customLogoutFilter" class="com.hurontg.libms.security.CustomLogoutFilter" />
过滤器:
public class CustomLogoutFilter extends OncePerRequestFilter {
/**
*
*/
private XLogger logger = XLoggerFactory
.getXLogger(CustomLogoutFilter.class.getName());
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
logger.error("========================================================================================");
logger.error("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Custom Logout Filter $$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
logger.error("========================================================================================");
chain.doFilter(req, res);
}
}
Spring版本:4.1.1 Spring 安全性:3.2.5
它很可能是为正在请求的其他 URL 而调用的。例如,如果您有任何 css、javascript 图像加载到页面上,它将为每个图像调用。尝试添加一个显示当前请求信息的日志语句,看看是否是这种情况。例如,
logger.error("URL = " + req.getRequestURL());
如果您正在使用 Spring 引导,上下文中的任何 GenericFilterBean(OncePerRequestFilter 是一个)将自动添加到过滤器链中。这意味着您上面的配置将包含相同的过滤器两次。
最简单的解决方法是在上下文中定义 FilterRegistrationBean 并将其禁用:
<beans:bean id="customLogoutFilterRegistration" class="org.springframework.boot.context.embedded.FilterRegistrationBean">
<beans:property name="filter" ref="customLogoutFilter"/>
<beans:property name="enabled" value="false"/>
</beans:bean>
编辑(2020 年 11 月 3 日):
对于在 SpringBoot 中工作并希望使用注释注册 bean 的任何人。在 Spring Boot app initializer 文件中添加以下代码(其中一个带有 @SpringBootApplication 注释):
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new YourCustomFilterClassName());
registrationBean.setEnabled(false);
return registrationBean;
}
只是分享我的案例:(
我没有在 AuthenticationProvider 中设置 authentication.setAuthenticated(true)
。
因此,AbstractPreAuthenticatedProcessingFilter
调用了一次authenticate
,然后AbstractSecurityInterceptor
也调用了authenticateIfNeeded
。
Spring 安全性有大约 12 个过滤器,其中一些会尝试检查用户是否已通过身份验证。例如,有一个名为 AnonymousAuthenticationFilter 的过滤器。
如果您提供身份验证提供程序并且您已经对请求进行了一次身份验证,则应在安全上下文中设置身份验证对象。
SecurityContextHolder.getContext().setAuthentication(authentication)
在示例中,AnonymousAuthenticationFilter 尝试从安全上下文中获取身份验证。如果没有找到,会再次调用。