添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
面冷心慈的茶叶  ·  cosmosSearch vector ...·  7 月前    · 
谦逊的开心果  ·  计算器怎么用log2-掘金·  1 年前    · 
逼格高的楼梯  ·  Get Started with UE-V ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Spring Security AuthenticationCredentialsNotFoundException, SecurityContextHolder.getContext is null

Ask Question

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:

  • works when I connect from a page of my website, exemple I'm on mywebsite.com which is public and going to mywebsite.com/login?user=me it works
  • but doesn't work if I come from Google on mywebsite.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.

    Setting authentication object in SecurityContext inside a controller instead of a classic Spring security schema of Filter > Manager > Provider sounds extrange to me. – jlumietu Jul 21, 2016 at 12:33 Even though, I see an extrange thing. According to UsernamePasswordAuthenticationToken api, it has two constructors; one taking and (Object principal, Object credentials) which produces a non-authenticated token and another one taking (Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) which return an authenticated token. You are using ther first one, so your token could be not authenticated – jlumietu Jul 21, 2016 at 12:55 What sort of SSO? If you're using SAML or OAuth, it would probably make a lot more sense to integrate properly with that, rather than trying to work around Spring Security. If not, then it will be a bit trickier. – ipsi Jul 21, 2016 at 14:35 Honnestly, it's a very big company who have locked process, and i m not free with security, we MUST deal with the ldap api provided, it's the standard process. – amdev Jul 21, 2016 at 15:03 Using LDAP and every API you already have it's ok within Spring Security. What @ipsi says to be a bit tricky is to call the ldap authentication from inside a controller instead of a Filter > AuthenticationManager > Provider > UserDetailsService schema – jlumietu Jul 22, 2016 at 11:17

    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.

    The answer seems interesting but finally i found a solution far away from that, the SecurityContextHolder was lost because in the some case ( /login in GET from outside the website ) configuration's bean was loaded in the wrong order. I don't really get it. If you or someone can explain why extend AbstractSecurityWebApplicationInitializer really solve the problem I'll give it the bounty. – amdev Jul 21, 2016 at 20:02

    Finally I found the solution here

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

    my configuration was missing this point

    The last step is we need to map the springSecurityFilterChain. We can easily do this by extending AbstractSecurityWebApplicationInitializer and optionally overriding methods to customize the mapping.

    The most basic example below accepts the default mapping and adds springSecurityFilterChain with the following characteristics:

    springSecurityFilterChain is mapped to “/*” springSecurityFilterChain uses the dispatch types of ERROR and REQUEST The springSecurityFilterChain mapping is inserted before any servlet Filter mappings that have already been configured

    public class SecurityWebApplicationInitializer 
       extends AbstractSecurityWebApplicationInitializer {
    

    Just adding that class solved the problem, if not you can add @Order

    “Ordering of WebApplicationInitializer”

    If any servlet Filter mappings are added after AbstractSecurityWebApplicationInitializer is invoked, they might be accidentally added before springSecurityFilterChain. Unless an application contains Filter instances that do not need to be secured, springSecurityFilterChain should be before any other Filter mappings. The @Order annotation can be used to help ensure that any WebApplicationInitializer is loaded in a deterministic order.

    @Order(1)
    public class SpringWebMvcInitializer extends
       AbstractAnnotationConfigDispatcherServletInitializer {
      @Override
      protected Class<?>[] getRootConfigClasses() {
        return new Class[] { HelloWebSecurityConfiguration.class };
        @Order(2)
        public class SecurityWebApplicationInitializer 
           extends AbstractSecurityWebApplicationInitializer {
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.