Java Spring Boot Security 不会抛出 401 Unauthorized Exception 但 404 Not Found
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34753147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Boot Security does not throw 401 Unauthorized Exception but 404 Not Found
提问by Frame91
My authentication is based on a spring-boot-security-example. When I enter an invalid token, I would like to throw a 401 Unauthorized exception. However, I always get a 404 resource not found instead. My configuration sets an exception handling but it is ignored - probably because my AuthenticationFilter is added before and the request does not reach my exception handler.
我的身份验证基于spring-boot-security-example。当我输入无效令牌时,我想抛出 401 Unauthorized 异常。但是,我总是得到一个未找到的 404 资源。我的配置设置了一个异常处理,但它被忽略了 - 可能是因为我的 AuthenticationFilter 是之前添加的并且请求没有到达我的异常处理程序。
What would I need to change to throw 401 exceptions instead?
我需要更改什么才能抛出 401 异常?
I have a authentication filter:
我有一个身份验证过滤器:
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;
}
a AuthenticationProvider implementation:
一个 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"));
}
...
}
and a configuration which looks as follows:
以及如下所示的配置:
@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);
}
}
采纳答案by Frame91
I found the answer in this thread: Return HTTP Error 401 Code & Skip Filter Chains
我在这个线程中找到了答案:Return HTTP Error 401 Code & Skip Filter Chains
Instead of
代替
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
I need to call
我需要打电话
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
It seems like the chain will stop when I don't continue calling it and by setting the status to a different code - the exception is thrown correctly
当我不继续调用它并将状态设置为不同的代码时,似乎链会停止 - 异常被正确抛出
回答by candide
I solved it by adding the following annotation on my top-level @SpringBootApplication
class:
我通过在顶级@SpringBootApplication
类上添加以下注释来解决它:
@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
Could Spring Boot have trouble finding its default error page?
Spring Boot 是否无法找到其默认错误页面?
回答by Aditya Parmar
In addition to the above answer, I modified my code to achieve 401, previously I got 500 on an invalid or missing token.
除了上面的答案,我修改了我的代码以达到 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;
}
}
}