如何处理自定义筛选器FORM_LOGIN_FILTER的例外

时间:2014-07-08 17:51:56

标签: spring-mvc spring-security exceptionhandler

我有一个自定义过滤器,用于验证令牌,tokenLoginFilter

我的Spring Security xml

<http pattern="/api/**" use-expressions="true" create-session="stateless" entry-point-ref="restAuthenticationEntryPoint" authentication-manager-ref="authenticationManager">
    <custom-filter ref="tokenLoginFilter" position="FORM_LOGIN_FILTER" />
    <!--<form-login authentication-success-handler-ref="mySuccessHandler" authentication-failure-handler-ref="myFailureHandler" />
    <logout />-->
    <intercept-url pattern="/api/**" access="isAuthenticated()" />
    <intercept-url pattern="/api/login/**" access="permitAll"/>
</http>

TokenLoginFilter

@Component
public class TokenLoginFilter extends GenericFilterBean {
    public static final String DEFAULT_TOKEN_AUTHENTICATION_PARAMETER_NAME = "token";

    @Autowired
    protected AuthenticationManager authenticationManager;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        String[] parameters = request.getParameterValues(DEFAULT_TOKEN_AUTHENTICATION_PARAMETER_NAME);

        if (parameters != null && parameters.length > 0) {
            String token = parameters[0];

            if (token != null && !token.isEmpty()) {
                TemporaryAuthenticationToken temporaryAuthenticationToken = new TemporaryAuthenticationToken(token);
                temporaryAuthenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));

                SecurityContextHolder.getContext().setAuthentication(
                        authenticationManager.authenticate(temporaryAuthenticationToken));
            }
        }

        chain.doFilter(request, response);
    }
}

在authenticationManager.authenticate调用中,我从 TokenAuthenticationProvider

中抛出 BadCredentialsException

TokenAuthenticationProvider

@Component
public class TokenAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    public static final TimeZone DEFAULT_TIME_ZONE = getTimeZone("UTC");

    @Autowired
    protected TemporaryAuthenticationTicketService temporaryAuthenticationTicketService;

    @Autowired
    protected UsersService usersService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (authentication instanceof TemporaryAuthenticationToken) {
            TemporaryAuthenticationTicket temporaryAuthenticationTicket = null;
            try {
                temporaryAuthenticationTicket = temporaryAuthenticationTicketService.find(
                        ((TemporaryAuthenticationToken) authentication).getCredentials());
            } catch (EmptyResultDataAccessException ignored) {
            }

            if (temporaryAuthenticationTicket != null) {
                Calendar expirationDate = Calendar.getInstance(DEFAULT_TIME_ZONE);
                expirationDate.setTime(temporaryAuthenticationTicket.getExpirationDate());

                if (Calendar.getInstance(DEFAULT_TIME_ZONE).before(expirationDate)) {
                    Users users = usersService.findUsersUnsecure(temporaryAuthenticationTicket.getUserId());

                    if (users != null) {
                        UserDetails userDetails = new User(users);

                        return createSuccessAuthentication(userDetails, authentication, userDetails);
                    }
                }
            } else {
                throw new BadCredentialsException(messages.getMessage(
                        "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }

        return null;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return TemporaryAuthenticationToken.class.isAssignableFrom(authentication);
    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
    }

    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        return null;
    }
}

这个 BadCredentialsException 被Tomcat解释为错误代码500.

HTTP/1.1 500 Internal Server Error
Connection: close
Content-Language: en
Content-Length: 3152
Content-Type: text/html;charset=utf-8
Date: Tue, 08 Jul 2014 17:37:55 GMT
Server: Apache-Coyote/1.1

<html><head><title>Apache Tomcat/7.0.50 - Error report</title><style><!--H1 {fon
t-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:
22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525
D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;backgro
und-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;col
or:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:w
hite;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;backgroun
d:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR
{color : #525D76;}--></style> </head><body><h1>HTTP Status 500 - Bad credentials
</h1><HR size="1" noshade="noshade"><p><b>type</b> Exception report</p><p><b>mes
sage</b> <u>Bad credentials</u></p><p><b>description</b> <u>The server encounter
ed an internal error that prevented it from fulfilling this request.</u></p><p><
b>exception</b> <pre>org.springframework.security.authentication.BadCredentialsE
xception: Bad credentials
        com.neptune.unsub.security.TokenAuthenticationProvider.authenticate(Toke
nAuthenticationProvider.java:56)
        org.springframework.security.authentication.ProviderManager.authenticate
(ProviderManager.java:156)
        com.neptune.unsub.security.TokenLoginFilter.doFilter(TokenLoginFilter.ja
va:38)
        org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doF
ilter(FilterChainProxy.java:342)
        org.springframework.security.web.context.request.async.WebAsyncManagerIn
tegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequ
estFilter.java:106)
        org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doF
ilter(FilterChainProxy.java:342)
        org.springframework.security.web.context.SecurityContextPersistenceFilte
r.doFilter(SecurityContextPersistenceFilter.java:87)
        org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doF
ilter(FilterChainProxy.java:342)
        org.springframework.security.web.FilterChainProxy.doFilterInternal(Filte
rChainProxy.java:192)
        org.springframework.security.web.FilterChainProxy.doFilter(FilterChainPr
oxy.java:160)
        org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(Dele
gatingFilterProxy.java:343)
        org.springframework.web.filter.DelegatingFilterProxy.doFilter(Delegating
FilterProxy.java:260)
        org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(H
iddenHttpMethodFilter.java:77)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequ
estFilter.java:106)
        org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(
CharacterEncodingFilter.java:88)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequ
estFilter.java:106)
        com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:169)
        com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:232)
</pre></p><p><b>note</b> <u>The full stack trace of the root cause is available
in the Apache Tomcat/7.0.50 logs.</u></p><HR size="1" noshade="noshade"><h3>Apac
he Tomcat/7.0.50</h3></body></html>

我尝试过使用Spring MVC处理异常的方式

@ControllerAdvice
public class GlobalExceptionHandlers extends ResponseEntityExceptionHandler {

    @ExceptionHandler(BadCredentialsException.class)
    public ResponseEntity<String> badCredentialsException(BadCredentialsException e) {
        return new ResponseEntity<>(e.getMessage(), UNAUTHORIZED);
    }


}

但是ExceptionHandler没有处理异常。在Tomcat处理之前,如何让Spring拦截此异常?

1 个答案:

答案 0 :(得分:1)

您可以尝试在doFilter方法中捕获异常,并通过实现AuthenticationFailureHandler的类来处理它。代码应返回正确的HTTP状态代码以及JSON格式的合适Exception。

 @Override
public void onAuthenticationFailure(HttpServletRequest request, 
  HttpServletResponse response,AuthenticationException exception) throws 
  IOException, ServletException {
            response.setContentType( MediaType.APPLICATION_JSON_VALUE);
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

             ApiError authenticationError = new ApiError(HttpStatus.UNAUTHORIZED, exception.getMessage(), "Not Authenticated");
             ObjectMapper objectMapper = new ObjectMapper();
             response.getWriter().write(objectMapper.writeValueAsString(authenticationError));
};

ApiError 是一个自定义类,其中包含返回具有正确状态代码和异常消息字符串的异常方案所需的字段。

旁注

您的 GlobalExceptionHandlers 类不起作用,因为它们使用ControllerAdvice注释,这意味着从那里处理从控制器抛出的任何异常。