每个系统对应很多的用户,通常我们会遇到此类需求,某系统的用户已经离职;那么我们需要冻结该用户在系统的使用权;
普遍的做法是将该用户修改为离职状态,不让其进行再次登录;
但是这样做存在一个问题,若是该用户正在使用公司系统,修改用户的在职与否的状态,并不能将其强制踢下线,该用户仍然可以使用该公司的系统(在不退出登录的情况下)
而本篇博客的博客的内容,主要就是针对这一问题进行处理;当然,处理的方式有很多种,这只是其中的一种;
涉及到技术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; } }
前端代码
Over......
备案号:湘ICP备19000029号
Copyright © 2018-2019 javaxl晓码阁 版权所有