web-dev-qa-db-de.com

Spring Boot Security gibt nicht 401 Unauthorized Exception aus, sondern 404 Not Found

Meine Authentifizierung basiert auf einem Spring-Boot-Sicherheitsbeispiel . Wenn ich ein ungültiges Token eingebe, möchte ich eine 401 Unauthorized-Ausnahme auslösen. Ich bekomme jedoch immer eine 404-Ressource, die nicht gefunden wird. Meine Konfiguration legt eine Ausnahmebehandlung fest, die jedoch ignoriert wird - wahrscheinlich, weil mein AuthenticationFilter zuvor hinzugefügt wurde und die Anforderung meinen Ausnahmebehandler nicht erreicht.

Was müsste ich ändern, um stattdessen 401 Ausnahmen zu werfen?

Ich habe einen Authentifizierungsfilter:

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;
}

eine AuthenticationProvider-Implementierung:

@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"));
}

...

}

und eine Konfiguration, die wie folgt aussieht:

@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);
}
}
7
Frame91

Ich habe die Antwort in diesem Thread gefunden: HTTP-Fehlercode 401 zurückgeben und Filterketten überspringen

Anstatt

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

Ich muss anrufen

httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

Es scheint, als würde die Kette anhalten, wenn ich sie nicht weiter anrufe und den Status auf einen anderen Code setze - die Ausnahme wird korrekt ausgelöst 

7
Frame91

Ich habe es gelöst, indem ich die folgende Anmerkung in meiner @SpringBootApplication-Klasse der obersten Ebene hinzugefügt habe:

@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})

Könnte Spring Boot die Standard-Fehlerseite nicht finden?

6
candide

Zusätzlich zu der obigen Antwort habe ich meinen Code so geändert, dass er 401 erreicht. Vorher hatte ich 500 für ein ungültiges oder fehlendes Token erhalten. 

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;

    }

}

1
Aditya Parmar