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的主要内容,如果未能解决你的问题,请参考以下文章