您好,欢迎来到纷纭教育。
搜索
您的当前位置:首页使用 SpringBoot + JWT+ Redis 实现在线人数统计功能

使用 SpringBoot + JWT+ Redis 实现在线人数统计功能

来源:纷纭教育

使用 SpringBoot + JWT+ Redis 实现在线人数统计功能

在线人数统计是Web应用中非常重要的一环,尤其对于大型网站和高流量平台而言。精准的在线人数统计不仅能够帮助网站管理员监控用户活动,还能在用户高峰期优化资源分配和服务器负载。传统的实现方式通常依赖于数据库,但在处理高并发数据时,数据库可能成为性能瓶颈。为了提高统计的实时性和系统的可扩展性,我们可以利用Redis作为存储解决方案,并结合JWT进行用户身份验证。

Redis,作为一种高性能的内存数据存储解决方案,能够高效地处理大量并发读写操作,非常适合用于存储和管理在线用户数据。它的Set数据结构使得我们可以轻松地管理在线用户列表,同时利用其过期时间功能来自动处理用户的会话超时问题。

JWT(JSON Web Token)则提供了一种简洁的方式来实现用户身份验证。它通过在用户登录时生成一个包含用户信息和有效期的Token,使得服务器在后续请求中可以通过解析Token来验证用户身份,从而简化了会话管理的复杂度。

在本设计方案中,我们将详细介绍如何使用Spring Boot框架搭建后端服务,如何结合JWT实现用户认证,以及如何利用Redis高效管理在线用户数据。我们还将展示如何使用Thymeleaf和Bootstrap构建前端页面,实现在线人数的实时展示。

通过本方案,你将学会如何将这些技术结合起来,创建一个高效、可靠的在线人数统计系统,提升用户体验并优化系统性能。

运行效果:

技术栈

  • Spring Boot3.3:用于构建后端应用程序。

  • JWT:用于用户认证和会话管理。

  • Redis:用于高效存储和更新在线用户数据。

  • Thymeleaf:用于前端模板渲染。

  • Bootstrap:用于前端样式和响应式设计。

  • Jquery:提供了一种高效的前端JavaScript库,帮助简化HTML文档遍历、事件处理、动画和Ajax交互,增强网页的响应式设计和用户体验。

项目结构

  • 后端:Spring Boot + JWT + Redis

  • 前端:Thymeleaf + Bootstrap + Jquery

环境配置

Maven配置(pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoderoad</groupId>
	<artifactId>online-user-statistics</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>online-user-statistics</name>
	<description>Demo project for Spring Boot</description>
	
	<properties>
		<java.version>17</java.version>
		<jjwt.version>0.12.6</jjwt.version>
	</properties>
	<dependencies>
		
		<!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Starter Security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!-- Spring Boot Starter Data Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- JSON Web Token -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>

        <!-- Thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件(application.yml

spring:
  redis:
    host: localhost
    port: 6379
    password: 123456
  security:
    jwt:
      secret: 37507d4cb936fdfb5dbb12a9a3983733
      expiration: 3600

后端实现

WT 生成和解析工具类

package com.icoderoad.online_user_statistics.util;

import static java.time.temporal.ChronoUnit.SECONDS;

import java.security.Key;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;


@Component
public class JwtUtil {

    @Value("${spring.security.jwt.secret}")
    private String secretKey;

    @Value("${spring.security.jwt.expiration}")
    private long expiration;// 过期时间以秒为单位

    private Key key;

    public String issueToken(String subject) {
        return issueToken(subject, Map.of());
    }

    public String issueToken(String subject, String ...scopes) {
        return issueToken(subject, Map.of("scopes", scopes));
    }

    public String issueToken(String subject, List<String> scopes) {
        return issueToken(subject, Map.of("scopes", scopes));
    }


    public String issueToken(
            String subject,
            Map<String, Object> claims) {
        String token = Jwts
                .builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(Date.from(Instant.now()))
                .setExpiration(
                        Date.from(
                                Instant.now().plus(expiration, SECONDS)
                        )
                )
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
        return token;
    }

    public String getSubject(String token) {
        return getClaims(token).getSubject();
    }

    private Claims getClaims(String token) {
        Claims claims = Jwts
                .parser()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
        return claims;
    }

    private Key getSigningKey() {
        return Keys.hmacShaKeyFor(secretKey.getBytes());
    }

    public boolean isTokenValid(String jwt, String username) {
        String subject = getSubject(jwt);
        return subject.equals(username) && !isTokenExpired(jwt);
    }

    private boolean isTokenExpired(String jwt) {
        Date today = Date.from(Instant.now());
        return getClaims(jwt).getExpiration().before(today);
    }
}

Redis配置和在线人数管理

package com.icoderoad.online_user_statistics.service;

import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class OnlineUserService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String ONLINE_USERS_KEY = "online_users";

    public void userLogin(String username) {
        redisTemplate.opsForSet().add(ONLINE_USERS_KEY, username);
        redisTemplate.expire(ONLINE_USERS_KEY, 1, TimeUnit.HOURS); // 设置过期时间
    }

    public void userLogout(String username) {
        redisTemplate.opsForSet().remove(ONLINE_USERS_KEY, username);
    }

    public Long getOnlineUserCount() {
        return redisTemplate.opsForSet().size(ONLINE_USERS_KEY);
    }

    public Set<String> getOnlineUsers() {
        return redisTemplate.opsForSet().members(ONLINE_USERS_KEY);
    }
}

控制器实现

package com.icoderoad.online_user_statistics.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.icoderoad.online_user_statistics.service.OnlineUserService;
import com.icoderoad.online_user_statistics.util.JwtUtil;

import jakarta.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private OnlineUserService onlineUserService;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public String login(@RequestParam String username, HttpServletRequest request) {
        // 生成JWT token
        String token = jwtUtil.issueToken(username);
        // 登录并记录在线用户
        onlineUserService.userLogin(username);
        return token;
    }

    @PostMapping("/logout")
    public String logout(HttpServletRequest request) {
       
        String authHeader = request.getHeader("Authorization");

        if (authHeader != null && authHeader.startsWith("Bearer ")) {
        	 // 从请求中提取JWT token
        	 String token = request.getHeader("Authorization").replace("Bearer ", "");
	        String username = jwtUtil.getSubject(token);
	        // 注销并更新在线用户
	        onlineUserService.userLogout(username);
        }
        return "Logged out";
    }

    @GetMapping("/onlineCount")
    public Long getOnlineUserCount() {
        return onlineUserService.getOnlineUserCount();
    }
}

视图控制器

package com.icoderoad.online_user_statistics.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

    @GetMapping("/")
    public String index() {
        return "index";
    }
    
}

前端实现

Thymeleaf模板(index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>在线人数统计</title>
    <!-- 使用Bootstrap 4.5.2 CSS -->
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
    <h1>在线人数统计</h1>
    <button id="loginBtn" class="btn btn-primary">登录</button>
    <button id="logoutBtn" class="btn btn-secondary">登出</button>
    <h2>在线人数: <span id="onlineCount">0</span></h2>
</div>

<!-- 使用jQuery 3.5.1 -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<!-- 使用Bootstrap 4.5.2 JS -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
<script>
    $(document).ready(function() {
        $('#loginBtn').click(function() {
            $.post('/api/login', {username: 'testuser'})
                .done(function(token) {
                    localStorage.setItem('token', token);
                    updateOnlineCount();
                });
        });

        $('#logoutBtn').click(function() {
            const token = localStorage.getItem('token');
            $.post('/api/logout', {}, {
                headers: {
                    'Authorization': 'Bearer ' + token
                }
            })
            .done(function() {
                localStorage.removeItem('token');
                updateOnlineCount();
            });
        });

        function updateOnlineCount() {
            $.get('/api/onlineCount')
                .done(function(count) {
                    $('#onlineCount').text(count);
                });
        }

        // 页面加载时更新在线人数
        updateOnlineCount();
    });
</script>
</body>
</html>

总结

本文详细介绍了如何使用 Spring Boot、JWT 和 Redis 来实现一个在线人数统计系统。通过结合这些技术,我们可以高效地管理用户会话,并实时统计在线人数。后端使用 JWT 进行认证,Redis 用于存储在线用户数据,前端使用 Thymeleaf 和 Bootstrap 进行展示。这个系统不仅提高了用户体验,还保证了系统的高效性和可靠性。

今天就讲到这里,如果有问题需要咨询,大家可以直接留言或扫下方二维码来知识星球找我,我们会尽力为你解答。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- fenyunshixun.cn 版权所有 湘ICP备2023022495号-9

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务