I have a strange error, hours of debugging and I can't understand.
UPDATE 1: I use Spring Security 4.0.3, running on Tomcat 7.
The problem is close to this question, maybe the SecurityContextHolder
is lost during response.redirect()
but the answer doesn't help.
The problem seems close to this question too but the answer has no sense to me.
This is my configuration:
@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");
}
}
After log in I try to get a secured URL:
@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();
}
the method inside the service
@Secured("ROLE_TEST_SERVICE")
public boolean getTestMethod() {
Sysout.out.println("Hiii")
return true
}
Now, the log when it doesn't work:
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.springframework.security.authentication.UsernamePasswordAuthenticationToken@bbbc4f45: Principal: org.springframework.security.core.userdetails.User@fc8a: 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.springframework.security.web.authentication.WebAuthenticationDetails@fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 613DFE47B2CE6E29DA3C227F8E028590; Granted Authorities: ROLE_ADMIN, ROLE_TEST_SERVICE
get security context
--------------------
org.springframework.security.core.context.SecurityContextImpl@bbbc4f45: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bbbc4f45: Principal: org.springframework.security.core.userdetails.User@fc8a: 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.springframework.security.web.authentication.WebAuthenticationDetails@fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 613DFE47B2CE6E29DA3C227F8E028590; Granted Authorities: ROLE_ADMIN, ROLE_TEST_SERVICE
get security context holder
org.springframework.security.core.context.SecurityContextImpl@ffffffff: 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)
and when it works. I just see no difference ... the Spring context is set and contains credential as sysout proove but SecurityContextHolder
is not set.
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.springframework.security.authentication.UsernamePasswordAuthenticationToken@bbbc4f45: Principal: org.springframework.security.core.userdetails.User@fc8a: 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.springframework.security.web.authentication.WebAuthenticationDetails@fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 613DFE47B2CE6E29DA3C227F8E028590; Granted Authorities: ROLE_ADMIN, ROLE_TEST_SERVICE
get security context
--------------------
org.springframework.security.core.context.SecurityContextImpl@bbbc4f45: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bbbc4f45: Principal: org.springframework.security.core.userdetails.User@fc8a: 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.springframework.security.web.authentication.WebAuthenticationDetails@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
org.springframework.security.core.context.SecurityContextImpl@4440cc59: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@4440cc59: Principal: org.springframework.security.core.userdetails.User@fc8a: 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.springframework.security.web.authentication.WebAuthenticationDetails@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
The only difference between when it works and when it doesn't is:
mywebsite.com
which is public and going to mywebsite.com/login?user=me
it worksmywebsite.com/login?user=me
The session property SPRING_SECURITY_CONTEXT
is set in both cases but not SecurityContextHolder
which fired an exception inside @Secured
line 222
I don't want to use a trick like check manually SecurityContextHolder
in a method (after redirect) and if is null
set the session property SPRING_SECURITY_CONTEXT
which is not null
, I want fix the issue at the root.
Assuming you're not able to integrate with your existing SSO provider, the following solution is probably the best you can hope for.
Fundamentally, what you're doing is not going to work unless you limit yourself to a single thread in your container (which is a terrible idea). From the docs on SecurityContextHolder
:
Associates a given
SecurityContext
with the current execution thread.
This is important - the current execution thread. If you make a login request, which is handled by Thread A, then you attempt to go to the secured page, but this request is handled by Thread B, then the SecurityContext
you set on A will not be available on B.
Spring Security is quite capable of handling this for you, by storing the security context in the session and storing / fetching it as required (see org.springframework.security.web.session.SessionManagementFilter
). You would need make some changes to your configuration, though.
Primarily, you would need to create a new class which extends from AbstractAuthenticationProcessingFilter
, and override the attemptAuthentication
method. This new filter would then need to be registered, and would need to replace the existing org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
. That class would also be a good place to look to get some idea of how this all hangs together, and what you can configure.
This new filter would do something along the lines of
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());
}
This will return a fully authenticated user, which can be used later on. This is what will be stored in the SecurityContextHolder
, and can be easily retrieved. It will also be stored in the session (if configured), so I would recommended making sure it's serializable. Since you don't make use of the password, I would strongly recommend against storing it in the User
.
Hopefully that will set you in the right direction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With