2016-07-11 24 views
10

Ho uno strano errore, ore di debug e non riesco a capire.Spring Security AuthenticationCredentialsNotFoundException, SecurityContextHolder.getContext è nullo

UPDATE 1: Io uso Primavera di sicurezza 4.0.3, in esecuzione su Tomcat 7.

Il problema è vicino a this question, forse il SecurityContextHolder è perduto durante response.redirect() ma la risposta non aiuta.

Il problema sembra vicino a this question ma la risposta non ha senso per me.

Questa è la mia configurazione:

@Configuration 
@EnableWebSecurity 
@EnableGlobalMethodSecurity(securedEnabled = true) 
public class ProjectSecurityConfiguration extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.csrf().disable(); 
     http.authorizeRequests().antMatchers("/login").anonymous(); 
    } 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth.inMemoryAuthentication().withUser(Constants.PROFIL_ADMIN).password(Constants.PROFIL_ADMIN). 
      roles("ADMIN","TEST_SERVICE"); 
    } 
} 

dopo il log in cerco di ottenere un URL protetto:

@RequestMapping(value = "/myurl", method = RequestMethod.GET) 
@ResponseBody 
public boolean getTestService(HttpServletRequest request) 
     throws SQLException, PoRulesException { 

    System.out.println("get security context"); 
    System.out.println("--------------------"); 
    SecurityContext secuContext = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); 
    System.out.println(secuContext); 
    System.out.println("get security context holder"); 
    System.out.println(SecurityContextHolder.getContext()); 

    return testService.getTestMethod(); 
} 

il metodo all'interno del servizio di

@Secured("ROLE_TEST_SERVICE") 
public boolean getTestMethod() { 
    Sysout.out.println("Hiii") 
    return true 
} 

Ora, il registro quando non funziona:

INFO 2016-07-11 15:57:00,006 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - Login 
INFO 2016-07-11 15:57:00,057 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - User : axel 
INFO 2016-07-11 15:57:00,065 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - Authenticate : true 
INFO 2016-07-11 15:57:00,065 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - Authenticate : org.springframew[email protected]bbbc4f45: Principal: [email protected]: Username: ADM; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_CONSULTER_CA,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 613DFE47B2CE6E29DA3C227F8E028590; Granted Authorities: ROLE_ADMIN, ROLE_TEST_SERVICE 
get security context 
-------------------- 
[email protected]bc4f45: Authentication: org.springframew[email protected]bbbc4f45: Principal: [email protected]: Username: ADM; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_CONSULTER_CA,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 613DFE47B2CE6E29DA3C227F8E028590; Granted Authorities: ROLE_ADMIN, ROLE_TEST_SERVICE 
get security context holder 
[email protected]ffffff: Null authentication 
ERROR 2016-07-11 15:57:01,960 [http-bio-8080-exec-10] com.po.exception.GlobalControllerExceptionHandler - org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext 
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext 
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:378) 
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:222) 
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) 
    at com.po.service.CAService$$EnhancerBySpringCGLIB$$f6e21857.getVarAndPrevCa(<generated>) 
    at com.po.mvc.controller.CaController.getVarAndPrevCa(CaController.java:56) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222) 
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) 
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) 
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814) 
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737) 
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) 
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) 
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) 
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) 
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:860) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621) 
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) 
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405) 
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515) 
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:304) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
    at java.lang.Thread.run(Thread.java:745) 

e quando funziona. Non vedo alcuna differenza ... il contesto Spring è impostato e contiene credenziali come sysout proove ma SecurityContextHolder non è impostato.

INFO 2016-07-11 16:09:45,374 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - Login 
INFO 2016-07-11 16:09:45,393 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - User : axel 
INFO 2016-07-11 16:09:45,395 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - Authenticate : true 
INFO 2016-07-11 16:09:45,395 [http-bio-8080-exec-3] com.po.mvc.controller.LoginController - Authenticate : org.springframew[email protected]bbbc4f45: Principal: [email protected]: Username: ADM; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_TEST_SERVICE; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 613DFE47B2CE6E29DA3C227F8E028590; Granted Authorities: ROLE_ADMIN, ROLE_TEST_SERVICE 
get security context 
-------------------- 
[email protected]bc4f45: Authentication: org.springframew[email protected]bbbc4f45: Principal: org.springframe[email protected]: Username: ADM; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_TEST_SERVICE Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 613DFE47B2CE6E29DA3C227F8E028590; Granted Authorities: ROLE_ADMIN, ROLE_CONSULTER_CA, ROLE_USER 
get security context holder 
[email protected]40cc59: Authentication: org.springframew[email protected]4440cc59: Principal: [email protected]: Username: ADM; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_CONSULTER_CA,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 19994511EEE72462E8D680CDADCFEC4C; Granted Authorities: ROLE_ADMIN, ROLE_CONSULTER_CA, ROLE_USER 

INFO 2016-07-11 16:09:46,879 [http-bio-8080-exec-3] Hiii 

L'unica differenza tra quando funziona e quando lo fa non è così:

  • lavori quando si collegano da una pagina del mio sito, exemple sono in mywebsite.com che è pubblica e andando per mywebsite.com/login?user=me funziona
  • ma non funziona se vengo da Google su mywebsite.com/login?user=me

La proprietà sessione SPRING_SECURITY_CONTEXT è s et in entrambi i casi, ma non SecurityContextHolder che sparava un'eccezione all'interno @Securedline 222

Non voglio usare un trucco come il check manualmente SecurityContextHolder in un metodo (dopo il redirect) e se è null impostare la proprietà della sessione SPRING_SECURITY_CONTEXT che non è null, voglio risolvere il problema alla radice.

+1

Impostazione oggetto di autenticazione in SecurityContext dentro un controllore, invece di uno schema classico di sicurezza primavera del Filtro> Manager> Provider suona Extrange a me. – jlumietu

+0

Anche se, vedo una cosa straniera. Secondo UsernamePasswordAuthenticationToken api, ha due costruttori; una presa e (oggetto principale, credenziali Oggetto) che produce un token non autenticato e un'altra presa (identità oggetto, credenziali Oggetto, raccolta ) che restituiscono un token autenticato. Stai usando il primo, quindi il tuo token potrebbe non essere autenticato – jlumietu

+0

Accediamo a un portale con SSO, quindi ogni app con LDAP da stringa ss, l'accesso a ldap è raggiunto da un'API interna (CorpLDAP nel mio esempio) che restituisce un utente se autorizzato o nullo se non. Quindi, non ho la mano sul processo di accesso che è fortemente protetto. Qui la sicurezza Spring non è per il login, quindi il "login - password" non interessa. LDAP restituisce un utente (o meno se non autorizzato) con un profilo. E io uso la sicurezza di primavera per definire quale profilo può accedere a un servizio. – amdev

risposta

4

Finalmente ho trovato la soluzione qui

https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security/

mia configurazione mancava questo punto

L'ultimo passo è che abbiamo bisogno di mappare lo springSecurityFilterChain. Possiamo fare facilmente estendendo AbstractSecurityWebApplicationInitializer e opzionalmente sostituendo i metodi per personalizzare la mappatura.

L'esempio più semplice di sotto accetta la mappatura predefinita e aggiunge springSecurityFilterChain con le seguenti caratteristiche:

springSecurityFilterChain è mappato “/ *” springSecurityFilterChain utilizza i tipi di spedizione di errori e la richiesta viene inserito il mapping springSecurityFilterChain prima di qualsiasi servlet filtro mappature già configurati

public class SecurityWebApplicationInitializer 
    extends AbstractSecurityWebApplicationInitializer { 
} 

Solo l'aggiunta di quella classe ha risolto il problema, se non è possibile aggiungere @Order

“Ordinamento dei WebApplicationInitializer”

Se sono aggiunti dopo AbstractSecurityWebApplicationInitializer viene richiamato tutti i mapping servlet filtro, potrebbero essere aggiunto per errore prima di springSecurityFilterChain.A meno che un'applicazione contenga istanze di filtro che non devono essere protette, springSecurityFilterChain deve essere prima di qualsiasi altra mappatura del filtro. L'annotazione @Order può essere utilizzata per garantire che qualsiasi oggetto WebApplicationInitializer sia caricato in un ordine deterministico.

@Order(1) 
public class SpringWebMvcInitializer extends 
    AbstractAnnotationConfigDispatcherServletInitializer { 

    @Override 
    protected Class<?>[] getRootConfigClasses() { 
    return new Class[] { HelloWebSecurityConfiguration.class }; 
    } 
    ... 
} 

    @Order(2) 
    public class SecurityWebApplicationInitializer 
     extends AbstractSecurityWebApplicationInitializer { 
    } 
+0

Il 'SecurityWebApplicationInitializer' lo ha risolto per me, evviva! –

4

Supponendo che non si sia in grado di integrarsi con il proprio provider SSO esistente, la soluzione seguente è probabilmente la migliore che si possa desiderare.

Fondamentalmente, quello che stai facendo non funzionerà se non ti limiti a un singolo thread nel tuo contenitore (che è una pessima idea). Dalla documentazione sul SecurityContextHolder:

Associates un dato SecurityContext al thread di esecuzione corrente.

Questo è importante - la corrente thread di esecuzione.Se si effettua una richiesta di accesso, gestita dal thread A, si tenta di accedere alla pagina protetta, ma questa richiesta viene gestita da Thread B, quindi lo SecurityContext impostato su A lo sarà non disponibile su B.

Spring Security è in grado di gestirlo al meglio, memorizzando il contesto di sicurezza nella sessione e memorizzandolo/recuperandolo come richiesto (vedere org.springframework.security.web.session.SessionManagementFilter). Tuttavia, è necessario apportare alcune modifiche alla configurazione.

In primo luogo, è necessario creare una nuova classe che si estenda da AbstractAuthenticationProcessingFilter e sovrascrivere il metodo attemptAuthentication. Questo nuovo filtro dovrebbe quindi essere registrato e dovrebbe sostituire lo esistente org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter. Quella classe sarebbe anche un buon posto dove guardare per avere un'idea di come tutto questo si blocca e cosa è possibile configurare.

Questo nuovo filtro avrebbe fatto qualcosa sulla falsariga di

public Authentication attemptAuthentication(HttpServletRequest request, 
    HttpServletResponse response) throws AuthenticationException { 
    UserLDAP ldapUser = CorpLDAP.findUser(request); 
    User user = new User(ldapUser.getProfil(), ldapUser.getPwd(), Collections.emptyList()); 
    return new UsernamePasswordAuthenticationToken(user, "", Collections.emptyList()); 
} 

Ciò restituirà un utente completamente autenticato, che possono essere utilizzati in seguito. Questo è ciò che verrà memorizzato nello SecurityContextHolder e può essere facilmente recuperato. Sarà anche memorizzato nella sessione (se configurato), quindi mi raccomando di assicurarmi che sia serializzabile. Dal momento che non si utilizza la password, vorrei fortemente raccomandare contro la memorizzazione nel User.

Speriamo che ti metta nella giusta direzione.

+0

La risposta sembra interessante ma alla fine ho trovato una soluzione molto lontana da ciò, SecurityContextHolder è stato perso perché in alcuni casi (/ login in GET dal di fuori del sito Web) il bean di configurazione è stato caricato nell'ordine sbagliato. Non capisco davvero. Se voi o qualcuno potete spiegare perché estendere AbstractSecurityWebApplicationInitializer risolve davvero il problema, gli darò la taglia. – amdev