查看原文
科技

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

编程疏影 路条编程
2024-09-04


使用 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

在 src/main/resources/templates 目录下创建 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 进行展示。这个系统不仅提高了用户体验,还保证了系统的高效性和可靠性。


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


AI资源聚合站已经正式上线,该平台不仅仅是一个AI资源聚合站,更是一个为追求知识深度和广度的人们打造的智慧聚集地。通过访问 AI 资源聚合网站 https://ai-ziyuan.techwisdom.cn/,你将进入一个全方位涵盖人工智能和语言模型领域的宝藏库。


作者:路条编程(转载请获本公众号授权,并注明作者与出处)
继续滑动看下一个
路条编程
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存