1

This is the message that I have in the Spring logs:

[DEBUG] 2016-04-22 10:39:19,469 [] [-254028363]  org.springframework.security.web.access.intercept.FilterSecurityInterceptor authenticateIfRequired - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@6faa3d44: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: F5BF9CC6CFE7D2C98A0CB043F4D5C10F; Granted Authorities: ROLE_ANONYMOUS

org.springframework.security.web.access.intercept.FilterSecurityInterceptor 
authenticateIfRequired - Previously Authenticated
org.springframework.security.authentication.AnonymousAuthenticationToken@6faa3d44: 
Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; 
Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c:
RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: F5BF9CC6CFE7D2C98A0CB043F4D5C10F;
Granted Authorities: ROLE_ANONYMOUS

Considering the implementation of "authenticateIfRequired()" method of org.springframework.security.access.intercept.AbstractSecurityInterceptor.class:

/**
 * Checks the current authentication token and passes it to the AuthenticationManager
 * if {@link org.springframework.security.core.Authentication#isAuthenticated()}
 * returns false or the property <tt>alwaysReauthenticate</tt> has been set to true.
 *
 * @return an authenticated <tt>Authentication</tt> object.
 */
private Authentication authenticateIfRequired() {
    Authentication authentication = SecurityContextHolder.getContext()
            .getAuthentication();

    if (authentication.isAuthenticated() && !alwaysReauthenticate) {
        if (logger.isDebugEnabled()) {
            logger.debug("Previously Authenticated: " + authentication);
        }

        return authentication;
    }

    authentication = authenticationManager.authenticate(authentication);

    // We don't authenticated.setAuthentication(true), because each provider should do
    // that
    if (logger.isDebugEnabled()) {
        logger.debug("Successfully Authenticated: " + authentication);
    }

    SecurityContextHolder.getContext().setAuthentication(authentication);

    return authentication;
}

The result of this situation is that 'anonymousUser' is never authenticated.

I never see the trace "Successfully Authenticated: " for 'anonymousUser' in the Spring logs.

Furthermore, the SecurityContextHolder will not contain any Authentication object, because is never executed this line of code:

SecurityContextHolder.getContext().setAuthentication(authentication);

In fact, if I execute SecurityContextHolder.getContext().getAuthentication(), I have a null object as a return.

This behaviour doesn't match with what is written in the documentation:

http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#anonymous-overview

This is what we mean by anonymous authentication. Note that there is no real conceptual difference between a user who is "anonymously authenticated" and an unauthenticated user. Spring Security’s anonymous authentication just gives you a more convenient way to configure your access-control attributes. Calls to servlet API calls such as getCallerPrincipal, for example, will still return null even though there is actually an anonymous authentication object in the SecurityContextHolder.

There are other situations where anonymous authentication is useful, such as when an auditing interceptor queries the SecurityContextHolder to identify which principal was responsible for a given operation. Classes can be authored more robustly if they know the SecurityContextHolder always contains an Authentication object, and never null.

Why this behaviour? In my case, is not true that Authentication object is never null.

This is my Java Config:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
public class SecurityContextConfig extends WebSecurityConfigurerAdapter{

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/index.html","/myApp/pages/**").permitAll().and()
            .authorizeRequests().anyRequest().authenticated().and()
            .httpBasic().and()
            .csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
            .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll().logoutSuccessUrl("/index.html");
    }

    // ...
}

Any idea? Thanks!

EDIT

This is the complete stack of the call of the method "authenticateIfRequired()":

    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.authenticateIfRequired(AbstractSecurityInterceptor.java:345)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:228)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:123)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at myApp.security.config.SecurityContextConfig$1.doFilterInternal(SecurityContextConfig.java:150)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:96)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at myApp.filter.RequestFilter.doFilter(RequestFilter.java:21)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:521)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1096)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:674)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

This is my current implementation:

public class MyDaoAuthenticationProvider extends DaoAuthenticationProvider {

    @Autowired
    AuthenticationHandler authenticationHandler;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Authentication authenticated = null;

        try {
            authenticated = super.authenticate(authentication);
        } catch (Exception e) {
            e.printStackTrace();
            authenticationHandler.handleFailedAuthentication(authentication.getName());
            throw e;
        }

        // this works
        SecurityContextHolder.getContext().setAuthentication(authentication);
        authenticationHandler.handleSuccessAuthentication(authentication.getName());
        return authenticated;
    }

}

The SecurityContextHolder.getContext().getAuthentication() is called in a @MappedSuperclass, when I need the informsation of the current user.

public abstract class TableObject implements Serializable {

    @PreUpdate
    protected void onUpdate() {
        userModifier = SecurityContextHolder.getContext().getAuthentication().getName();
// this don't work
    }

}
Alessandro C
  • 3,310
  • 9
  • 46
  • 82
  • If it is `null` it is either because you are calling this outside the processing of the Spring Security filters or after the cleaning of the thread local. Else it will be filled. It also has nothing to do with that part of the code as if it would be `null` a `NullPointerException` would be thrown! So WHERE and WHEN exactly are you calling this code ... – M. Deinum Apr 22 '16 at 09:46
  • I have added the full stack trace; the call is automatic. – Alessandro C Apr 22 '16 at 10:01
  • Note that "authentication" object at the beginning of the method is NOT null. It is the "anonymousUser". – Alessandro C Apr 22 '16 at 10:03
  • The stack is obtained forcing "null" in debugging. – Alessandro C Apr 22 '16 at 10:04
  • 1
    This happens because "anonymousUser" is autheniticated, and the flag "alwaysReauthenticate" is setted to false by default. – Alessandro C Apr 22 '16 at 10:07
  • The `Authentication` is still not null ... The actual authentication as present at the start is returned... Which was the `AnonymousAuthenticationToken`. So at this point it isn't `null` nor becomes `null`... Also I wasn't regarding the code of spring security but the location where you call the code to obtain the `Authentication`... As that can and will only be `null` if you execute outside the scope of spring security. So again where are you trying to obtain the authentication... – M. Deinum Apr 22 '16 at 10:11
  • I have a custom DaoAuthenticationProvider, it is on the Spring Security context obviously, otherwise the application can't start! – Alessandro C Apr 22 '16 at 10:22
  • You aren't answering question and drawing wrong conclusions... Where is the call to `SecurityContextHolder.getContext().getAuthentication(),` being made? If that is in your custom `DaoAuthenticationProvider` then yes it is `null` because it hasn't been set at that stage... – M. Deinum Apr 22 '16 at 10:25
  • There is something that is wrong in my configuration probably, because I noticed that SecurityContextHolder.getContext().getAuthentication() returns null not only in my custom DaoAuthenticationProvider, but also in another class where this problem was never happened before. – Alessandro C Apr 22 '16 at 10:36
  • Without the code and configuration that is hard to tell, however your analysis so far is flawed. As mentioned as soon as Spring Security processed the request and all filters succeeded then there is an `Authentication` object. If you try to obtain it before the security chain or outside of the security chain then obviously it will be `null`. So please add the configuration and code that is trying to obtain the `Authentication` object. However in your `DaoAuthenticationProvider` it will always return `null` as it hasn't been set by then. – M. Deinum Apr 22 '16 at 10:41
  • I have added the implementation. Consider that before there was not the custom DaoAuthenticationProvider, and the code worked, but I need it for the new implementation. – Alessandro C Apr 22 '16 at 10:57
  • Why would you need a custom `DaoAuthenticationProvider` for that... Looks like you need to use one of the extension points instead o hacking your own provider. Spring Security has succes and failure handlers for what you are trying to achieve. The fact that you do't get an exception doesn't mean authentication was successful, it could also be that authentication couldn't be handled. – M. Deinum Apr 22 '16 at 12:21
  • I'm using it because I'm trying to have an implementation that can handle the success/failure authentications to make some things. I'm using basic-authentication, so I can't use a successHandler by loginForm. – Alessandro C Apr 22 '16 at 12:27
  • What is it you need to do, as there are also events fired and using an `ApplicationListener` to listen to those events is even easier... Next to that it really belongs in the filter then I would suggest extending that and not hack it in the provider as it doesn't have to provide a successful or failed login it might not be able to handle it. – M. Deinum Apr 22 '16 at 12:29
  • Can you give me an idea for an ApplicationListener approach, please? – Alessandro C Apr 22 '16 at 12:30
  • Just implement the interface and listen for `AbstractAuthenticationEvent`. In the case of `AuthenticationSuccessEvent` or `InteractiveAuthenticationSuccessEvent` it was OK in the case of one of the subclasses of `AbstractAuthenticationFailureEvent` it was NOK. – M. Deinum Apr 22 '16 at 12:34
  • Ok, I'll try that solution. Thanks! – Alessandro C Apr 22 '16 at 12:38
  • I'll tried your suggest. It can intercepts every single event (login OK, login KO, etc...), but if I have to load **in that moment** the informations in the SecurityContextHolder.getContext().getAuthentication(), is still a null object. – Alessandro C Apr 22 '16 at 14:03
  • Because you shouldn't do it and you are using it wrong... The `AuthenticationEvent` contains the `Authentication` it concerned. In the case of a login failure the `getAuthentication` will be `null` because it will never be set due to the failed login. I suggest a read of the documentation (reference and javadoc) of the classes you use instead of trying to work around the framework. – M. Deinum Apr 23 '16 at 09:42

0 Answers0