适用于session方式的登录,不适用于jwt token方式的登录。因为jwt token方式的话session就没什么事儿了 (此处为了防止小白不懂原理盲目抄)。

有这样一个需求,一个账号只能在一个客户端浏览器上登录,如果同样的账号在别的客户端浏览器上登录的话,之前的登录需要被踢下去。这个需求看似很简单,而且默认情况下使用Spring Security配置也挺简单,如下配置即可:

@Override
protected void configure(HttpSecurity http) throws Exception {
http
... //此处忽略若干行配置
.sessionManagement()
.maximumSessions(1);
}

这个配置在默认formLogin状态下生效,但如果使用自定义过滤器接管登录过程的话就会失效,如这样:

@Override
protected void configure(HttpSecurity http) throws Exception {
http
... //此处忽略若干行配置
    // 添加自己的登录过滤器
    .addFilterBefore(adminLoginFilter(), UsernamePasswordAuthenticationFilter.class)
    // 因为adminLoginFilter接管了默认formLogin的工作,所以下面的session配置失效了
.sessionManagement()
.maximumSessions(1);
}

这时我们需要手动配置session了,先看看官网是怎么说的 官网说明 这是官网的一段话

If you are using a customized authentication filter for form-based login, then you have to configure concurrent session control support explicitly.

官网也提供了具体配置方法

<http>
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>
<beans:bean id="redirectSessionInformationExpiredStrategy"
class="org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy">
<beans:constructor-arg name="invalidSessionUrl" value="/session-expired.htm" />
</beans:bean>
<beans:bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:constructor-arg name="sessionInformationExpiredStrategy" ref="redirectSessionInformationExpiredStrategy" />
</beans:bean>
<beans:bean id="myAuthFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<beans:constructor-arg>
<beans:list>
<beans:bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<beans:constructor-arg ref="sessionRegistry"/>
<beans:property name="maximumSessions" value="1" />
<beans:property name="exceptionIfMaximumExceeded" value="true" />
</beans:bean>
<beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
</beans:bean>
<beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<beans:constructor-arg ref="sessionRegistry"/>
</beans:bean>
</beans:list>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />

官网提供的是xml配置方法,现在应该是比较少用了,所以我们得在自己项目中使用代码配置,具体流程:

第一步 定义一个sessionRegistry bean

@Bean
public SessionRegistry sessionRegistry(){
return new SessionRegistryImpl();
}

第二步 定义几个Session Strategy bean,其中在ConcurrentSessionControlAuthenticationStrategy配置账号的最大同时登录数

@Bean
public ConcurrentSessionControlAuthenticationStrategy controlAuthenticationStrategy(SessionRegistry sessionRegistry){
ConcurrentSessionControlAuthenticationStrategy strategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
strategy.setMaximumSessions(1); // 这里配置一个账号最大登录数
return strategy;
}
@Bean
public SessionFixationProtectionStrategy sessionFixationProtectionStrategy(){
return new SessionFixationProtectionStrategy();
}
@Bean
public RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy(SessionRegistry sessionRegistry){
return new RegisterSessionAuthenticationStrategy(sessionRegistry);
}

第三步 定义CompositeSessionAuthenticationStrategy bean,他接受的是List集合,也就是其他Session Strategy的集合

@Bean
public CompositeSessionAuthenticationStrategy sessionAuthenticationStrategy(List<SessionAuthenticationStrategy> authenticationStrategies){
return new CompositeSessionAuthenticationStrategy(authenticationStrategies);
}

第四步 定义ConcurrentSessionFilter bean

@Bean
public ConcurrentSessionFilter concurrentSessionFilter(SessionRegistry sessionRegistry){
return new ConcurrentSessionFilter(sessionRegistry);
}

第五步 在自己的自定义登录filter里面设置session strategy

public AdminLoginFilter adminLoginFilter(CompositeSessionAuthenticationStrategy strategy) throws Exception {
AdminLoginFilter adminLoginFilter = new AdminLoginFilter();
... 省略若干行配置
adminLoginFilter.setSessionAuthenticationStrategy(strategy);
return adminLoginFilter;
}

第六步 在configure(HttpSecurity http)里面配置session

@Override
protected void configure(HttpSecurity http) throws Exception {
List<SessionAuthenticationStrategy> authenticationStrategies = Arrays.asList(
controlAuthenticationStrategy(sessionRegistry()),
sessionFixationProtectionStrategy(),
registerSessionAuthenticationStrategy(sessionRegistry())
);
http
... //此处忽略若干行配置
.addFilterBefore(concurrentSessionFilter(sessionRegistry()),ConcurrentSessionFilter.class)
.addFilterBefore(adminLoginFilter(sessionAuthenticationStrategy(authenticationStrategies)), UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy(authenticationStrategies));
}

下面看完整的代码

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class PortalSecurityConfig2 extends WebSecurityConfigurerAdapter {
@Autowired
private AdminLoginService adminDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public AdminLoginFilter adminLoginFilter(CompositeSessionAuthenticationStrategy strategy) throws Exception {
AdminLoginFilter adminLoginFilter = new AdminLoginFilter();
adminLoginFilter.setFilterProcessesUrl("/admin/login");
adminLoginFilter.setAuthenticationManager(authenticationManagerBean());
adminLoginFilter.setSessionAuthenticationStrategy(strategy);
return adminLoginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
List<SessionAuthenticationStrategy> authenticationStrategies = Arrays.asList(
controlAuthenticationStrategy(sessionRegistry()),
sessionFixationProtectionStrategy(),
registerSessionAuthenticationStrategy(sessionRegistry())
);
http
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilterBefore(concurrentSessionFilter(sessionRegistry()), ConcurrentSessionFilter.class)
.addFilterBefore(adminLoginFilter(sessionAuthenticationStrategy(authenticationStrategies)), UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy(authenticationStrategies));
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(adminDetailsService);
}
@Bean
public SessionRegistry sessionRegistry(){
return new SessionRegistryImpl();
}
@Bean
public ConcurrentSessionFilter concurrentSessionFilter(SessionRegistry sessionRegistry){
return new ConcurrentSessionFilter(sessionRegistry);
}
@Bean
public ConcurrentSessionControlAuthenticationStrategy controlAuthenticationStrategy(SessionRegistry sessionRegistry){
ConcurrentSessionControlAuthenticationStrategy strategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
strategy.setMaximumSessions(1);
return strategy;
}
@Bean
public SessionFixationProtectionStrategy sessionFixationProtectionStrategy(){
return new SessionFixationProtectionStrategy();
}
@Bean
public RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy(SessionRegistry sessionRegistry){
return new RegisterSessionAuthenticationStrategy(sessionRegistry);
}
@Bean
public CompositeSessionAuthenticationStrategy sessionAuthenticationStrategy(List<SessionAuthenticationStrategy> authenticationStrategies){
return new CompositeSessionAuthenticationStrategy(authenticationStrategies);
}
}

希望以上内容能帮到你。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。