spring security(八) session 并发,剔除前一个用户

原创 2017年08月23日 17:28:38

解决 session 并发问题 ,同时只有一个用户在线。 有一个用户在线后其他的设备登录此用户将剔除前一个用户。强制前一个用户下线。

本文代码,是基于 springboot+security restful权限控制官方推荐(五)的代码

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);
            }
        });
    }

本文源码:https://github.com/527515025/springBoot

版权声明:本文为博主编写文章,未经博主允许转载,转载请注明出处。

相关文章推荐

SpringBoot整合SpringSecurity,SESSION 并发管理,同账号只允许登录一次

重写了UsernamePasswordAuthenticationFilter,里面继承AbstractAuthenticationProcessingFilter,这个类里面的session认证策略...
  • LWJdear
  • LWJdear
  • 2017年06月19日 16:52
  • 1512

Spring Security限制多个用户登录

在开发系统认证授权时,经常会碰到需要控制单个用户重复登录次数或者手动踢掉登录用户的需求。如果使用Spring Security 3.1.x该如何实现呢? Spring Security中可以...

关于使用SpringSecurity不能设置Session并发无效、剔除前一个用户无效的核心解决方案

那些年掉过SpringSecurity的坑。 最近在研究SpringBoot集成SpringSecurity权限框架的Demo,关于怎么集成,网上一大片的文章,我就不废话多说了,自行百度解决。 我...

springboot+security 动态权限修改session立即失效(六)

这个是springboot结合security 系列的第六篇博客了,不知不觉已经写了6篇了。中间有写的很不好,不尽人意。文章的排列也是我遇见问题,解决问题的过程,希望大家见谅。这不,最近出现了这个问题...

spring security(七) session 并发,一个用户在线后其他的设备登录此用户失败

这又是 一片 关于security 的文章,用于解决 session 并发问题 ,同时只有一个用户,在线。 有一个用户在线后其他的设备登录此用户失败。本文代码,是基于 springboot+secur...

Spring实战学习(四)

Spring security 1、spring security借助一系列的Servlet Filter来提供各种安全功能。DelegatingFilterProxy是一个特殊的ServletFil...

Spring Security 4.X xml配置 session 超时处理(http、ajax)

处理Spring Security 在session超时进行界面跳转

解决Spring Security 开启remember-me(持久化),session并发控制后重启服务器remember-me持久化凭证消失问题

学习Spring security过程中实现了remember-me的持久化实现(其实很简单的呀)

Spring Security 取Session中的值和修改userDetails

1.在session中取得spring security的登录用户名如下 ${session.SPRING_SECURITY_CONTEXT.authentication.principal.use...

Spring Security(13)——session管理

Spring Security通过http元素下的子元素session-management提供了对Http Session管理的支持。   1.1     检测session超时       ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:spring security(八) session 并发,剔除前一个用户
举报原因:
原因补充:

(最多只允许输入30个字)