Spring security - learning notes - concurrency control of session management: the same account is only allowed to log in on one device

scene

Take over an old project:
Start with POM xml

		...slightly
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>


		<!-- security -->
		<!-- Spring Security -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		...slightly

demand

  1. The same account can only be logged in on one device
  2. The status of the last logged in device is automatically invalidated. (return to the login page after refreshing)

analysis

  1. After checking the data, it is known that the Session management module of spring security supports this function by default.
  2. However, the project customized the login filter, inherited AbstractAuthenticationProcessingFilter and replaced the default UsernamePasswordAuthenticationFilter.
  3. Session management is implemented through the injected sessionStrategy in AbstractAuthenticationProcessingFilter, but the default implementation is just a decoration, and we need to inject it ourselves.
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
	// Slightly
	private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
	// Slightly
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
		try {
			authResult = attemptAuthentication(request, response);
			if (authResult == null) {
				return;
			}
			// The session is handled here.
			sessionStrategy.onAuthentication(authResult, request, response);
		}
	}
	// Slightly
}

3.1. The concurrent sessioncontrolauthenticationstrategy of spring security can judge whether the Session is out of limit. But the pit is completely decoupled. It only cares about this core business. As for who will count the Session, there is no bird at all, so in sessionregistry Getallsessions () is always empty. Nature can't surpass.
3.2. The RegisterSessionAuthenticationStrategy of spring security is responsible for (directing) registration management.
3.3. The CompositeSessionAuthenticationStrategy provided with spring security is responsible for packaging and executing the above two.

  1. Therefore, we can directly inject the user-defined authentication filter into the compositesession authentication strategy.

Branch plot: (some people say that it needs to be rewritten, but I didn't rewrite it normally in my scene, so I skipped it. This paragraph is not verified, nor mentioned in the official documents) the session registration management is in the charge of SessionRegistry, and the default implementation is called SessionRegistryImpl.
UserDetails implements the class org. By default springframework. security. core. UserDetails. User rewrites toString, equals and hashcode.

  1. Add concurrentSessionFilter to kick out the previous login device. (the above work only marks the old session as expired, but does not do other processing. With this filter, as soon as the old device has a request, it determines that the session is expired and redirects directly to the login.)

to configure

web.xml

To use concurrent sessions, you need to register this listener. It is responsible for listening and publishing Session events

<listener>
	<listener-class>
	org.springframework.security.web.session.HttpSessionEventPublisher
	</listener-class>
</listener>

application-security.xml

For spring security configuration files, some items may be named differently. This section is basically moved on the official website. Adjust it slightly according to your own situation.

	<http auto-config="true" use-expressions="true">
		<form-login login-page="/login.jsp" />
		<logout logout-success-url="/logout" />
		<logout invalidate-session="true" logout-url="/logout" logout-success-url="/login.jsp" />
		<!-- a1. Custom authentication filter -->
		<custom-filter ref="myAuthenticationFilter" before="FORM_LOGIN_FILTER" />
		<custom-filter ref="springSecurityFilterPermission" before="FILTER_SECURITY_INTERCEPTOR" />
		<!-- b1. Processing filters that exceed concurrency -->
		<custom-filter ref="concurrentSessionFilter" position="CONCURRENT_SESSION_FILTER" />
		<csrf disabled="true" />
		<headers><frame-options policy="SAMEORIGIN" /></headers>
	</http>
	<!-- a2. Custom authentication filter -->
	<beans:bean id="myAuthenticationFilter"
		class="com.faith.framework.core.web.security.RequestHeaderProcessingFilter">
		<beans:property name="authenticationManager" ref="authenticationManager" />
		<beans:property name="logoutURL" value="/logout" />
		<beans:property name="loginFailureHandler" ref="loginFailureHandler" />
		<beans:property name="loginSuccessHandler" ref="loginSuccessHandler" />
		<beans:property name="loginPageURL" value="/login" />
		<beans:property name="loginFailPageURL" value="/logout" />
		<!-- a3. Session management goes from here -->
		<beans:property name="sessionAuthenticationStrategy" ref="sas"/>
	</beans:bean>

<!--Spring Security An account can only be logged in once at a time begin-->
	<!--a4. Responsible for a4.1,a4.2. Package execution. We will customize this bean Note to AbstractAuthenticationProcessingFilter.sessionStrategy-->
	<beans:bean id="sas"
				class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
		<beans:constructor-arg>
			<beans:list>
				<beans:ref bean="concurrentSessionControlAuthenticationStrategy"/>
				<beans:ref bean="registerSessionAuthenticationStrategy"/>
			</beans:list>
		</beans:constructor-arg>
	</beans:bean>
	<!-- a4.1. sessionRegistry obtain/Register session. It has one Map<String, SessionInformation> sessionIds To manage sessions. -->
	<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
	<!-- a4.2. It is responsible for judging whether the number of sessions of the current user exceeds the upper limit-->
	<beans:bean id="concurrentSessionControlAuthenticationStrategy" 
				class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
		<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
		<beans:property name="maximumSessions" value="1" /> <!-- You can only log in on at most one device -->
		<beans:property name="exceptionIfMaximumExceeded" value="false" /> <!-- When exceeding, kick the previous equipment offline -->
	</beans:bean>	
	<!-- Responsible for registration session-->
	<beans:bean id="registerSessionAuthenticationStrategy" 
				class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
		<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
	</beans:bean>
	
	<!-- b2. ConcurrentSessionFilter Kick more concurrent sessions offline -->
	<beans:bean id="concurrentSessionFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
		<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
		<beans:constructor-arg name="sessionInformationExpiredStrategy" ref="simpleRedirectSessionInformationExpiredStrategy" />
	</beans:bean>
	<beans:bean id="simpleRedirectSessionInformationExpiredStrategy" class="org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy">
		<beans:constructor-arg name="invalidSessionUrl" value="/login.jsp" />
	</beans:bean>
	<!--Spring Security An account can only be logged in once at a time end-->

Reference News

Official document: IV. Web Application Security 21 Session Management >21.3 Concurrency Control
Chinese document: Part IV. Web application security session management 21.3 concurrency control

Spring Security implements the configuration principle of prohibiting users from logging in repeatedly
[Spring Security] how to realize that multiple devices can only allow one account to log in at the same time (that is, the former login user is pushed off the line by the latter login user)? Just two simple steps!

Keywords: Java Session Spring Spring Security

Added by bob2006 on Tue, 18 Jan 2022 21:23:02 +0200