5

Prima di tutto voglio commentare che ho già controllato le altre domande di Stack Overflow e realizzato il mio approccio sulla base delle risposte: https://stackoverflow.com/a/14425801/2487263 e https://stackoverflow.com/a/16101649/2487263Come gestire le diverse eccezioni di autenticazione in Spring Security 3.1?

Sto cercando di assicurarsi un API REST utilizzando la protezione Primavera 3.1 in un Primavera 3.2, l'applicazione Spring MVC, sto usando un approccio di base di autenticazione con una semplice configurazione:

<http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint"> 
    <intercept-url pattern="/**" access="ROLE_USER"/> 
    <http-basic /> 
</http> 

Come potete vedere sto usando il mio punto di ingresso personalizzato ho il mio oggetto ErrorResponse che aggiungerà al http risposta in formato json, vedere il codice seguente:

@Component 
public class AuthenticationFailedEntryPoint implements AuthenticationEntryPoint { 
    static Logger log = Logger.getLogger(AuthenticationFailedEntryPoint.class); 

    @Override 
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 
     log.error(ExceptionUtils.getStackTrace(authException)); 
     ErrorResponse errorResponse = new ErrorResponse(); 

      ... here I fill my errorResponse object ... 

     ObjectMapper jsonMapper = new ObjectMapper(); 

     response.setContentType("application/json;charset=UTF-8"); 
     response.setStatus(status); 

     PrintWriter out = response.getWriter(); 
     out.print(jsonMapper.writeValueAsString(errorResponse)); 
    } 
} 

ho provato l'approccio con due casi di test:

  1. cercare di consumare un servizio senza fornire intestazioni di autenticazione di base:

Questa è la richiesta/risposta:

GET http://localhost:8081/accounts/accounts?accountNumber=1013 


-- response -- 
401 Unauthorized 
Server: Apache-Coyote/1.1 

Content-Type: application/json;charset=UTF-8 

Content-Length: 320 

Date: Fri, 25 Oct 2013 17:11:15 GMT 

Proxy-Connection: Keep-alive 

{"status":401,"messages":[{"code":"000011","message":"You are not authorized to reach this endpoint"}]} 

2.- Provare a utilizzare lo stesso servizio ma ora l'invio di base autentico intestazioni ation con una password errata:

Questa è la richiesta/risposta:

GET http://localhost:8081/accounts/accounts?accountNumber=1013 
Authorization: Basic bXl1c2VyOmdvb2RieWU= 


-- response -- 
401 Unauthorized 
Server: Apache-Coyote/1.1 

WWW-Authenticate: Basic realm="Spring Security Application" 

Content-Type: text/html;charset=utf-8 

Content-Length: 1053 

Date: Fri, 25 Oct 2013 17:03:09 GMT 

Proxy-Connection: Keep-alive 

<html> ... ugly html generated by tc server ... </html> 

Come si può vedere nel primo caso il punto di ingresso è stato raggiunto e il metodo commence è stato eseguito con la corretta gestione del eccezione e la risposta JSON è stata restituita. Ma questo non è accaduto quando la password era sbagliata.

Nei registri ho scoperto che entrambi i casi stanno producendo un flusso diverso:

Per il caso 1 (nessuna intestazione auth):

... 
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /accounts?accountNumber=1013; Attributes: [ROLE_USER] 
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.sprin[email protected]9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS 
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.access.vote.AffirmativeBased - Voter: [email protected], returned: -1 
2013-10-25 13:11:15,831 DEBUG tomcat-http--13 org.springframework.security.access.vote.AffirmativeBased - Voter: [email protected]ef7, returned: 0 
2013-10-25 13:11:15,831 DEBUG tomcat-http--13 org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point 
org.springframework.security.access.AccessDeniedException: Access is denied 
... 

Per il caso 2 (password errata):

... 
2013-10-25 13:03:08,941 DEBUG tomcat-http--11 org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'myuser' 
2013-10-25 13:03:08,941 DEBUG tomcat-http--11 org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider 
2013-10-25 13:03:09,544 DEBUG tomcat-http--11 org.springframework.security.authentication.dao.DaoAuthenticationProvider - Authentication failed: password does not match stored value 
2013-10-25 13:03:09,545 DEBUG tomcat-http--11 org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials 
2013-10-25 13:00:30,136 DEBUG tomcat-http--9 org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed 
... 

Il primo caso genera una AccessDeniedException che viene catturata e inviata al metodo di inizio nel mio punto di ingresso, ma il secondo caso che genera una eccezione di BadCredentials non si avvia o il punto di ingresso.

La cosa strana qui è che il metodo Commence dovrebbe ricevere un AuthenticationException, ma AccessDeniedException non è un AuthenticationException ma BadCredentialsException è, vedere l'albero di ereditarietà dalla sicurezza primavera documentazione 3.1 API:

java.lang.Object 
    extended by java.lang.Throwable 
     extended by java.lang.Exception 
      extended by java.lang.RuntimeException 
       extended by org.springframework.security.access.AccessDeniedException 

java.lang.Object 
    extended by java.lang.Throwable 
     extended by java.lang.Exception 
      extended by java.lang.RuntimeException 
       extended by org.springframework.security.core.AuthenticationException 
        extended by org.springframework.security.authentication.BadCredentialsException 

Perché il metodo di inizio viene chiamato con un'eccezione che non è del tipo corretto e perché non viene chiamata quando si ha l'eccezione BadCredentialsException del tipo corretto?

cura --- Implementata la risposta @Luke

due descritti di soluzioni utilizzano l'AuthenticationEntryPoint personalizzato indicato nella domanda, deve essere modificato scegliendo una delle due opzioni seguenti la configurazione:

  1. Aggiunta di un BASIC_AUTH_FILTER personalizzato:

    <http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint"> 
        <intercept-url pattern="/**" access="ROLE_USER"/> 
        <custom-filter position="BASIC_AUTH_FILTER" ref="authenticationFilter" /> 
    </http> 
    
    <beans:bean id="authenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter"> 
        <beans:constructor-arg name="authenticationManager" ref="authenticationManager" /> 
        <beans:constructor-arg name="authenticationEntryPoint" ref="authenticationFailedEntryPoint" /> 
    </beans:bean> 
    
  2. O l'aggiunta del punto di ingresso per l'elemento http-base, IMO è la soluzione più pulita:

    <http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint"> 
        <intercept-url pattern="/**" access="ROLE_USER"/> 
        <http-basic entry-point-ref="authenticationFailedEntryPoint" /> 
    </http> 
    
+0

Informazioni sul problema di eccezione commentato alla fine della domanda, sembra che AccesDeniedException sia stato tradotto da ExceptionTranlationFilter in AuthenticationException. Questo è il motivo per cui il metodo di inizio nel punto di ingresso personalizzato viene chiamato – raspacorp

+0

raspacorp - Come eseguire il metodo "inizio" anche se abbiamo @ExceptionalHandler sul posto? – Prateek

+0

@pashtika Questo è un problema che ho avuto quasi due anni fa e che è stato risolto utilizzando quel punto di accesso personalizzato. Ricordo che i gestori di eccezioni non stavano cogliendo quell'eccezione come succede in una fase iniziale. Tuttavia non so se è stato risolto nelle versioni più recenti del framework, nel qual caso basta aggiungere un gestore di eccezioni appropriato. – raspacorp

risposta

4

Credo che il problema è che, dopo un'autenticazione di base fallito, la chiamata al punto di ingresso avviene direttamente dal BasicAuthenticationFilter, che di default sarà l'implementazione integrata.

È necessario anche impostare l'elemento entry-point-ref attribute on the http-basic per risolvere il problema.

In alternativa è possibile definire il filtro di autenticazione di base come bean ed evitare completamente lo spazio dei nomi.

+0

Ancora non funziona – Prateek

Problemi correlati