文章目录

  • redis工具类
  • 用户实体类
  • token配置
  • service层保存和查询在线用户
  • 工具类 获得用户浏览器等其他信息
  • controller层

redis工具类

import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisConnectionUtils;
@Component
@SuppressWarnings({"unchecked", "all"})
public class RedisUtils {private RedisTemplate<Object, Object> redisTemplate;@Value("${jwt.online-key}")private String onlineKey;public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 根据 key 获取过期时间* @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(Object key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 查找匹配key* @param pattern key* @return /*/public List<String> scan(String pattern) {ScanOptions options = ScanOptions.scanOptions().match(pattern).build();RedisConnectionFactory factory = redisTemplate.getConnectionFactory();RedisConnection rc = Objects.requireNonNull(factory).getConnection();Cursor<byte[]> cursor = rc.scan(options);List<String> result = new ArrayList<>();while (cursor.hasNext()) {result.add(new String(cursor.next()));}try {RedisConnectionUtils.releaseConnection(rc, factory);} catch (Exception e) {e.printStackTrace();}return result;}/*** 分页查询 key* @param patternKey key* @param page 页码* @param size 每页数目* @return /*/public List<String> findKeysForPage(String patternKey, int page, int size) {ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();RedisConnectionFactory factory = redisTemplate.getConnectionFactory();RedisConnection rc = Objects.requireNonNull(factory).getConnection();Cursor<byte[]> cursor = rc.scan(options);List<String> result = new ArrayList<>(size);int tmpIndex = 0;int fromIndex = page * size;int toIndex = page * size + size;while (cursor.hasNext()) {if (tmpIndex >= fromIndex && tmpIndex < toIndex) {result.add(new String(cursor.next()));tmpIndex++;continue;}// 获取到满足条件的数据后,就可以退出了if (tmpIndex >= toIndex) {break;}tmpIndex++;cursor.next();}try {RedisConnectionUtils.releaseConnection(rc, factory);} catch (Exception e) {e.printStackTrace();}return result;}/*** 判断key是否存在* @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存* @param key 可以传一个值 或多个*/public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(key));}}}/*** 普通缓存获取* @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 批量获取* @param keys* @return*/public List<Object> multiGet(List<String> keys) {Object obj = redisTemplate.opsForValue().multiGet(Collections.singleton(keys));return null;}/*** 普通缓存放入* @param key   键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间* @param key   键* @param value 值* @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间* @param key   键* @param value 值* @param time  时间* @param timeUnit 类型* @return true成功 false 失败*/public boolean set(String key, Object value, long time, TimeUnit timeUnit) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, timeUnit);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}//省略其他操作map的方法
}

用户实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OnlineUser {private String userName;private String nickName;private String job;private String browser;private String ip;private String address;private String key;private Date loginTime;}

token配置

securityproerties:

@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "jwt")
public class SecurityProperties {/** Request Headers : Authorization */private String header;/** 令牌前缀,最后留个空格 Bearer */private String tokenStartWith;/** 必须使用最少88位的Base64对该令牌进行编码 */private String base64Secret;private String secret;/** 令牌过期时间 此处单位/毫秒 */private Long tokenValidityInSeconds;/** 在线用户 key,根据 key 查询 redis 中在线用户的数据 */private String onlineKey;/** 验证码 key */private String codeKey;public String getTokenStartWith() {return tokenStartWith + " ";}
}

其中Yml配置:


#jwt
jwt:header: Authorization# 令牌前缀token-start-with: Bearersecret: k09BQnaF# 必须使用最少88位的Base64对该令牌进行编码base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=# 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.htmltoken-validity-in-seconds: 14400000# 在线用户keyonline-key: online-token# 验证码code-key: code-key

service层保存和查询在线用户

在线用户层

@Service
@Slf4j
public class OnlineUserService {private final SecurityProperties properties;//这个特性类的属性在上面private RedisUtils redisUtils;public OnlineUserService(SecurityProperties properties, RedisUtils redisUtils) {this.properties = properties;this.redisUtils = redisUtils;}/*** 保存在线用户信息* @param jwtUser /* @param token /* @param request /*/public void save(JwtUser jwtUser, String token, HttpServletRequest request) {String job = jwtUser.getDept() + "/" + jwtUser.getJob();String ip = StringUtils.getIp(request);String browser = StringUtils.getBrowser(request);String address = StringUtils.getCityInfo(ip);OnlineUser onlineUser = null;try {onlineUser = new OnlineUser(jwtUser.getUsername(), jwtUser.getNickName(), job, browser, ip, address, EncryptUtils.desEncrypt(token), new Date());} catch (Exception e) {e.printStackTrace();}redisUtils.set(properties.getOnlineKey() + token, onlineUser, properties.getTokenValidityInSeconds() / 1000);}/*** 查询全部数据 分页* @param filter /* @param pageable /* @return /*/public Map<String, Object> getAll(String filter, int type, Pageable pageable) {List<OnlineUser> onlineUsers = getAll(filter, type);return PageUtil.toPage(PageUtil.toPage(pageable.getPageNumber(), pageable.getPageSize(), onlineUsers),onlineUsers.size());}/*** 查询全部数据,不分页* @param filter /* @return /*/public List<OnlineUser> getAll(String filter, int type) {List<String> keys = null;if (type == 1) {keys = redisUtils.scan("m-online-token*");} else {keys = redisUtils.scan(properties.getOnlineKey() + "*");}Collections.reverse(keys);List<OnlineUser> onlineUsers = new ArrayList<>();for (String key : keys) {OnlineUser onlineUser = (OnlineUser) redisUtils.get(key);if (StringUtils.isNotBlank(filter)) {if (onlineUser.toString().contains(filter)) {onlineUsers.add(onlineUser);}} else {onlineUsers.add(onlineUser);}}onlineUsers.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()));return onlineUsers;}/*** 踢出用户* @param key /* @throws Exception /*/public void kickOut(String key) throws Exception {key = properties.getOnlineKey() + EncryptUtils.desDecrypt(key);redisUtils.del(key);}/*** 踢出移动端用户* @param key /* @throws Exception /*/public void kickOutT(String key) throws Exception {String keyt = "m-online-token" + EncryptUtils.desDecrypt(key);redisUtils.del(keyt);}/*** 退出登录* @param token /*/public void logout(String token) {String key = properties.getOnlineKey() + token;redisUtils.del(key);}/*** 检测用户是否在之前已经登录,已经登录踢下线* @param userName 用户名*/public void checkLoginOnUser(String userName, String igoreToken) {List<OnlineUser> onlineUsers = getAll(userName, 0);if (onlineUsers == null || onlineUsers.isEmpty()) {return;}for (OnlineUser onlineUser : onlineUsers) {if (onlineUser.getUserName().equals(userName)) {try {String token = EncryptUtils.desDecrypt(onlineUser.getKey());if (StringUtils.isNotBlank(igoreToken) && !igoreToken.equals(token)) {this.kickOut(onlineUser.getKey());} else if (StringUtils.isBlank(igoreToken)) {this.kickOut(onlineUser.getKey());}} catch (Exception e) {log.error("checkUser is error", e);}}}}}

工具类 获得用户浏览器等其他信息

上面的StringUtils工具类有一些获取信息的方法:

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
public class StringUtils extends org.apache.commons.lang3.StringUtils {/*** 获取ip地址*/public static String getIp(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}String comma = ",";String localhost = "127.0.0.1";if (ip.contains(comma)) {ip = ip.split(",")[0];}if (localhost.equals(ip)) {// 获取本机真正的ip地址try {ip = InetAddress.getLocalHost().getHostAddress();} catch (UnknownHostException e) {e.printStackTrace();}}return ip;}/*** 根据ip获取详细地址*/public static String getCityInfo(String ip) {String api = String.format(YshopConstant.Url.IP_URL, ip);JSONObject object = JSONUtil.parseObj(HttpUtil.get(api));return object.get("addr", String.class);}//获取用户浏览器名字public static String getBrowser(HttpServletRequest request) {UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));Browser browser = userAgent.getBrowser();return browser.getName();}/*** 获得当天是周几*/public static String getWeekDay() {String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};Calendar cal = Calendar.getInstance();cal.setTime(new Date());int w = cal.get(Calendar.DAY_OF_WEEK) - 1;if (w < 0) {w = 0;}return weekDays[w];}
}

controller层

@RestController
@RequestMapping("/auth/online")
@Api(tags = "系统:在线用户管理")
public class OnlineController {private final OnlineUserService onlineUserService;public OnlineController(OnlineUserService onlineUserService) {this.onlineUserService = onlineUserService;}@ApiOperation("查询在线用户")@GetMapping@PreAuthorize("@el.check()")public ResponseEntity<Object> getAll(@RequestParam(value = "filter", defaultValue = "") String filter,@RequestParam(value = "type", defaultValue = "0") int type,Pageable pageable) {return new ResponseEntity<>(onlineUserService.getAll(filter, type, pageable), HttpStatus.OK);}@ApiOperation("踢出用户")@DeleteMapping@PreAuthorize("@el.check()")public ResponseEntity<Object> delete(@RequestBody Set<String> keys) throws Exception {for (String key : keys) {onlineUserService.kickOut(key);}return new ResponseEntity<>(HttpStatus.OK);}@ForbidSubmit@ApiOperation("踢出移动端用户")@PostMapping("/delete")@PreAuthorize("@el.check()")public ResponseEntity<Object> deletet(@RequestBody Set<String> keys) throws Exception {for (String key : keys) {onlineUserService.kickOutT(key);}return new ResponseEntity<>(HttpStatus.OK);}

用redis+jwt保存在线用户和获得在线用户列表、踢出用户示例相关推荐

  1. TCP多人聊天程序Java实现(群聊,私聊,在线用户,踢出用户)

    本程序在程序 https://blog.csdn.net/joffy/article/details/18079331 的基础是上添加了私聊,踢出用户两个功能. 由客户端和服务器端构成程序,程序借助J ...

  2. spring boot + shiro 实现登陆 踢出用户功能 (挤人) 以及UnknownSessionException异常问题 记住我功能

    简介:踢出用户功能:就是限制一个账号登陆人数. 本文限定一个账号一个用户登陆,并且是挤掉前一个用户 目录 首先 pom 然后Shiro配置Bean  ShiroConfigBean 然后配置 Shir ...

  3. linux强制踢出用户,Linux下管理员强行踢出用户的命令使用方法

    Linux强制踢出用户命令: 一.输入w命令查看已登录用户信息 [root@KW_S01_192.168.1.106_A ~]# w 19:22:31 up  2:11,  3 users,  loa ...

  4. shiro 删除用户session_我的shiro之旅: 十二 shiro 踢出用户(同一用户只能一处登录)...

    看了一下官网,没有找到关于如何控制同一用户只能一处登录的介绍,网上也没有找到相关的文章.可能有些人会记录用户的登录信息,然后达到踢出用户的效果.这里介绍一个更简单的方法. 如果我们跟shiro的源码, ...

  5. JavaWeb-网站在线用户信息、网站访问量以及管理踢出用户实例

    转载请注明出处: http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] 这个稍微比上个版本做得完善了一点,用了JavaBean来封装用户信息,添加了一个管 ...

  6. linux 踢出其他用户,Linux查看当前登录用户并踢出用户

    1.查看当前登录用户 [root@94ip ~]$ whatis w w     (1) - Show who is logged on and what they are doing [root@9 ...

  7. java 登录踢出_spring security 4 如何踢出用户?

    项目用的是spring boot 1.3 全javaconfig 配置 目的 : 我要踢出一个登录在线的用户,踢出后,剔出的用户再次访问服务器的时候会去到登录界面, security 配置 : htt ...

  8. win8 必须重新启动计算机才能关闭用户账户控制,win8系统禁止弹出用户账户控制窗口的方法...

    有不少用户在运行一些软件程序的时候,发现Windows总是会自动弹出用户账户控制窗口,虽然可以有效防止有害程序更改计算机设置,但是对于一些可信任的程序来说就多余了,那么要 1.在运行程序时,当弹出&q ...

  9. 组策略 计算机 用户账户控制,Win10系统总是弹出用户账户控制提示是否允许程序修改计算机的解决方法...

    Win10系统总是弹出用户账户控制提示"是否允许程序修改计算机"的解决方法 不少使用Win10系统的用户都会遇到这样一个问题:当我们打开程序时,系统总会弹出"你要允许以下 ...

最新文章

  1. js算法入门(2)--哈希表
  2. C++中虚函数、虚指针和虚表详解
  3. 人生苦短,我用python,为什么选择python,python简介
  4. 使用 010 Editor 分析二进制文件格式
  5. shell脚本每日一练(一)
  6. 一段 关于 观察者 和 唯识 的文字 (转帖摘录)
  7. CF993E Nikita and Order Statistics
  8. 【数字逻辑设计】推气泡
  9. php mktime 时间不对_PHP 语言需要避免的 10 大误区
  10. Python机器学习:PCA与梯度上升:05高维数据映射成低维数据(封装一个PCA)
  11. opencv判断两个是否相等_openCV学习pointPolygonTest()
  12. 系统学习NLP(十七)--文本相似度
  13. ubuntu18设置屏幕旋转_无线电对讲机,常用电台上中继台设置
  14. Javascript单例模式概念与实例
  15. 移动办公系统全自动加密和数字签名解决方案
  16. Node.js meitulu图片批量下载爬虫1.01版
  17. CC1310在433MHz下的PCB设计指南
  18. HighNewTech:【2019WAIC世界人工智能大会】上海正式开幕——马化腾的AI深思、二马激辩AI
  19. ElasticsearchTemplate的详细使用,完成多条件查询、匹配度查询等
  20. Android10/11 原生Launcher3深度定制开发

热门文章

  1. 开源版本_开源版本PowerShell Core 6.2 发布
  2. mysql一次读取500条数据_mysql批量插入500条数据
  3. java的imshow方法_imshow窗口是截止的
  4. native react 图片多选_开源一个图片组件 react-native-border-radius-image
  5. brew安装_苹果mac系统必须安装python3
  6. python拷贝文件函数_Python Set集合,函数,深入拷贝,浅入拷贝,文件处理
  7. word图片自动换行不亮_知网查重时提交pdf检测结果竟然比Word高很多!
  8. 创建hadoop用户并配置ssh免密码登录
  9. 不只是相关:基于真理共识论的社区问答可信答案选择新方法
  10. 用少于10行代码训练前沿深度学习新药研发模型