Spring Security Integration
Spring 会话提供与 Spring Security 的集成。
Spring Session provides integration with Spring Security.
Spring Security Remember-me Support
Spring Session 提供与 @docs-url/spring-security/site/docs/{spring-security-core-version}/reference/html5/#servlet-rememberme[Spring Security 的 Remember-me 身份验证] 的集成。支持:
Spring Session provides integration with {docs-url}/spring-security/site/docs/{spring-security-core-version}/reference/html5/#servlet-rememberme[Spring Security’s Remember-me Authentication]. The support:
-
更改会话过期时间长度。
-
Changes the session expiration length
-
确保会话 cookie 在 `Integer.MAX_VALUE`到期。cookie 过期时间设置为最大可能值,因为 cookie 仅在创建会话时设置。如果将其设置为与会话过期时间相同的值,则用户使用会话时将续订会话,但不会更新 cookie 过期时间(导致过期时间固定)。
-
Ensures that the session cookie expires at
Integer.MAX_VALUE
. The cookie expiration is set to the largest possible value, because the cookie is set only when the session is created. If it were set to the same value as the session expiration, the session would get renewed when the user used it but the cookie expiration would not be updated (causing the expiration to be fixed).
要通过 Java 配置将 Spring 会话与 Spring Security 配置在一起,可以使用以下列表作为指南:
To configure Spring Session with Spring Security in Java Configuration, you can use the following listing as a guide:
/*
* Copyright 2014-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.security;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
/**
* @author rwinch
*/
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableSpringHttpSession
public class RememberMeSecurityConfiguration {
// tag::http-rememberme[]
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ... additional configuration ...
.rememberMe((rememberMe) -> rememberMe
.rememberMeServices(rememberMeServices())
);
// end::http-rememberme[]
return http
.formLogin(Customizer.withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
).build();
}
// tag::rememberme-bean[]
@Bean
public SpringSessionRememberMeServices rememberMeServices() {
SpringSessionRememberMeServices rememberMeServices =
new SpringSessionRememberMeServices();
// optionally customize
rememberMeServices.setAlwaysRemember(true);
return rememberMeServices;
}
// end::rememberme-bean[]
@Bean
public InMemoryUserDetailsManager userDetailsService() {
return new InMemoryUserDetailsManager(
User.withUsername("user").password("{noop}password").roles("USER").build());
}
@Bean
MapSessionRepository sessionRepository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
}
// end::class[]
}
/*
* Copyright 2014-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.security;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
/**
* @author rwinch
*/
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableSpringHttpSession
public class RememberMeSecurityConfiguration {
// tag::http-rememberme[]
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ... additional configuration ...
.rememberMe((rememberMe) -> rememberMe
.rememberMeServices(rememberMeServices())
);
// end::http-rememberme[]
return http
.formLogin(Customizer.withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
).build();
}
// tag::rememberme-bean[]
@Bean
public SpringSessionRememberMeServices rememberMeServices() {
SpringSessionRememberMeServices rememberMeServices =
new SpringSessionRememberMeServices();
// optionally customize
rememberMeServices.setAlwaysRemember(true);
return rememberMeServices;
}
// end::rememberme-bean[]
@Bean
public InMemoryUserDetailsManager userDetailsService() {
return new InMemoryUserDetailsManager(
User.withUsername("user").password("{noop}password").roles("USER").build());
}
@Bean
MapSessionRepository sessionRepository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
}
// end::class[]
基于 XML 的配置看起来如下所示:
An XML-based configuration would look something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- tag::config[] -->
<security:http>
<!-- ... -->
<security:form-login />
<security:remember-me services-ref="rememberMeServices"/>
<security:intercept-url pattern="/**" access="permitAll()"/>
</security:http>
<bean id="rememberMeServices"
class="org.springframework.session.security.web.authentication.SpringSessionRememberMeServices"
p:alwaysRemember="true"/>
<!-- end::config[] -->
<security:user-service>
<security:user name="user" password="{noop}password" authorities="ROLE_USER"/>
</security:user-service>
<bean class="org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration"/>
<bean id="springSessionRepository" class="org.springframework.session.MapSessionRepository">
<constructor-arg>
<bean class="java.util.concurrent.ConcurrentHashMap"/>
</constructor-arg>
</bean>
</beans>
Spring Security Concurrent Session Control
Spring 会话提供与 Spring Security 的集成,以支持其并发会话控制。这允许限制单个用户可以同时拥有的活动会话数,但与默认 Spring Security 支持不同,这在集群环境中也能起作用。这是通过提供Spring Security 的 SessionRegistry
接口的自定义实现来完成的。
Spring Session provides integration with Spring Security to support its concurrent session control.
This allows limiting the number of active sessions that a single user can have concurrently, but, unlike the default
Spring Security support, this also works in a clustered environment. This is done by providing a custom
implementation of Spring Security’s SessionRegistry
interface.
使用 Spring Security 的 Java 配置 DSL 时,您可以通过 SessionManagementConfigurer
配置自定义 SessionRegistry
,如下面列表所示:
When using Spring Security’s Java config DSL, you can configure the custom SessionRegistry
through the
SessionManagementConfigurer
, as the following listing shows:
/*
* Copyright 2014-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docs.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
/**
* @author Joris Kuipers
*/
// tag::class[]
@Configuration
public class SecurityConfiguration<S extends Session> {
@Autowired
private FindByIndexNameSessionRepository<S> sessionRepository;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// other config goes here...
.sessionManagement((sessionManagement) -> sessionManagement
.maximumSessions(2)
.sessionRegistry(sessionRegistry())
)
.build();
}
@Bean
public SpringSessionBackedSessionRegistry<S> sessionRegistry() {
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
}
}
// end::class[]
这假设您还配置了 Spring 会话以提供 FindByIndexNameSessionRepository
,该会话返回 Session
实例。
This assumes that you have also configured Spring Session to provide a FindByIndexNameSessionRepository
that
returns Session
instances.
使用 XML 配置时,它看起来如下所示:
When using XML configuration, it would look something like the following listing:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
<!-- tag::config[] -->
<security:http>
<!-- other config goes here... -->
<security:session-management>
<security:concurrency-control max-sessions="2" session-registry-ref="sessionRegistry"/>
</security:session-management>
</security:http>
<bean id="sessionRegistry"
class="org.springframework.session.security.SpringSessionBackedSessionRegistry">
<constructor-arg ref="sessionRepository"/>
</bean>
<!-- end::config[] -->
</beans>
这假设您的 Spring 会话 SessionRegistry
bean 称为 sessionRegistry
,这是所有 SpringHttpSessionConfiguration
子类的名称。
This assumes that your Spring Session SessionRegistry
bean is called sessionRegistry
, which is the name used by all
SpringHttpSessionConfiguration
subclasses.
Limitations
Spring 会话对 Spring Security SessionRegistry
接口的实现不支持 getAllPrincipals
方法,因为无法使用 Spring 会话检索此信息。Spring Security 永远不会调用此方法,因此只会影响访问 SessionRegistry
本身的应用程序。
Spring Session’s implementation of Spring Security’s SessionRegistry
interface does not support the getAllPrincipals
method, as this information cannot be retrieved by using Spring Session. This method is never called by Spring Security,
so this affects only applications that access the SessionRegistry
themselves.