博客信息

在线用户踢下线方案

发布时间:『 2019-06-21 23:32』  博客类别:解决方案  阅读(1639)

前言

每个系统对应很多的用户,通常我们会遇到此类需求,某系统的用户已经离职;那么我们需要冻结该用户在系统的使用权;

普遍的做法是将该用户修改为离职状态,不让其进行再次登录;

但是这样做存在一个问题,若是该用户正在使用公司系统,修改用户的在职与否的状态,并不能将其强制踢下线,该用户仍然可以使用该公司的系统(在不退出登录的情况下)

 

而本篇博客的博客的内容,主要就是针对这一问题进行处理;当然,处理的方式有很多种,这只是其中的一种;

涉及到技术shiro的会话管理、redis的缓存存储、springboot等等

 

实现思路:

1、用户登录后会将用户信息存储到redis缓存中,一个sessionid对应着一个用户;也就是说redis中存放着所有在线用户的数据;

2、通过redis键的生成规则,可以获取到每一个在线用户在redis内存中所对应的具体键名称;

3、通过redis中具体的键,可以获取到每一个session用户,然后返回session集合,最终经过处理用于前台展示;

4、清理在线用户的原理,其实也就是将在线用户的sessionid传入后台,让shiro自身的会话管理模块,在内存中获取该会话,设置其0秒后失效,以达到清理在线会话的效果;

 

效果图


谷歌浏览器用户admin登录


小李飞刀_解决方案


360浏览器用户xiaoli登录


小李飞刀_解决方案


小李飞刀_解决方案


强制踢下线


小李飞刀_解决方案


当再次操作谷歌浏览器admin用户所属菜单时,就会跳转登录页面了;也就是说admin用户已经被xiaoli用户踢下线了;

 

核心代码

在线用户列表

获取每一个在线用户在redis中的key,然后通过一个个key,获取到对应的在线用户对应的shiro管理的会话

 

RedisSessionDAO代码

private String keyPrefix = "shiro_redis_session:";

@Override
public Collection<Session> getActiveSessions() {
    Set<Session> sessions = new HashSet<Session>();

    Set<byte[]> keys = redisManager.keys(this.keyPrefix + "*");
    if(keys != null && keys.size()>0){
        for(byte[] key:keys){
            Session s = (Session)SerializeUtils.deserialize(redisManager.get(key));
            sessions.add(s);
        }
    }

    return sessions;
}


SessionServiceImpl代码

@Override
public List<UserOnline> list() {
    List<UserOnline> list = new ArrayList<>();
    Collection<Session> sessions = sessionDAO.getActiveSessions();
    for (Session session : sessions) {
        UserOnline userOnline = new UserOnline();
        if (session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null) {
            continue;
        } else {
            SimplePrincipalCollection principalCollection = (SimplePrincipalCollection) session
                    .getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            UserDO userDO = (UserDO) principalCollection.getPrimaryPrincipal();
            userOnline.setUsername(userDO.getUsername());
        }
        userOnline.setId((String) session.getId());
        userOnline.setHost(session.getHost());
        userOnline.setStartTimestamp(session.getStartTimestamp());
        userOnline.setLastAccessTime(session.getLastAccessTime());
        userOnline.setTimeout(session.getTimeout());
        list.add(userOnline);
    }
    return list;
}


SessionController代码

@ResponseBody
@RequestMapping("/list")
public List<UserOnline> list() {
   return sessionService.list();
}


返回到前端的数据

[{
	"id": "dce245fb-d0f4-4086-9145-eb82b2a1935d",
	"userId": null,
	"username": "admin",
	"host": "0:0:0:0:0:0:0:1",
	"systemHost": null,
	"userAgent": null,
	"status": "on_line",
	"startTimestamp": "2019-06-22 10:25:39",
	"lastAccessTime": "2019-06-22 10:26:09",
	"timeout": 0,
	"onlineSession": null
}, {
	"id": "901d15c3-db3f-4332-980d-57ddc1248f9d",
	"userId": null,
	"username": "xiaoli",
	"host": "0:0:0:0:0:0:0:1",
	"systemHost": null,
	"userAgent": null,
	"status": "on_line",
	"startTimestamp": "2019-06-22 10:15:27",
	"lastAccessTime": "2019-06-22 10:26:27",
	"timeout": 1800000,
	"onlineSession": null
}]


清理在线用户

shiro中的SessionDAO类中具备通过sessionId寻找对应在线用户的方法;我们可以将对应的会话session直接设置为0秒后超时,以达到清理会话的效果;


SessionServiceImpl

@Override
public boolean forceLogout(String sessionId) {
    Session session = sessionDAO.readSession(sessionId);
    session.setTimeout(0);
    return true;
}


SessionController

@ResponseBody
@RequestMapping("/forceLogout/{sessionId}")
public R forceLogout(@PathVariable("sessionId") String sessionId, RedirectAttributes redirectAttributes) {
   try {
      sessionService.forceLogout(sessionId);
      return R.ok();
   } catch (Exception e) {
      e.printStackTrace();
      return R.error();
   }

}


R类

package com.javaxl.bootdo.common.utils;

import java.util.HashMap;
import java.util.Map;

public class R extends HashMap<String, Object> {
   private static final long serialVersionUID = 1L;

   public R() {
      put("code", 0);
      put("msg", "操作成功");
   }

   public static R error() {
      return error(1, "操作失败");
   }

   public static R error(String msg) {
      return error(500, msg);
   }

   public static R error(int code, String msg) {
      R r = new R();
      r.put("code", code);
      r.put("msg", msg);
      return r;
   }

   public static R ok(String msg) {
      R r = new R();
      r.put("msg", msg);
      return r;
   }

   public static R ok(Map<String, Object> map) {
      R r = new R();
      r.putAll(map);
      return r;
   }

   public static R ok() {
      return new R();
   }

   @Override
   public R put(String key, Object value) {
      super.put(key, value);
      return this;
   }
}


前端代码


online.html 

online.js 


Over......





关键字:     解决方案       shiro       redis  

备案号:湘ICP备19000029号

Copyright © 2018-2019 javaxl晓码阁 版权所有