一、配置Session和自定义登出
package com.sf.browser;
import com.sf.config.MySessionExpiredStrategy;
import com.sf.filter.ValidateCodeFilter;
import com.sf.handler.MyAuthenticationFailureHandler;
import com.sf.handler.MyAuthenticationSucessHandler;
import com.sf.handler.MyLogOutSuccessHandler;
import com.sf.service.impl.UserDetailService;
import com.sf.smsvalidate.SmsAuthenticationConfig;
import com.sf.smsvalidate.SmsCodeFilter;
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationSucessHandler authenticationSucessHandler;
@Autowired
private MyAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private ValidateCodeFilter validateCodeFilter;
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailService userDetailService;
@Autowired
private SmsCodeFilter smsCodeFilter;
@Autowired
private SmsAuthenticationConfig smsAuthenticationConfig;
@Autowired
private MySessionExpiredStrategy mySessionExpiredStrategy;
@Autowired
private MyLogOutSuccessHandler logOutSuccessHandler;
@Autowired
SessionRegistry sessionRegistry;
/**
* .loginPage("/login.html")指定了跳转到登录页面的请求URL,
* .loginProcessingUrl("/login")对应登录页面form表单的action="/login",
* .antMatchers("/login.html").permitAll()表示跳转到登录页面的请求不被拦截
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器
.formLogin() // 表单登录
// http.httpBasic() // HTTP Basic
.loginPage("/authentication/require") // 登录跳转 URL, 进行验证或者登陆页 跳转
.loginProcessingUrl("/login") // 处理表单登录 URL
.successHandler(authenticationSucessHandler) // 处理登录成功
.failureHandler(authenticationFailureHandler) // 处理登录失败
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository()) // 配置 token 持久化仓库
.tokenValiditySeconds(3600) // remember 过期时间,单为秒
.userDetailsService(userDetailService) // 处理自动登录逻辑
.and()
.authorizeRequests() // 授权配置
.antMatchers("/authentication/require", "/login.html", "/code/image",
"/code/sms", "/session/invalid", "/css/*", "/js/*", "/login1.html", "/statics/**", "/signout/success").permitAll() // 登录跳转 URL 无需认证 (含登录页的静态资源)
.anyRequest() // 所有请求
.authenticated() // 都需要认证
.and()
.sessionManagement() // 添加 Session管理器
.invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
.maximumSessions(1) //最大session并发数量,超过定义数量前一个session就会失效
.maxSessionsPreventsLogin(true) //Session达到最大有效数的时候,不再允许相同的账户登录。
.expiredSessionStrategy(mySessionExpiredStrategy)//配置了Session在并发下失效后的处理策略;
.sessionRegistry(sessionRegistry)//添加 session 注册
.and()
.and()
.logout()//自定义退出登录
.logoutUrl("/signout")//退出登录的URL
.invalidateHttpSession(true)
//.logoutSuccessUrl("/signout/success")//退出成功后跳转的URL
.deleteCookies("JSESSIONID")//退出成功后删除名称为JSESSIONID的cookie, 删除不成功???
.logoutSuccessHandler(logOutSuccessHandler) //logoutSuccessHandler指定退出成功处理器来处理退出成功后的逻辑:
.and()
.csrf().disable()// 关闭 CSRF攻击防御关了
.apply(smsAuthenticationConfig);// 将短信验证码认证配置加到 Spring Security 中
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置token持久化对象
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();//默认表
jdbcTokenRepository.setDataSource(dataSource);
jdbcTokenRepository.setCreateTableOnStartup(false);
return jdbcTokenRepository;
}
/**
* 用户登录以后security 会把生成的session 放到 SessionRegistry 里面。那么我门想让session 失效只用找到对应的session,然后将它从SessionRegistry 中剔除出去就好了。
* 但是查看了源码发现SessionRegistry 是 final 。。。。。这个可怎么办呢?
* 自己new 一个对象吧,所以我就new SessionRegistry(),并用我自己的SessionRegistry管理session 咯
*/
@Bean
public SessionRegistry getSessionRegistry(){
SessionRegistry sessionRegistry=new SessionRegistryImpl();
return sessionRegistry;
}
}
二、自定义登出SuccessHandler,在里面出来登录用户的session,不然虽然登出但是session还有效,下次登录会累加session次数,超出上面配置的maximumSessions,会提示 "Maximum sessions of {n} for this principal exceeded"
package com.sf.handler;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Component
public class MyLogOutSuccessHandler implements LogoutSuccessHandler {
@Autowired
private SessionRegistry sessionRegistry;
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
System.out.println(authentication);
System.out.println(authentication.getName());
List<Object> o= sessionRegistry.getAllPrincipals();
//退出成功后删除当前用户session
for (Object principal : o) {
if (principal instanceof User) {
final User loggedUser = (User) principal;
if (authentication.getName().equals(loggedUser.getUsername())) {
List<SessionInformation> sessionsInfo = sessionRegistry.getAllSessions(principal, false);
if (null != sessionsInfo && sessionsInfo.size() > 0) {
for (SessionInformation sessionInformation : sessionsInfo) {
sessionInformation.expireNow();
}
}
}
}
}
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write("退出成功,请重新登录");
}
}