创建 matching 匹配页面及完成到 playing 页面的跳转
This commit is contained in:
+18
-2
@@ -60,7 +60,7 @@
|
||||
<version>3.5.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 用于安全验证-->
|
||||
<!-- 用于安全验证-->
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -68,7 +68,7 @@
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 用于使用 JWT 验证-->
|
||||
<!-- 用于使用 JWT 验证-->
|
||||
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
@@ -92,6 +92,22 @@
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 用于配置使用 websocket-->
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- json 用于前后端通信-->
|
||||
<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.24</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
@@ -25,7 +26,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
|
||||
|
||||
// 配置密码加密方式
|
||||
// 配置密码加密方式
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
@@ -36,10 +37,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
/*
|
||||
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||
用于配置公开链接
|
||||
*/
|
||||
|
||||
/*
|
||||
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||
用于配置公开链接
|
||||
*/
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
@@ -52,4 +54,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
// 用于配置 websocket:放行所有的 websocket/ 链接
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.ignoring().antMatchers("/websocket/**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.kob.backend.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,12 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
token = token.substring(7);
|
||||
|
||||
|
||||
|
||||
// 核心验证逻辑
|
||||
String userid;
|
||||
try {
|
||||
// 将 token 解析,如果能成功解析出 userid 表示合法,否则表示不合法
|
||||
Claims claims = JwtUtil.parseJWT(token);
|
||||
userid = claims.getSubject();
|
||||
} catch (Exception e) {
|
||||
@@ -53,6 +57,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
User user = userMapper.selectById(Integer.parseInt(userid));
|
||||
|
||||
// User user = userMapper.selectById(new JwtAuthenticationUtil().getUserId(token));
|
||||
if (user == null) {
|
||||
throw new RuntimeException("用户名未登录");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
package com.kob.backend.consumer;
|
||||
|
||||
// WebSocket用于前后端通信
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.kob.backend.consumer.utils.JwtAuthenticationUtil;
|
||||
import com.kob.backend.mapper.UserMapper;
|
||||
import com.kob.backend.pojo.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.server.PathParam;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
@Component
|
||||
@ServerEndpoint("/websocket/{token}") // 注意不要以'/'结尾
|
||||
public class WebSocketServer {
|
||||
// 后端向前端发送信息,首先需要创建一个 session
|
||||
private Session session = null;
|
||||
|
||||
// 用户信息:定义一个成员变量
|
||||
private User user;
|
||||
|
||||
/*
|
||||
存储所有链接:对所有 websocket 可见的全局变量,存储为 static 静态变量
|
||||
因为每个 websocket 实例都在一个独立的线程里,所以该公共变量应该是线程安全的
|
||||
使用线程安全的 Hash 表 ConcurrentHashMap<>(),
|
||||
将 userId 映射到 webSocketServer
|
||||
*/
|
||||
private static final ConcurrentHashMap<Integer, WebSocketServer> users = new ConcurrentHashMap<>();
|
||||
|
||||
// 使用 CopyOnWriteArraySet 创建一个线程安全的匹配池 matchPool
|
||||
private static final CopyOnWriteArraySet<User> matchPool = new CopyOnWriteArraySet<>();
|
||||
|
||||
|
||||
//在 WebSocketServer 中注入数据库的方法演示-> 使用 static 定义为独一份的变量
|
||||
private static UserMapper userMapper;
|
||||
|
||||
// 注入方法
|
||||
@Autowired
|
||||
public void setUserMapper(UserMapper userMapper) {
|
||||
// 静态变量 userMapper 访问需要使用类名 WebSocketServer 访问
|
||||
WebSocketServer.userMapper = userMapper;
|
||||
}
|
||||
|
||||
|
||||
// @OnOpen 创建链接时自动触发这个函数
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("token") String token) throws IOException {
|
||||
// 建立链接时需要将 session 存下来
|
||||
this.session = session;
|
||||
// 成功建立连接时,输出 connected!
|
||||
System.out.println("backend connected!");
|
||||
// 将 userId 取出来,通过 userId 将 user 找出来
|
||||
// int userId = Integer.parseInt(token);
|
||||
int userId = JwtAuthenticationUtil.getUserId(token);
|
||||
this.user = userMapper.selectById(userId);
|
||||
|
||||
// 如果 user 存在, 表示用户登录成功, 用户信息是存在的
|
||||
if (this.user != null) {
|
||||
// 将 user 存到 users HashMap里
|
||||
users.put(userId, this);
|
||||
|
||||
// (测试)后台输出看用户信息
|
||||
// System.out.println(user);
|
||||
}
|
||||
// 否则,断开连接(这里需要抛出异常)
|
||||
else {
|
||||
this.session.close();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose() {
|
||||
// 关闭链接
|
||||
System.out.println("backend disconnected!");
|
||||
// 断开连接时,需要将 user 从 users 里面删掉
|
||||
if (this.user != null) {
|
||||
users.remove(this.user.getId());
|
||||
// 将匹配池数组删掉
|
||||
matchPool.remove(this.user);
|
||||
}
|
||||
}
|
||||
|
||||
// @OnMessage 用于从前端接收请求: 一般 onMessage 当成路由使用,做为消息判断处理的中间部分
|
||||
@OnMessage
|
||||
public void onMessage(String message, Session session) {
|
||||
// 从 Client 接收消息
|
||||
System.out.println("receive message");
|
||||
// 解析从前端接收到的请求
|
||||
JSONObject data = JSONObject.parseObject(message);
|
||||
String event = data.getString("event");
|
||||
// System.out.println(event);
|
||||
|
||||
// 匹配状态函数调用
|
||||
if ("start-matching".equals(event)) {
|
||||
startMatching();
|
||||
} else if ("stop-matching".equals(event)) {
|
||||
stopMatching();
|
||||
}
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error) {
|
||||
error.printStackTrace();
|
||||
}
|
||||
|
||||
// 从后端向前端发送信息
|
||||
public void sendMessage(String message) {
|
||||
// 异步通信过程,先加一个锁
|
||||
synchronized (this.session) {
|
||||
try {
|
||||
// 将 message 发送到前端
|
||||
this.session.getBasicRemote().sendText(message);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 开始匹配的逻辑部分
|
||||
private void startMatching() {
|
||||
System.out.println("start matching");
|
||||
matchPool.add(this.user);
|
||||
|
||||
while (matchPool.size()>=2){
|
||||
// 迭代器用于枚举前两个人进行匹配
|
||||
Iterator<User> it = matchPool.iterator();
|
||||
User a = it.next(), b = it.next();
|
||||
// 取出两个人之后,从匹配池中将他们删除
|
||||
matchPool.remove(a);
|
||||
matchPool.remove(b);
|
||||
|
||||
// 将 a 配对成功的消息传回客户端
|
||||
JSONObject respA = new JSONObject();
|
||||
respA.put("event","start-matching");
|
||||
respA.put("opponent_username",b.getUsername());
|
||||
respA.put("opponent_photo",b.getPhoto());
|
||||
// 获取 a 的链接,并通过 sendMessage 将消息传给前端
|
||||
users.get(a.getId()).sendMessage(respA.toJSONString());
|
||||
|
||||
// 同理,将 b 的匹配成功信息传回给前端
|
||||
JSONObject respB = new JSONObject();
|
||||
respB.put("event","start-matching");
|
||||
respB.put("opponent_username",a.getUsername());
|
||||
respB.put("opponent_photo",a.getPhoto());
|
||||
users.get(b.getId()).sendMessage(respB.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
// 取消匹配的逻辑部分
|
||||
private void stopMatching() {
|
||||
System.out.println("stop matching");
|
||||
matchPool.remove(this.user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.kob.backend.consumer.utils;
|
||||
|
||||
import com.kob.backend.utils.JwtUtil;
|
||||
import io.jsonwebtoken.Claims;
|
||||
|
||||
//Jwt 验证配置工具类
|
||||
public class JwtAuthenticationUtil {
|
||||
public static Integer getUserId(String token) {
|
||||
// 核心验证逻辑
|
||||
// 默认 userid 赋值为 -1 ,表示不存在
|
||||
int userId = -1;
|
||||
try {
|
||||
// 将 token 解析,如果能成功解析出 userid 表示合法,否则表示不合法
|
||||
Claims claims = JwtUtil.parseJWT(token);
|
||||
userId = Integer.parseInt(claims.getSubject());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -74,7 +74,7 @@ public class RegisterServiceImpl implements RegisterService {
|
||||
// 对密码进行加密
|
||||
String encodedPassword = new BCryptPasswordEncoder().encode(password);
|
||||
// 默认头像
|
||||
String photo = "https://cdn.acwing.com/media/user/profile/photo/253652_lg_e3d8435b66.jpg";
|
||||
String photo = "https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202302251824054.png";
|
||||
// id 是数据库自增,这里生成新用户只需要将 id 参数写为 null 即可
|
||||
User user = new User(null, username, encodedPassword, photo);
|
||||
userMapper.insert(user);
|
||||
|
||||
Reference in New Issue
Block a user