【问题标题】:Spring Boot Security does not throw 401 Unauthorized Exception but 404 Not FoundSpring Boot Security 不会抛出 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);
}
}

【问题讨论】:

    标签: java spring authentication spring-security


    【解决方案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()

      的另一个重载方法

      【讨论】:

        猜你喜欢
        • 2019-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-31
        • 2020-10-21
        • 2021-09-11
        • 2016-03-26
        相关资源
        最近更新 更多