Spring Boot Security 不会抛出 401 Unauthorized Exception but 404 Not Found

Posted

技术标签:

【中文标题】Spring Boot Security 不会抛出 401 Unauthorized Exception but 404 Not Found【英文标题】:Spring Boot Security does not throw 401 Unauthorized Exception but 404 Not Found 【发布时间】:2016-04-17 15:04:18 【问题描述】:

我的身份验证基于spring-boot-security-example。 当我输入一个无效的令牌时,我想抛出一个 401 Unauthorized 异常。但是,我总是找不到 404 资源。我的配置设置了异常处理,但它被忽略了 - 可能是因为之前添加了我的 AuthenticationFilter 并且请求没有到达我的异常处理程序。

我需要更改什么来引发 401 异常?

我有一个身份验证过滤器:

public class AuthenticationFilter extends GenericFilterBean 

...

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
    HttpServletRequest httpRequest = asHttp(request);
    HttpServletResponse httpResponse = asHttp(response);
    Optional<String> token = Optional.fromNullable(httpRequest.getHeader("X-Auth-Token"));

    try 
        if (token.isPresent()) 
            logger.debug("Trying to authenticate user by X-Auth-Token method. Token: ", token);
            processTokenAuthentication(token);
            addSessionContextToLogging();
        

        logger.debug("AuthenticationFilter is passing request down the filter chain");
        chain.doFilter(request, response);
     catch (InternalAuthenticationServiceException internalAuthenticationServiceException) 
        SecurityContextHolder.clearContext();
        logger.error("Internal authentication service exception", internalAuthenticationServiceException);
        httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
     catch (AuthenticationException authenticationException) 
        SecurityContextHolder.clearContext();
        httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
     finally 
        MDC.remove(TOKEN_SESSION_KEY);
        MDC.remove(USER_SESSION_KEY);
    


private void addSessionContextToLogging() 
    ...


...

private void processTokenAuthentication(Optional<String> token) 
    Authentication resultOfAuthentication = tryToAuthenticateWithToken(token);
    SecurityContextHolder.getContext().setAuthentication(resultOfAuthentication);


private Authentication tryToAuthenticateWithToken(Optional<String> token) 
    PreAuthenticatedAuthenticationToken requestAuthentication = new PreAuthenticatedAuthenticationToken(token, null);
    return tryToAuthenticate(requestAuthentication);


private Authentication tryToAuthenticate(Authentication requestAuthentication) 
    Authentication responseAuthentication = authenticationManager.authenticate(requestAuthentication);
    if (responseAuthentication == null || !responseAuthentication.isAuthenticated()) 
        throw new InternalAuthenticationServiceException("Unable to authenticate Domain User for provided credentials");
    
    logger.debug("User successfully authenticated");
    return responseAuthentication;

一个 AuthenticationProvider 实现:

@Provider
public class TokenAuthenticationProvider implements AuthenticationProvider 

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException 
    Optional<String> token = (Optional) authentication.getPrincipal();
    if (!token.isPresent() || token.get().isEmpty()) 
        throw new BadCredentialsException("No token set.");
    
    if (!myCheckHere())
        throw new BadCredentialsException("Invalid token");
    

    return new PreAuthenticatedAuthenticationToken(myConsumerObject, null, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_API_USER"));


...


以及如下所示的配置:

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

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.
            csrf().disable().
            sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
            and().
            anonymous().disable().
            exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint());

    http.addFilterBefore(new AuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);



@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.authenticationProvider(tokenAuthenticationProvider());



@Bean
public AuthenticationProvider tokenAuthenticationProvider() 
    return new TokenAuthenticationProvider();


@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() 
    return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);


【问题讨论】:

【参考方案1】:

我在这个帖子中找到了答案:Return HTTP Error 401 Code & Skip Filter Chains

而不是

httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());

我需要打电话

httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

当我不继续调用它并将状态设置为不同的代码时,链似乎会停止 - 异常被正确抛出

【讨论】:

建议的更改以及 + 确保何时调用 httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED) chain.doFilter 不应在控制流中调用:异常被正确抛出。【参考方案2】:

我通过在我的*** @SpringBootApplication 类上添加以下注释来解决它:

@EnableAutoConfiguration(exclude = ErrorMvcAutoConfiguration.class)

Spring Boot 找不到其默认错误页面吗?

【讨论】:

不幸的是,这不起作用。我不使用 spring-boot 的 MVC 部分 它对我不起作用。我正在使用 Spring Boot 应用程序,因此添加了以下行。 @SpringBootApplication(排除 = DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class,ElastiCacheAutoConfiguration.class,ErrorMvcAutoConfiguration.class )。有人可以提供解决方法吗? 这差不多是 3 年前的事了。从那以后,Spring Boot 中可能发生了很多变化。无论如何,我不再使用这项技术了,很抱歉我不能真正帮助你。【参考方案3】:

除了上面的答案,我修改了我的代码以实现 401,之前我在无效或丢失的令牌上得到 500。

public class JwtAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter 

    public JwtAuthenticationTokenFilter() 
        super("/secure/**");
    

    @Autowired
    private JWTService jwtService;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException 


        String header = httpServletRequest.getHeader("Authorization");


        if (header == null || !header.startsWith("Bearer ")) 

            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                    "Please pass valid jwt token.");


        else if(jwtService.validate(header.substring(7))==null)

            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                    "jwt token is invalid or incorrect");

        
        else

            String authenticationToken = header.substring(7);

            JwtAuthenticationToken token = new JwtAuthenticationToken(authenticationToken);
            return getAuthenticationManager().authenticate(token);
        

        return null;

    

【讨论】:

【参考方案4】:

我知道这是一个老问题。如果对其他人方便,我只是添加它。

@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() 
    return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, HttpStatus.UNAUTHORIZED.getReasonPhrase());

它应该也可以工作。我刚刚调用了 sendError()

的另一个重载方法

【讨论】:

以上是关于Spring Boot Security 不会抛出 401 Unauthorized Exception but 404 Not Found的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 前后台分离项目 怎么处理spring security 抛出的异常

Spring boot 前后台分离项目 怎么处理spring security 抛出的异常

Spring boot 前后台分离项目 怎么处理spring security 抛出的异常

Spring boot 前后台分离项目 怎么处理spring security 抛出的异常

Spring boot 前后台分离项目 怎么处理spring security 抛出的异常

Spring Boot Security PreAuthenticated Scenario with Anonymous access