2017-06-05 127 views
1

我正在使用JSON Web令牌的Spring Security & Spring Security應用程序。在彈簧安全篩選器中返回自定義錯誤

我有一個彈簧安全過濾器來檢查現有JWT的存在,並且如果是這樣,注入一個UsernamePasswordAuthenticationToken:

public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter { 

    @Value("${api.token.header}") 
    String tokenHeader; 

    @Autowired 
    TokenUtility tokenUtility; 

    @Override 
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 

     HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; 

     String incomingToken = httpRequest.getHeader(tokenHeader); 

     if (SecurityContextHolder.getContext().getAuthentication() == null && incomingToken != null) { 

      UserDetails userDetails = null; 

      try { 

       userDetails = tokenUtility.validateToken(incomingToken); 

      } catch (TokenExpiredException e) { 

       throw new ServletException("Token has expired", e); 
      } 

      if (userDetails != null) { 

       UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); 

       authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); 

       SecurityContextHolder.getContext().setAuthentication(authentication); 
      } 
     } 

     filterChain.doFilter(servletRequest, servletResponse); 
    } 
} 

此過濾器被注入如下:

@Configuration 
@EnableWebSecurity 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    UserDetailsService userDetailsService; 

    @Autowired 
    EntryPointUnauthorizedHandler unauthorizedHandler; 

    @Autowired 
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { 

     authenticationManagerBuilder 
          .userDetailsService(userDetailsService) 
          .passwordEncoder(passwordEncoder()); 
    } 

    @Bean 
    public PasswordEncoder passwordEncoder() { 

     return new BCryptPasswordEncoder(); 
    } 

    @Bean 
    @Override 
    public AuthenticationManager authenticationManager() throws Exception { 

     return super.authenticationManager(); 
    } 

    @Bean 
    public AuthenticationTokenFilter authenticationTokenFilter() throws Exception { 

     AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter(); 
     authenticationTokenFilter.setAuthenticationManager(authenticationManager()); 

     return authenticationTokenFilter; 
    } 

    @Override 
    protected void configure(HttpSecurity httpSecurity) throws Exception { 

     httpSecurity 
      .csrf() 
       .disable() 
      .exceptionHandling() 
       .authenticationEntryPoint(unauthorizedHandler) 
       .and() 
      .sessionManagement() 
       .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
       .and() 
      .authorizeRequests() 
       .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() 
       .antMatchers("/auth/**").permitAll() 
       .anyRequest().authenticated(); 

     // filter injected here 
     httpSecurity.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); 
    } 
} 

如果用戶傳入已過期的令牌時,會收到以下錯誤:

{ 
    "timestamp":1496424964894, 
    "status":500, 
    "error":"Internal Server Error", 
    "exception":"com.app.exceptions.TokenExpiredException", 
    "message":"javax.servlet.ServletException: Token has expired", 
    "path":"/orders" 
} 

我知道彈簧安全性會在請求到達控制器層之前攔截請求,所以我不能使用現有的@ControllerAdvice來處理這些異常。

我的問題是,我如何定製在這裏返回的錯誤消息/對象?在其他地方,我使用JSON序列化的POJO來返回錯誤消息,我想要保持一致。我也不希望用戶看到javax.servlet.ServletException

回答

1

正如你使用.exceptionHandling()我相信你可以配置一個新的ExceptionHandler;

另一種方法是覆蓋你想要不同的消息,像這樣post