文章目录

  • 一、使用JWT做用户实时在线数判断的原理
  • 1.1、什么是JWT
  • 1.2、使用JWT做用户在线数统计的原理
  • 二、具体的代码实现



最近再做一个小程序项目,在这个项目中需要有一个管理员用户在线数实时刷新的功能,一开始用的是网上广为流传的做法,即创建一个session监听器,在用户登录时即创建一个session,监听器记录下来并且把count加一,当用户点击注销时把session给remove掉,count减一。但是这个方案只适合估计一个值,而不适合做精确的在线人数判断,譬如,当用户关闭浏览器时并不会触发session监听,当下一次登录时仍然会让count加一;或者在session过期时,session监听并不能做一个实时的响应去将在线数减一,当用户在次登陆,由于cookie中含有的session_id不同而导致session监听器记录下session创建,而使count加一。

一、使用JWT做用户实时在线数判断的原理

本文不仔细地讲解什么是JWT,因为关于这个的文章已经写了很多了,但是在这里还是需要提一下什么是JWT。

1.1、什么是JWT

JWT即Json Web token(Json网络令牌),它由一种特殊的加密方式生成一串包含令牌状态(令牌过期时间、令牌签发时间、令牌的受众等)、用户数据等的字符串。前端在每次请求接口时都需要将这串令牌传送给后端校验这次请求是否正确,数据是否被篡改,用户是否已过期等。

JAVA 系统在线人数统计方案 jwt统计在线人数_jwt代替session做用户在线数

关于如何在SpringBoot下使用JWT的代码,都在这篇文章中:

* 

1.2、使用JWT做用户在线数统计的原理

JAVA 系统在线人数统计方案 jwt统计在线人数_springboot jwt_02

根据时序图的这套方案,用户如果60s内没有任何操作(不调用接口去传递token)则判定该用户为下线状态,当用户重新登陆或者再次操作则判定为在线状态。

二、具体的代码实现

  • OnlineCounter
@Component
public class OnlineCounter {
    //每次打开此类只初始化一次countMap
    private static Map countMap = new ConcurrentHashMap<String,Object>();


    public void insertToken(String token){
        //获得当前时间(毫秒)
        long currentTime = System.currentTimeMillis();
        //解析token,获得签发时间
        Claims claims = null;
        try {
            claims = JWTUtils.parseJWT(token);
        } catch (Exception e) {
            throw new RuntimeException("token不存在或已过期");
        }
        Date issuedAt = claims.getIssuedAt();
        //以签发时间为key。当前时间+60s为value存入countMap中
        countMap.put(issuedAt.toString(),currentTime+60*1000);
    }

 
    public Integer getOnlineCount(){
        int onlineCount = 0;
        //获取countMap的迭代器
        Iterator iterator = countMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String,Object>  entry = (Map.Entry<String, Object>) iterator.next();
            Long value = (Long) entry.getValue();
            if (value > System.currentTimeMillis()) {
                //过期时间大于当前时间则没有过期
                onlineCount++;
            }

        }


        return onlineCount;
    }
}
  • AdminController
@Autowired
    private OnlineCounter onlineCounter;

    /**
     * @auther: Arong
     * @description: 获取当前实时在线人数(精确度为60s范围内)
     * @param
     * @return: int
     * @date: 2019/1/19 17:44
     */
    @GetMapping(value = "/getOnlineCount")
    public int getRealOnlineCount() {
        Integer onlines = onlineCounter.getOnlineCount();
        return onlines;
    }
  • JWTInterceptor
@Autowired
    private OnlineCounter onlineCounter;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 允许跨域
        response.setHeader("Access-Control-Allow-Origin", "*");
        //后台管理页面产生的token
        String token = request.getHeader("authorization");
        //判断是否过期
        Optional.ofNullable(token)
                .map(n -> {
                    try {
                        return JWTUtils.parseJWT(n);
                    } catch (Exception e) {
                        throw new RuntimeException("token不存在");
                    }
                })
                .map(n->{
                    //存储该token方便记录在线人数
                    try {
                        onlineCounter.insertToken(token);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return n.getExpiration();
                })
                .orElseThrow(()->new RuntimeException("token已过期!请用户重新登陆!"));

        return true;
    }
  • 获取实时在线人数
@GetMapping(value = "/getOnlineCount")
    public int getRealOnlineCount() {
        Integer onlines = onlineCounter.getOnlineCount();
        return onlines;
    }