spring security(八) session 併發,剔除前一個使用者
阿新 • • 發佈:2019-01-10
解決 session 併發問題 ,同時只有一個使用者線上。 有一個使用者線上後其他的裝置登入此使用者將剔除前一個使用者。強制前一個使用者下線。
1.修改security配置
新增 SessionRegistry,自己管理SessionRegistry。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserService customUserService;
@Autowired
SessionRegistry sessionRegistry;
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/users/**")
.authenticated()
.antMatchers(HttpMethod.POST)
.authenticated()
.antMatchers(HttpMethod.PUT)
.authenticated()
.antMatchers(HttpMethod.DELETE)
.authenticated()
.antMatchers("/**" )
.permitAll()
.and()
.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry);
http.httpBasic();
}
@Bean
public SessionRegistry getSessionRegistry(){
SessionRegistry sessionRegistry=new SessionRegistryImpl();
return sessionRegistry;
}
2.LoginController
修改登入,登入成功後清除前一個線上使用者(dropPreviousUser 清除前一個使用者)
@RequestMapping(value = "/login")
@ResponseBody
//使用者名稱密碼是用base64 加密 原文為 admin:admin 即 使用者名稱:密碼 內容是放在request.getHeader 的 "authorization" 中
public Object login(@AuthenticationPrincipal SysUser loginedUser, @RequestParam(name = "logout", required = false) String logout,HttpServletRequest request) {
if (logout != null) {
return "logout";
}
if (loginedUser != null) {
SessionUtil.dropPreviousUser(request,sessionRegistry,loginedUser);
return loginedUser;
}
return null;
}
3.SessionUtil
session 管理工具類
/**
* Created by yangyibo on 8/23/17.
*/
public class SessionUtil {
/**
* 辨別使用者是否已經登入
*
* @param request
* @param sessionRegistry
* @param loginedUser
*/
public static void deleteSameUser(HttpServletRequest request, SessionRegistry sessionRegistry, User loginedUser) {
SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
List<SessionInformation> sessionsInfo;
sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), true);
String currentSessionId;
if (null != sessionsInfo && sessionsInfo.size() == 0) {
sessionRegistry.registerNewSession(request.getSession().getId(), sc.getAuthentication().getPrincipal());
sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
}
currentSessionId = sessionsInfo.get(0).getSessionId();
List<Object> o = sessionRegistry.getAllPrincipals();
for (Object principal : o) {
if (principal instanceof User && (loginedUser.getUsername().equals(((User) principal).getUsername()))) {
List<SessionInformation> oldSessionsInfo = sessionRegistry.getAllSessions(principal, false);
if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) {
for (SessionInformation sessionInformation : sessionsInfo) {
//當前session失效
sessionInformation.expireNow();
sc.setAuthentication(null);
sessionRegistry.removeSessionInformation(currentSessionId);
throw new GeneralServerExistException(ErrorMessage.ALONG_LOGIN_ERROTR.toString());
}
}
}
}
}
/**
* 剔除前一個使用者
*
* @param request
* @param sessionRegistry
* @param loginedUser
*/
public static void dropPreviousUser(HttpServletRequest request, SessionRegistry sessionRegistry, User loginedUser, SysMessageService sysMessageService) {
SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
List<SessionInformation> sessionsInfo;
sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
if (sessionsInfo.size() > 0) {
String currentSessionId = sessionsInfo.get(0).getSessionId();
List<Object> o = sessionRegistry.getAllPrincipals();
for (Object principal : o) {
if (principal instanceof User && (loginedUser.getUsername().equals(((User) principal).getUsername()))) {
List<SessionInformation> oldSessionsInfo = sessionRegistry.getAllSessions(principal, false);
if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) {
for (SessionInformation sessionInformation : oldSessionsInfo) {
//當前session失效
//send message
sysMessageService.sendMessage(((User) principal).getUsername(), new SysMessage(null, Consts.NOTIFICATION_TYPE_HADLOGIN_CONTENT, 5, Consts.NOTIFICATION_ACCEPT_TYPE_HADLOGIN));
sessionInformation.expireNow();
}
}
}
}
}else {
throw new GeneralServerExistException(ErrorMessage.ALONG_LOGIN_ERROTR.toString());
}
}
/**
* session 失效
*
* @param request
* @param sessionRegistry
*/
public static void expireSession(HttpServletRequest request, User user, SessionRegistry sessionRegistry) {
List<SessionInformation> sessionsInfo = null;
if (null != user) {
List<Object> o = sessionRegistry.getAllPrincipals();
for (Object principal : o) {
if (principal instanceof User && (user.getUsername().equals(((User) principal).getUsername()))) {
sessionsInfo = sessionRegistry.getAllSessions(principal, false);
}
}
} else if (null != request) {
SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
if (null != sc.getAuthentication().getPrincipal()) {
sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
sc.setAuthentication(null);
}
}
if (null != sessionsInfo && sessionsInfo.size() > 0) {
for (SessionInformation sessionInformation : sessionsInfo) {
//當前session失效
sessionInformation.expireNow();
sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
}
}
}
}
4.session 失效
制定定時任務,定時使session 失效。失效機制,定時掃描線上使用者,判斷線上使用者的最後一次操作時間AccessLastTime 於當前的時間差是否超過 session失效時間 ,如果超過session 失效時間,將session 置為失效。
此處的session失效和 springboot 配置檔案裡配置的session 失效時間應當一致。(springboot 配置檔案裡配置的session 失效實際上並不能將我門自己管理的SessionRegistry 中的session失效。)
/**
* 超時事件檢查
*/
@Scheduled(cron = "0 0/1 * * * ?")
public void ScanUserOnline() {
//獲取所有線上使用者
List<User> users = userDao.getUsersWithOnLine(4);
users.stream().parallel().forEach(user -> {
//通過時間判斷是否session過期
if (CommonUtil.CalculationData(user.getAccessLastTime(),30)) {
//如果過期則設定使用者為下下線狀態
updateOnline(user.getId(),4,null);
//session 置為失效 SessionUtil.expireSession(null,user,sessionRegistry);
}
});
}