2014-10-28 11 views
5

Prima di tutto, vorrei segnalare che non conosco molto Spring Security, in realtà conosco abbastanza poco le sue interfacce e classi, ma io ha un compito non così semplice da fare e non riesco a capirlo. Il mio codice è basato sul seguente post nel forum Spring Security (non ho lo stesso problema del proprietario del post): http://forum.spring.io/forum/spring-projects/security/747178-security-filter-chain-is-always-calling-authenticationmanager-twice-per-requestSpring Security: Return Status 401 quando AuthenticationManager genera BadCredentialsException

Sto programmando un sistema MVC Spring che servirà il contenuto HTTP ma, in Per fare ciò, ha un controllo preliminare (che attualmente sto usando RequestHeaderAuthenticationFilter con un custom AuthenticationManager).

Per autorizzare l'utente, controllerò il token contro due sorgenti, un "database" di cache Redis e Oracle. Se il token non viene trovato in nessuna di queste fonti, il metodo di autenticazione del mio AuthenticationManager personalizzato genera una BadCredentialsException (che credo rispetti il ​​contratto AuthenticationManager).

Ora mi piacerebbe ritornare nella risposta HTTP 401 - Non autorizzato, ma Spring continua a restituire 500 - Errore del server. È possibile personalizzare la mia configurazione per restituire solo 401 non 500?

Ecco il codice rilevante:

SecurityConfig - principale config sicurezza primavera

package br.com.oiinternet.imoi.web.config; 

import javax.validation.constraints.NotNull; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.boot.autoconfigure.security.SecurityProperties; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.annotation.Order; 
import org.springframework.http.HttpMethod; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.config.http.SessionCreationPolicy; 
import org.springframework.security.web.AuthenticationEntryPoint; 
import org.springframework.security.web.access.AccessDeniedHandler; 
import org.springframework.security.web.access.AccessDeniedHandlerImpl; 
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; 
import org.springframework.security.web.authentication.logout.LogoutFilter; 
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter; 

@Configuration 
@EnableWebSecurity 
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 

    private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class); 

    public static final String X_AUTH_TOKEN = "X-Auth-Token"; 

    private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl(); 

    @Bean 
    public AuthenticationManager authenticationManager() { 
     return new TokenBasedAuthenticationManager(); 
    } 

    @Bean 
    public AuthenticationEntryPoint authenticationEntryPoint() { 
     return new Http403ForbiddenEntryPoint(); 
    } 

    @Bean 
    public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
      final AuthenticationManager authenticationManager) { 
     RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter(); 
     filter.setAuthenticationManager(authenticationManager); 
     filter.setExceptionIfHeaderMissing(false); 
     filter.setPrincipalRequestHeader(X_AUTH_TOKEN); 
     filter.setInvalidateSessionOnPrincipalChange(true); 
     filter.setCheckForPrincipalChanges(true); 
     filter.setContinueFilterChainOnUnsuccessfulAuthentication(false); 
     return filter; 
    } 

    /** 
    * Configures the HTTP filter chain depending on configuration settings. 
    * 
    * Note that this exception is thrown in spring security headerAuthenticationFilter chain and will not be logged as 
    * error. Instead the ExceptionTranslationFilter will handle it and clear the security context. Enabling DEBUG 
    * logging for 'org.springframework.security' will help understanding headerAuthenticationFilter chain 
    */ 
    @Override 
    protected void configure(final HttpSecurity http) throws Exception { 
     RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = fromContext(http, 
       RequestHeaderAuthenticationFilter.class); 

     AuthenticationEntryPoint authenticationEntryPoint = fromContext(http, AuthenticationEntryPoint.class); 

     http.authorizeRequests() 
      .antMatchers(HttpMethod.GET, "/auth").permitAll() 
      .antMatchers(HttpMethod.GET, "/**").authenticated() 
      .antMatchers(HttpMethod.POST, "/**").authenticated() 
      .antMatchers(HttpMethod.HEAD, "/**").authenticated() 
     .and() 
      .sessionManagement() 
       .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
     .and().securityContext() 
     .and().exceptionHandling() 
       .authenticationEntryPoint(authenticationEntryPoint) 
       .accessDeniedHandler(accessDeniedHandler) 
     .and() 
      .addFilterBefore(requestHeaderAuthenticationFilter, LogoutFilter.class); 
    } 

    private <T> T fromContext(@NotNull final HttpSecurity http, @NotNull final Class<T> requiredType) { 
     @SuppressWarnings("SuspiciousMethodCalls") 
     ApplicationContext ctx = (ApplicationContext) http.getSharedObjects().get(ApplicationContext.class); 
     return ctx.getBean(requiredType); 
    } 
} 

TokenBasedAuthenticationManager - mia abitudine AuthenticationManager

package br.com.oiinternet.imoi.web.config; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.authentication.BadCredentialsException; 
import org.springframework.security.core.Authentication; 
import org.springframework.security.core.AuthenticationException; 

import br.com.oi.oicommons.lang.message.Messages; 
import br.com.oiinternet.imoi.service.AuthService; 
import br.com.oiinternet.imoi.web.security.auth.AuthenticationAuthorizationToken; 

public class TokenBasedAuthenticationManager implements AuthenticationManager { 

    @Autowired 
    private AuthService authService; 

    @Autowired 
    private Messages messages; 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 

     final String token = (String) authentication.getPrincipal(); 

     if (authService.isAuthorized(token) || authService.authenticate(token)) { 
      return new AuthenticationAuthorizationToken(token); 
     } 
      throw new BadCredentialsException(messages.getMessage("access.bad.credentials")); 
    } 

} 

Esempio di richiesta di ciclo/risposta utilizzando ricciolo:

[email protected]:curl --header "X-Auth-Token: 2592cd35124dc3d79bdd82407220a6ea7fad9b8b313a1205cf1824a5ce726aa8dd763cde8c05faadae48b47252de95b0" http://localhost:8081/test/auth -v 
* Hostname was NOT found in DNS cache 
* Trying 127.0.0.1... 
* Connected to localhost (127.0.0.1) port 8081 (#0) 
> GET /test/auth HTTP/1.1 
> User-Agent: curl/7.35.0 
> Host: localhost:8081 
> Accept: */* 
> X-Auth-Token: 2592cd35124dc3d79bdd82407220a6ea7fad9b8b313a1205cf1824a5ce726aa8dd763cde8c05faadae48b47252de95b0 
> 
< HTTP/1.1 500 Server Error 
< X-Content-Type-Options: nosniff 
< X-XSS-Protection: 1; mode=block 
< Pragma: no-cache 
< X-Frame-Options: DENY 
< Content-Type: application/json;charset=UTF-8 
< Connection: close 
* Server Jetty(9.1.0.v20131115) is not blacklisted 
< Server: Jetty(9.1.0.v20131115) 
< 
* Closing connection 0 
{"timestamp":1414513379405,"status":500,"error":"Internal Server Error","exception":"org.springframework.security.authentication.BadCredentialsException","message":"access.bad.credentials","path":"/test/auth"} 

risposta

12

Ho dato un'occhiata alla fonte. Sembrerebbe che si potrebbe raggiungere questo abbastanza facilmente da sottoclassi RequestHeaderAuthenticationFilter e l'override del metodo unsuccessfulAuthentication (...) che viene chiamato subito dopo viene rilevata un'autenticazione fallita e poco prima di una nuova RuntimeException è gettato:

public class MyRequestHeaderAuthenticationFilter extends RequestHeaderAuthenticationFilter { 

     @Override 
     protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, 
       AuthenticationException failed) { 
      super.unsuccessfulAuthentication(request, response, failed); 

      // see comments in Servlet API around using sendError as an alternative 
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 
     } 
    } 

Poi basta puntare il filtro di configurazione a un'istanza di questo.

Problemi correlati