我使用的是自定义Spring Security过滤器,它覆盖了AbstractAuthenticationProcessingFilter,但我必须写错了,因为它似乎永远不会调用过滤器链的其余部分。具体来说,我依靠OpenEntityManagerInViewFilter过滤器来确保Jackson + Hibernate可以处理延迟加载的对象。
如果我的web.xml首先包含OpenEntityManagerInViewFilter,那么一切正常:
<filter>
<filter-name>hibernateFilterChain</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是,如果我将springSecurityFilterChain置于顶部,我的应用程序就像我根本没有指定OpenEntityManagerInViewFilter一样。
这是我的springSecurity.xml:
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:http entry-point-ref="restAuthenticationEntryPoint"
use-expressions="true" create-session="stateless">
<security:custom-filter ref="authenticationTokenProcessingFilter"
position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/**"
access="isAuthenticated()" />
<security:logout />
</security:http>
<bean class="edu.ucdavis.dss.dw.security.CustomTokenAuthenticationFilter"
id="authenticationTokenProcessingFilter">
<constructor-arg type="java.lang.String">
<value>/**</value>
</constructor-arg>
</bean>
<security:authentication-manager>
<security:authentication-provider
user-service-ref="userService"></security:authentication-provider>
</security:authentication-manager>
<bean id="userService" class="edu.ucdavis.dss.dw.services.UserAuthenticationService"></bean>
</beans>
最后,这里是CustomTokenAuthenticationFilter本身,可能导致问题:
public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Autowired @Qualifier("org.springframework.security.authenticationManager")
private AuthenticationManager authenticationManager;
public CustomTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(new NoOpAuthenticationManager());
setAuthenticationSuccessHandler(new TokenSimpleUrlAuthenticationSuccessHandler());
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String token = request.getParameter("token");
if(token == null) {
throw new AuthenticationServiceException("Token Missing");
}
Authentication authResponse;
try {
authResponse = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(token, "dssit"));
} catch (AuthenticationException e) {
throw new AuthenticationServiceException("Bad Token");
}
return authResponse;
}
}
总结:我制作了一个自定义安全过滤器,它似乎不会调用其后列出的任何过滤器。如果我删除我的自定义过滤器并使用内置的安全性:http-basic,它可以正常工作。
提前感谢您提供的任何帮助。
答案 0 :(得分:2)
您想尝试添加
吗?public void doFilter(javax.servlet.ServletRequest req,
javax.servlet.ServletResponse res,
javax.servlet.FilterChain chain)
throws IOException,
javax.servlet.ServletException {
chain.doFilter(req, res);
}
到CustomTokenAuthenticationFilter类?
答案 1 :(得分:2)
据我所知,Spring身份验证过滤器用于验证请求,然后重定向到某个地方,启动一个全新的请求。这意味着有目的地停止过滤器链。这个想法是,一旦经过身份验证并且在新请求中,将存在身份验证会话,因此身份验证筛选器不必再次重定向。至少,这是我从一点调试中得到的理解。
为了解决此问题,您必须实现自己的AbstractAuthenticationProcessingFilter,AuthenticationEntryPoint,AbstractAuthenticationToken,AuthenticationProvider和SimpleUrlAuthenticationSuccessHandler。
答案 2 :(得分:1)
克里斯托弗在答复中提到的内容是正确的,但我认为最好显示一些代码。就我而言,我通过扩展UsernamePasswordAuthenticationFilter和自定义AuthenticationSuccessHandler实现来实现自定义身份验证方法。
一开始,我具有以下用于实现authenticationSuccessHandler返回响应的实现
httpServletResponse.setStatus(HttpServletResponse.
httpServletResponse.setContentType("application/json");
httpServletResponse.getWriter().write(loginSuccessResponse);
httpServletResponse.getWriter().flush();
但是,当我使用以下命令打开调试时:
@EnableWebSecurity(debug = true)
并为org.springframework.security.web.FilterChainProxy启用调试日志记录。
我意识到安全过滤器链停止在自定义实现UsernamePasswordAuthenticationFilter上。然后,我发现克里斯托弗(Christopher)答复并按他的意愿去做,即:
将AuthenticationSuccessHandler中的响应重定向到另一个端点:
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.sendRedirect(ApiResourceConstant.POST_LOGIN);
}
}
您也可以在SimpleUrlAuthenticationSuccessHandler上进行探索。然后,您应该能够从日志中看到所有筛选器链均已触发。
希望它会有所帮助:)
答案 3 :(得分:0)
AbstractAuthenticationProcessingFilter
类具有以下可以覆盖的方法:
protected boolean requiresAuthentication(HttpServletRequest request,
HttpServletResponse response)
在使用过滤器启动身份验证之前调用此方法(在doFilter
中):
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
超类要覆盖此方法,以检查它是否可以验证请求:如果令牌不存在或无效,则应返回false。
例如:
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
if (!super.requiresAuthentication(request, response)) {
// We're not required to authenticate this request (ant matchers).
return false;
}
if (null == request.getParameter("token")) {
// We can't authenticate this request, because the token is missing.
return false;
}
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(token, "dssit"));
} catch (AuthenticationException e) {
// We can't authenticate this token because an exception occurred.
return false;
}
// We can authenticate this request.
return true;
}