How is SecurityContextLogoutHandler's clearAut

2020-08-23 02:23发布

问题:

Spring's SecurityContextLogoutHandler notes that the clearAuthentication flag is used to:

removes the Authentication from the SecurityContext to prevent issues with concurrent requests.

What specific issue is being prevented by removing the Authentication from the SecurityContext? Why isn't simply invalidating the session (which is the other responsibility of SecurityContextLogoutHandler) sufficient?

By not clearing the SecurityContext is the concern that a SecurityContextPersistenceFilter may preserve the current authentication to a new session id? Effectively leaving the user logged in just with a new session?

回答1:

clearAuthentication flag was added in this commit with comment

Previously there was a race condition could occur when the user attempts to access a slow resource and then logs out which would result in the user not being logged out.

SecurityContextLogoutHandler will now remove the Authentication from the SecurityContext to protect against this scenario.

It fixed this issue (same issue on github). Quote:

HttpSessionSecurityContextRepository restores authentication to the session if session is invalidated from another thread if SecurityContextPersistenceFilter execution takes significant amount of time.

I am using Spring + JSF + DWR framework + GWT event service (ajax push). In any time there is at least one thread waiting at the server side for push events. This request is handled by SecurityContextPersistenceFilter which remembers the authentication at the moment of request's arriving to the server. If during the processing of this filter the session is being invalidated (by clicking logout in another tab of invalidating session by id from admin area) then HttpSessionSecurityContextRepository put the outdated authentication to the new session(which is created by JSF framework, so the session is changed during the processing of SecurityContextPersistenceFilter ). This easily reproducable if some processing delay is inserted to SecurityContextPersistenceFilter.

SaveToSessionResponseWrapper should remember the initial HttpSession and check if the original session was invalidated so it won't set the current authentication to the new session.



回答2:

What is SecurityContextLogoutHandler?

SecurityContextLogoutHandler is a handler which implements LogoutHandler.

What SecurityContextLogoutHandler does?

  1. It performs a logout by modifying the SecurityContextHolder.
  2. It will also invalidate the HttpSession if isInvalidateHttpSession() is true and the session is not null.
  3. It will also remove the Authentication from the current SecurityContext if clearAuthentication is set to true (default).

Is SecurityContextHolder thread safe?

Yes, it's thread safe with the default strategy (MODE_THREADLOCAL) (as long as you don't try to change the strategy on the fly). However, if you want spawned threads to inherit SecurityContext of the parent thread, you should set MODE_INHERITABLETHREADLOCAL.

Also aspects don't have any "threading logic", they are executed at the same thread as the advised method.

Credit goes to @axtavt

What is authentication in Spring Security?

Authentication: The framework tries to identify the end user with the provided credentials. The authentication can be done against a third party system plugged into Spring Security.

Let's consider a standard authentication scenario that everyone is familiar with.

  1. A user is prompted to log in with a username and password.
  2. The system (successfully) verifies that the password is correct for the username.
  3. The context information for that user is obtained (their list of roles and so on).

A security context is established for the user The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information.

The first three items constitute the authentication process so we'll take a look at how these take place within Spring Security.

  1. The username and password are obtained and combined into an instance of UsernamePasswordAuthenticationToken (an instance of the Authentication interface, which we saw earlier).
  2. The token is passed to an instance of AuthenticationManager for validation.
  3. The AuthenticationManager returns a fully populated Authentication instance on successful authentication.
  4. The security context is established by calling SecurityContextHolder.getContext().setAuthentication(...), passing in the returned authentication object.

SecurityContextPersistentFilter

The name is quite explicit. The SecurityContextPersistentFilter interface purpose is to store the security context in some repository. To achieve this task, the filter delegates the job to a SecurityContextRepository interface. Spring provides a default implementation for this interface: org.springframework.security.web.context.HttpSessionSecurityContextRepository. This is quite self-explanatory. The repository for the security context is simply the current user HTTP session. Below is the XML configuration for the SecurityContextPersistentFilter

<!-- Filter to store the Authentication object in the HTTP Session -->   
<bean id="securityContextPersistentFilter"
    class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
    <property name="securityContextRepository" ref="securityContextRepository" />
</bean>


<bean id="securityContextRepository"
    class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />

LogoutFilter

The LogoutFilter is in charge of logging out the current user and invalidating the security context. The task of invalidating the HTTP session is again delegated to another actor, the SecurityContextLogoutHandler.

This handler is injected in the LogoutFilter constructor:

<bean id="logoutFilter"
    class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <constructor-arg value="/pages/Security/logout.html" />
    <constructor-arg>
        <list>
            <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
        </list>
    </constructor-arg>
    <property name="filterProcessesUrl" value="/j_myApplication_logout"/>
</bean>

<constructor-arg value="/pages/Security/logout.html" /> - it defines the URL of the logout page.

The SecurityContextLogoutHandler is injected as constructor argument at <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>

The HTML URL for the logout action is define by the filterProcessesUrl parameter at <property name="filterProcessesUrl" value="/j_myApplication_logout"/>

Resource Link:

  1. https://doanduyhai.wordpress.com/2012/01/22/spring-security-part-i-configuration-and-security-chain/
  2. https://doanduyhai.wordpress.com/2012/02/05/spring-security-part-ii-securitycontextpersistentfilter-logoutfilter/
  3. http://shazsterblog.blogspot.com/2014/02/spring-security-custom-filterchainproxy.html
  4. http://docs.spring.io/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/web/context/SecurityContextPersistenceFilter.html


回答3:

http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html Storing the SecurityContext between requests

Depending on the type of application, there may need to be a strategy in place to store the security context between user operations. In a typical web application, a user logs in once and is subsequently identified by their session Id. The server caches the principal information for the duration session. In Spring Security, the responsibility for storing the SecurityContext between requests falls to the SecurityContextPersistenceFilter, which by default stores the context as an HttpSession attribute between HTTP requests. It restores the context to the SecurityContextHolder for each request and, crucially, clears the SecurityContextHolder when the request completes. You shouldn't interact directly with the HttpSession for security purposes. There is simply no justification for doing so - always use the SecurityContextHolder instead.

Many other types of application (for example, a stateless RESTful web service) do not use HTTP sessions and will re-authenticate on every request. However, it is still important that the SecurityContextPersistenceFilter is included in the chain to make sure that the SecurityContextHolder is cleared after each request.

[Note] Note In an application which receives concurrent requests in a single session, the same SecurityContext instance will be shared between threads. Even though a ThreadLocal is being used, it is the same instance that is retrieved from the HttpSession for each thread. This has implications if you wish to temporarily change the context under which a thread is running. If you just use SecurityContextHolder.getContext(), and call setAuthentication(anAuthentication) on the returned context object, then the Authentication object will change in all concurrent threads which share the same SecurityContext instance. You can customize the behaviour of SecurityContextPersistenceFilter to create a completely new SecurityContext for each request, preventing changes in one thread from affecting another. Alternatively you can create a new instance just at the point where you temporarily change the context. The method SecurityContextHolder.createEmptyContext() always returns a new context instance.



回答4:

The SecurityContextLogoutHandler invalidates the Servlet session, in the standard Servlet way, calling the invalidate method on the HttpSession object and also clearing the SecurityContext from Spring Security. SecurityContextLogoutHandler implements the LogoutHandler interface Traditionally in Java web applications, user session information is managed with the HttpSession object. In Spring Security(session clearing), at a low level, this is still the case , Spring security introduced new way of handling sessions or user session information. In an application using Spring Security, you will rarely access the Session object directly for retrieving user details. Instead, you will use SecurityContext (and its implementation class) and SecurityContextHolder (and its implementing classes). The SecurityContextHolder allows quick access to the SecurityContext, the SecurityContext allows quick access to the Authentication object, and the Authentication object allows quick access to the user details.

for example following programing illustrates accesing authentication object and displaying message

@Controller
@RequestMapping("/admin")
public class AdminController {

@RequestMapping(method = RequestMethod.POST, value = "/movies")
@ResponseBody
public String createMovie(@RequestBody String movie) {
System.out.println("Adding movie!! "+movie);
return "created";
}

@RequestMapping(method = RequestMethod.GET, value = "/movies")
@ResponseBody
public String createMovie() {
UserDetails user = (UserDetails)SecurityContextHolder.getContext().getAuthentication().
getPrincipal();
System.out.println("returned movie!");
return "User "+user.getUsername()+" is accessing movie x";
}
}

once authentication is done it creates a new session is created; Once session is created it contains information of user .on logout u need not only to invalidate session but also need to clear the session information by default in

  `isInvalidateHttpSession(`)  

checks whether session is valid or not if session is valid

setInvalidateHttpSession(boolean invalidateHttpSession)

is called . howver invalidateing session object invalidates but session object still contains information. to clear session information u need to call

   setClearAuthentication(boolean clearAuthentication)

method thus becomes thread safe if u dont it third method it information can be retrieved at low level