实现实时监听后台管理员在线人数

效果:
在这里插入图片描述

一、思路

1、管理员登录时,将用户信息存储到Redis里,并且设置过期时间(注意过期时间跟token同步)
2、通过Key去Redis遍历获取用户信息,加于展示

二、设计

1、结构设计

Key: LOGIN_TOKEN_KEY
Value
在这里插入图片描述

2、对象

@Data
public class OnlineAdmin {
    /**
     * 会话编号
     */
    private String tokenId;

    /**
     * 用户Token
     */
    private String token;

    /**
     * 管理员的UID
     */
    private String adminUid;

    /**
     * 用户名称
     */
    private String userName;

    /**
     * 登录IP地址
     */
    private String ipaddr;

    /**
     * 登录地址
     */
    private String loginLocation;

    /**
     * 浏览器类型
     */
    private String browser;

    /**
     * 操作系统
     */
    private String os;

    /**
     * 角色名称
     */
    private String roleName;

    /**
     * 登录时间
     */
    private String loginTime;

    /**
     * 过期时间
     */
    private String expireTime;
}

三、实现

1、登录时将用户信息存储到Redis上

在这里插入图片描述
具体实现:

@Override
public void addOnlineAdmin(Admin admin, Long expirationSecond) {
   // 1.获取请求信息
   HttpServletRequest request = RequestHolder.getRequest();
   // 2.获取操作系统,浏览器及浏览器版本信息
   Map<String, String> map = IpUtils.getOsAndBrowserInfo(request);
   String os = map.get(SysConf.OS);
   String browser = map.get(SysConf.BROWSER);
   String ip = IpUtils.getIpAddr(request);
   // 3.组装用户信息
   OnlineAdmin onlineAdmin = new OnlineAdmin();
   onlineAdmin.setAdminUid(admin.getUid());
   onlineAdmin.setTokenId(admin.getTokenUid());
   onlineAdmin.setToken(admin.getValidCode());
   onlineAdmin.setOs(os);
   onlineAdmin.setBrowser(browser);
   onlineAdmin.setIpaddr(ip);
   onlineAdmin.setLoginTime(DateUtils.getNowTime());
   onlineAdmin.setRoleName(admin.getRole().getRoleName());
   onlineAdmin.setUserName(admin.getUserName());
   onlineAdmin.setExpireTime(DateUtils.getDateStr(new Date(), expirationSecond));
   // 4.从Redis中获取IP来源
   String jsonResult = redisUtil.get(RedisConf.IP_SOURCE + Constants.SYMBOL_COLON + ip);
   if (StringUtils.isEmpty(jsonResult)) {
       String addresses = IpUtils.getAddresses(SysConf.IP + SysConf.EQUAL_TO + ip, SysConf.UTF_8);
       if (StringUtils.isNotEmpty(addresses)) {
          onlineAdmin.setLoginLocation(addresses);
          redisUtil.setEx(RedisConf.IP_SOURCE + Constants.SYMBOL_COLON + ip, addresses, 24, TimeUnit.HOURS);
       }
    } else {
       onlineAdmin.setLoginLocation(jsonResult);
    }
    // 5.将登录的管理员存储到在线用户表
    redisUtil.setEx(RedisConf.LOGIN_TOKEN_KEY + RedisConf.SEGMENTATION + admin.getValidCode(), JsonUtils.objectToJson(onlineAdmin), expirationSecond, TimeUnit.SECONDS);
    // 6.在维护一张表,用于 uuid - token 互相转换
    redisUtil.setEx(RedisConf.LOGIN_UUID_KEY + RedisConf.SEGMENTATION + admin.getTokenUid(), admin.getValidCode(), expirationSecond, TimeUnit.SECONDS);
}

2、展示用户表

创建接口

@AuthorityVerify
@ApiOperation(value = "获取在线管理员列表", notes = "获取在线管理员列表", response = String.class)
@PostMapping(value = "/getOnlineAdminList")
public String getOnlineAdminList(@Validated({GetList.class}) @RequestBody AdminVO adminVO, BindingResult result) {
    // 参数校验
    ThrowableUtils.checkParamArgument(result);
    return adminService.getOnlineAdminList(adminVO);
}

具体实现

@Override
public String getOnlineAdminList(AdminVO adminVO) {
   // 拼装分页信息
   int pageSize = adminVO.getPageSize().intValue();
   int currentPage = adminVO.getCurrentPage().intValue();
        AtomicReference<Integer> total = new AtomicReference<Integer>(0);
    int startIndex = Math.max((currentPage - 1) * pageSize, 0);

    // 获取Redis中匹配的key
    Set<String> keys = redisUtil.getRedisTemplate().execute((RedisCallback<Set<String>>) connection -> {
    	Set<String> keySetTemp = new ConcurrentSkipListSet<>();
        int index = 0;
        try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions()
                    .match(RedisConf.LOGIN_TOKEN_KEY + "*")
                    .count(100000)
                    .build())) {

           // 获取登录用户的key
           while (cursor.hasNext()) {
               index ++;
               // 先偏移起始位置个数据
               if (index<startIndex){
                    cursor.next();
                    continue;
               }
               String key = new String(cursor.next(), CharsetUtil.UTF_8);

               // 获取需要的key
               if (keySetTemp.size() <= pageSize) {
                    keySetTemp.add(key);
               }

            }
             total.set(index);
        } catch (Exception e) {
            log.error("Redis Scan get Exception:{}", ExceptionUtil.stacktraceToOneLineString(e), e);
            return new ConcurrentSkipListSet<>();
        }
        return keySetTemp;
    });

    // 获取在线用户数据
    List<String> onlineAdminJsonList = redisUtil.multiGet(keys);
    List<OnlineAdmin> onlineAdminList = new ArrayList<>();
    for (String item : onlineAdminJsonList) {
        OnlineAdmin onlineAdmin = JsonUtils.jsonToPojo(item, OnlineAdmin.class);
        // 数据脱敏【移除用户的token令牌】
        onlineAdmin.setToken("");
        onlineAdminList.add(onlineAdmin);
    }
    Page<OnlineAdmin> page = new Page<>();
    page.setCurrent(currentPage);
    page.setTotal(total.get());
    page.setSize(pageSize);
    page.setRecords(onlineAdminList);
    return ResultUtil.successWithData(page);
}

3、踢出用户

创建接口

@AuthorityVerify
@OperationLogger(value = "强退用户")
@ApiOperation(value = "强退用户", notes = "强退用户", response = String.class)
@PostMapping(value = "/forceLogout")
public String forceLogout(@ApiParam(name = "tokenUidList", value = "tokenList", required = false) @RequestBody List<String> tokenUidList) {
    return adminService.forceLogout(tokenUidList);
}

具体实现

@Override
public String forceLogout(List<String> tokenUidList) {
    if (tokenUidList == null || tokenUidList.size() == 0) {
        return ResultUtil.errorWithMessage(MessageConf.PARAM_INCORRECT);
   }

    // 从Redis中通过TokenUid获取到用户的真实token
    List<String> tokenList = new ArrayList<>();
    tokenUidList.forEach(item -> {
        String token = redisUtil.get(RedisConf.LOGIN_UUID_KEY + RedisConf.SEGMENTATION + item);
        if(StringUtils.isNotEmpty(token)) {
            tokenList.add(token);
        }
    });

    // 根据token删除Redis中的在线用户
    List<String> keyList = new ArrayList<>();
    String keyPrefix = RedisConf.LOGIN_TOKEN_KEY + RedisConf.SEGMENTATION;
    for (String token : tokenList) {
        keyList.add(keyPrefix + token);
    }
    redisUtil.delete(keyList);
    return ResultUtil.successWithMessage(MessageConf.OPERATION_SUCCESS);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亿先生@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值