优化统一鉴权,优化目录结构
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.onekeycall.videotablet.config;
|
package com.onekeycall.videotablet.config;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.filter.JwtAuthenticationFilter;
|
import com.onekeycall.videotablet.filter.JwtAuthenticationFilter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -36,12 +37,11 @@ public class SecurityConfig {
|
|||||||
.requestMatchers("/api/ws/**", "/topic/**").permitAll()
|
.requestMatchers("/api/ws/**", "/topic/**").permitAll()
|
||||||
.requestMatchers("/uploadFile/**").permitAll()
|
.requestMatchers("/uploadFile/**").permitAll()
|
||||||
.requestMatchers("/public/**").permitAll()
|
.requestMatchers("/public/**").permitAll()
|
||||||
.requestMatchers("/sn/**").permitAll()
|
// .requestMatchers("/sn/**").permitAll()
|
||||||
.requestMatchers("/user/**").permitAll()
|
// .requestMatchers("/user/**").permitAll()
|
||||||
.requestMatchers("/rtc/**").permitAll()
|
.requestMatchers("/rtc/**").permitAll()
|
||||||
.requestMatchers("/admin/**").hasRole("ADMIN")
|
.requestMatchers("/admin/**").hasRole("ADMIN")
|
||||||
// .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
|
// .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
|
||||||
.requestMatchers("/user/**").permitAll()
|
|
||||||
.requestMatchers("/contact/**").permitAll()
|
.requestMatchers("/contact/**").permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.onekeycall.videotablet.config;
|
|||||||
import com.onekeycall.videotablet.handler.CustomWebSocketHandler;
|
import com.onekeycall.videotablet.handler.CustomWebSocketHandler;
|
||||||
import com.onekeycall.videotablet.interceptor.AuthHandshakeInterceptor;
|
import com.onekeycall.videotablet.interceptor.AuthHandshakeInterceptor;
|
||||||
import com.onekeycall.videotablet.utils.JwtUtil;
|
import com.onekeycall.videotablet.utils.JwtUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||||
@@ -11,16 +12,13 @@ import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSocket
|
@EnableWebSocket
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class WebSocketConfig implements WebSocketConfigurer {
|
public class WebSocketConfig implements WebSocketConfigurer {
|
||||||
@Autowired
|
|
||||||
private JwtUtil jwtUtil;
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
private final CustomWebSocketHandler webSocketHandler;
|
private final CustomWebSocketHandler webSocketHandler;
|
||||||
|
|
||||||
public WebSocketConfig(CustomWebSocketHandler webSocketHandler) {
|
|
||||||
this.webSocketHandler = webSocketHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||||
registry.addHandler(webSocketHandler, "/ws") // 客户端连接端点
|
registry.addHandler(webSocketHandler, "/ws") // 客户端连接端点
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.pub;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.result.Result;
|
import com.onekeycall.videotablet.result.Result;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@RequestMapping("/public")
|
||||||
public class HelloController {
|
public class HelloController {
|
||||||
//引入 redis
|
//引入 redis
|
||||||
@Autowired
|
@Autowired
|
||||||
private StringRedisTemplate stringRedisTemplate;
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
@GetMapping("/public/hello")
|
@GetMapping("/hello")
|
||||||
public Result getMethodName() {
|
public Result getMethodName() {
|
||||||
return Result.ok().message("Welcome to Yijiantong");
|
return Result.ok().message("Welcome to Yijiantong");
|
||||||
}
|
}
|
||||||
@@ -24,7 +22,7 @@ public class HelloController {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@PostMapping("/public/set")
|
@PostMapping("/set")
|
||||||
public Result setRedis(@RequestParam(value = "username") String username) {
|
public Result setRedis(@RequestParam(value = "username") String username) {
|
||||||
//存储 key-value 键值对: "username"-"jaychou"
|
//存储 key-value 键值对: "username"-"jaychou"
|
||||||
stringRedisTemplate.opsForValue().set("username", username);
|
stringRedisTemplate.opsForValue().set("username", username);
|
||||||
@@ -36,7 +34,7 @@ public class HelloController {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@GetMapping("/public/get")
|
@GetMapping("/get")
|
||||||
public Result getRedis(@RequestParam(value = "username") String username) {
|
public Result getRedis(@RequestParam(value = "username") String username) {
|
||||||
//通过 key 值读取 value
|
//通过 key 值读取 value
|
||||||
String result = stringRedisTemplate.opsForValue().get(username);
|
String result = stringRedisTemplate.opsForValue().get(username);
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.pub;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.dto.TokenPair;
|
import com.onekeycall.videotablet.dto.TokenPair;
|
||||||
import com.onekeycall.videotablet.entity.User;
|
import com.onekeycall.videotablet.entity.User;
|
||||||
import com.onekeycall.videotablet.result.Result;
|
import com.onekeycall.videotablet.result.Result;
|
||||||
import com.onekeycall.videotablet.service.UserService;
|
import com.onekeycall.videotablet.service.UserService;
|
||||||
import com.onekeycall.videotablet.utils.JwtUtil;
|
import com.onekeycall.videotablet.utils.JwtUtil;
|
||||||
|
import com.onekeycall.videotablet.utils.TextUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -77,6 +78,10 @@ public class LoginController {
|
|||||||
}
|
}
|
||||||
String userId = user.getUserId();
|
String userId = user.getUserId();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(user.getPassword())) {
|
||||||
|
return Result.error().message("用户未设置密码,请使用验证码登录");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 创建认证令牌
|
// 1. 创建认证令牌
|
||||||
Authentication authenticationToken = new UsernamePasswordAuthenticationToken(userId, password);
|
Authentication authenticationToken = new UsernamePasswordAuthenticationToken(userId, password);
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.pub;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.config.FilePath;
|
import com.onekeycall.videotablet.config.FilePath;
|
||||||
import com.onekeycall.videotablet.entity.DeviceInfo;
|
import com.onekeycall.videotablet.entity.DeviceInfo;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.sms;
|
||||||
|
|
||||||
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
||||||
import com.aliyun.tea.TeaException;
|
import com.aliyun.tea.TeaException;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.sms;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.result.Result;
|
import com.onekeycall.videotablet.result.Result;
|
||||||
import com.onekeycall.videotablet.sms.SendSms;
|
import com.onekeycall.videotablet.sms.SendSms;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.sn;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.onekeycall.videotablet.entity.DeviceInfo;
|
import com.onekeycall.videotablet.entity.DeviceInfo;
|
||||||
@@ -160,22 +160,22 @@ public class BindSnController {
|
|||||||
// TODO: 2025/8/22 Device_Token在docker无法被接收到,使用Device-Token代替
|
// TODO: 2025/8/22 Device_Token在docker无法被接收到,使用Device-Token代替
|
||||||
@GetMapping("/get_bind_statu")
|
@GetMapping("/get_bind_statu")
|
||||||
public Result getBindStatus(
|
public Result getBindStatus(
|
||||||
@RequestHeader("Device-Token") String deviceToken, @RequestHeader("Device-ID") String deviceId,
|
// @RequestHeader("Device-Token") String deviceToken, @RequestHeader("Device-ID") String deviceId,
|
||||||
@RequestHeader("Device-Sig") String deviceSig,
|
// @RequestHeader("Device-Sig") String deviceSig,
|
||||||
@RequestParam(value = "sn") String sn) {
|
@RequestParam(value = "sn") String sn) {
|
||||||
|
|
||||||
if (!jwtUtil.validateDeviceToken(deviceToken, deviceId, sn)) {
|
// if (!jwtUtil.validateDeviceToken(deviceToken, deviceId, sn)) {
|
||||||
return Result.error().message("Invalid token");
|
// return Result.error().message("Invalid token");
|
||||||
}
|
// }
|
||||||
|
|
||||||
DeviceInfo deviceInfo = deviceSnService.findBySn(sn);
|
DeviceInfo deviceInfo = deviceSnService.findBySn(sn);
|
||||||
if (deviceInfo == null) {
|
if (deviceInfo == null) {
|
||||||
return Result.notFound().message("sn not found");
|
return Result.notFound().message("sn not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!deviceInfo.getBindSig().equals(deviceSig)) {
|
// if (!deviceInfo.getBindSig().equals(deviceSig)) {
|
||||||
return Result.error().message("device sig not match");
|
// return Result.error().message("device sig not match");
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (TextUtils.isEmpty(deviceInfo.getBindPhone())) {
|
if (TextUtils.isEmpty(deviceInfo.getBindPhone())) {
|
||||||
return Result.error().message("sn not bind");
|
return Result.error().message("sn not bind");
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.sn;
|
||||||
|
|
||||||
import com.aliyun.core.annotation.Body;
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.onekeycall.videotablet.bean.ApkUploadRequest;
|
import com.onekeycall.videotablet.bean.ApkUploadRequest;
|
||||||
import com.onekeycall.videotablet.entity.ApkInfo;
|
import com.onekeycall.videotablet.entity.ApkInfo;
|
||||||
import com.onekeycall.videotablet.entity.DeviceApkInfo;
|
|
||||||
import com.onekeycall.videotablet.result.Result;
|
import com.onekeycall.videotablet.result.Result;
|
||||||
import com.onekeycall.videotablet.service.DeviceApkInfoService;
|
import com.onekeycall.videotablet.service.DeviceApkInfoService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.sn;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.entity.Contact;
|
import com.onekeycall.videotablet.entity.Contact;
|
||||||
import com.onekeycall.videotablet.entity.DeviceInfo;
|
import com.onekeycall.videotablet.entity.DeviceInfo;
|
||||||
@@ -14,9 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/sn")
|
@RequestMapping("/sn")
|
||||||
@@ -30,41 +28,7 @@ public class DevicesController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ContactService contactService;
|
private ContactService contactService;
|
||||||
|
|
||||||
@GetMapping("/get_sn_list")
|
|
||||||
public Result register(
|
|
||||||
@RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId,
|
|
||||||
@RequestParam(value = "user_id") String userId, @RequestParam(value = "sn", required = false) String sn) {
|
|
||||||
// 1. 校验 Authorization 头
|
|
||||||
if (!authHeader.startsWith("Bearer ")) {
|
|
||||||
return Result.error().message("Invalid Authorization header");
|
|
||||||
}
|
|
||||||
String token = authHeader.substring(7); // 去掉 "Bearer " 前缀
|
|
||||||
|
|
||||||
// 2. 校验 Token
|
|
||||||
if (!jwtUtil.validateAccessToken(userId, token, deviceId)) {
|
|
||||||
return Result.error().message("Invalid token");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(sn)) {
|
|
||||||
List<DeviceInfo> deviceInfos = deviceSnService.findByUserId(userId);
|
|
||||||
if (deviceInfos == null || deviceInfos.isEmpty()) {
|
|
||||||
return Result.notFound().message("sn not found");
|
|
||||||
} else {
|
|
||||||
return Result.ok().data("deviceInfos", deviceInfos);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DeviceInfo deviceInfo = deviceSnService.findBySn(sn);
|
|
||||||
if (deviceInfo == null) {
|
|
||||||
return Result.notFound().message("sn not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!deviceInfo.getUserId().equals(userId)) {
|
|
||||||
return Result.error().message("sn not belong to user");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.ok().data("deviceInfo", deviceInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/update_location")
|
@PostMapping("/update_location")
|
||||||
public Result updateLocation(
|
public Result updateLocation(
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.user;
|
||||||
|
|
||||||
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
|
import com.onekeycall.videotablet.controller.pub.LoginController;
|
||||||
import com.onekeycall.videotablet.dto.TokenPair;
|
import com.onekeycall.videotablet.dto.TokenPair;
|
||||||
import com.onekeycall.videotablet.entity.DeviceApkInfo;
|
import com.onekeycall.videotablet.entity.DeviceApkInfo;
|
||||||
import com.onekeycall.videotablet.entity.DeviceInfo;
|
import com.onekeycall.videotablet.entity.DeviceInfo;
|
||||||
@@ -21,6 +21,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -105,6 +106,42 @@ public class UserController {
|
|||||||
return Result.ok().data("user_info", userInfo);
|
return Result.ok().data("user_info", userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get_sn_list")
|
||||||
|
public Result register(
|
||||||
|
@RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId,
|
||||||
|
@RequestParam(value = "user_id") String userId, @RequestParam(value = "sn", required = false) String sn) {
|
||||||
|
// 1. 校验 Authorization 头
|
||||||
|
if (!authHeader.startsWith("Bearer ")) {
|
||||||
|
return Result.error().message("Invalid Authorization header");
|
||||||
|
}
|
||||||
|
String token = authHeader.substring(7); // 去掉 "Bearer " 前缀
|
||||||
|
|
||||||
|
// 2. 校验 Token
|
||||||
|
if (!jwtUtil.validateAccessToken(userId, token, deviceId)) {
|
||||||
|
return Result.error().message("Invalid token");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(sn)) {
|
||||||
|
List<DeviceInfo> deviceInfos = deviceSnService.findByUserId(userId);
|
||||||
|
if (deviceInfos == null || deviceInfos.isEmpty()) {
|
||||||
|
return Result.notFound().message("sn not found");
|
||||||
|
} else {
|
||||||
|
return Result.ok().data("deviceInfos", deviceInfos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DeviceInfo deviceInfo = deviceSnService.findBySn(sn);
|
||||||
|
if (deviceInfo == null) {
|
||||||
|
return Result.notFound().message("sn not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceInfo.getUserId().equals(userId)) {
|
||||||
|
return Result.error().message("sn not belong to user");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok().data("deviceInfo", deviceInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/get_sn_location")
|
@GetMapping("/get_sn_location")
|
||||||
public Result getSnLocation(
|
public Result getSnLocation(
|
||||||
@RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId,
|
@RequestHeader("Authorization") String authHeader, @RequestHeader("Device-ID") String deviceId,
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
package com.onekeycall.videotablet.controller;
|
package com.onekeycall.videotablet.controller.user;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.entity.User;
|
import com.onekeycall.videotablet.entity.User;
|
||||||
import com.onekeycall.videotablet.result.Result;
|
import com.onekeycall.videotablet.result.Result;
|
||||||
import com.onekeycall.videotablet.service.UserService;
|
import com.onekeycall.videotablet.service.UserService;
|
||||||
import com.onekeycall.videotablet.utils.JwtUtil;
|
import com.onekeycall.videotablet.utils.JwtUtil;
|
||||||
import com.onekeycall.videotablet.utils.TextUtils;
|
import com.onekeycall.videotablet.utils.TextUtils;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
@@ -121,11 +121,11 @@ public class User implements UserDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return nickname;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
this.nickname = username;
|
this.userId = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
package com.onekeycall.videotablet.filter;
|
package com.onekeycall.videotablet.filter;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.onekeycall.videotablet.entity.DeviceInfo;
|
||||||
|
import com.onekeycall.videotablet.gson.GsonUtils;
|
||||||
|
import com.onekeycall.videotablet.result.Result;
|
||||||
|
import com.onekeycall.videotablet.service.DeviceSnService;
|
||||||
import com.onekeycall.videotablet.utils.JwtUtil;
|
import com.onekeycall.videotablet.utils.JwtUtil;
|
||||||
|
import com.onekeycall.videotablet.utils.TextUtils;
|
||||||
|
import io.jsonwebtoken.lang.Collections;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@@ -11,60 +24,176 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
|||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
|
||||||
|
|
||||||
private final UserDetailsService userDetailsService;
|
private final UserDetailsService userDetailsService;
|
||||||
|
|
||||||
private final JwtUtil jwtUtil;
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
public JwtAuthenticationFilter(UserDetailsService userDetailsService, JwtUtil jwtUtil) {
|
private final DeviceSnService deviceSnService;
|
||||||
this.userDetailsService = userDetailsService;
|
|
||||||
this.jwtUtil = jwtUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||||
FilterChain filterChain) throws ServletException, IOException {
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
// 从请求头中获取Token
|
// 使用ContentCachingRequestWrapper包装请求以支持多次读取
|
||||||
String authorizationHeader = request.getHeader("Authorization");
|
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
|
||||||
|
|
||||||
String username = null;
|
String uripath = wrappedRequest.getRequestURI();
|
||||||
String jwt = null;
|
// 新增请求路径日志
|
||||||
|
logger.debug("Processing request: " + uripath);
|
||||||
|
|
||||||
|
if (uripath.startsWith("/user")) {
|
||||||
|
// 从请求头中获取Token
|
||||||
|
String authorizationHeader = wrappedRequest.getHeader("Authorization");
|
||||||
|
// 增强header检查日志
|
||||||
|
if (authorizationHeader == null) {
|
||||||
|
logger.debug("Missing Authorization header for: " + wrappedRequest.getRequestURI());
|
||||||
|
setResponse(response, Result.unAuthorized().message("Missing Authorization header"));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
logger.debug("Found Authorization header");
|
||||||
|
}
|
||||||
|
|
||||||
|
String username = null;
|
||||||
|
String jwt = null;
|
||||||
|
|
||||||
|
// 检查Authorization头是否存在且以Bearer开头
|
||||||
|
if (authorizationHeader.startsWith("Bearer ")) {
|
||||||
|
jwt = authorizationHeader.substring(7);
|
||||||
|
try {
|
||||||
|
username = jwtUtil.getUsernameFromToken(jwt);
|
||||||
|
logger.debug("Extracted username: " + username);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Token解析失败 | Token: " + jwt, e);
|
||||||
|
setResponse(response, Result.unAuthorized().message("Invalid credentials"));
|
||||||
|
return; // 重要!验证失败时终止过滤器链
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果获取到用户名且当前上下文没有认证信息
|
||||||
|
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
||||||
|
|
||||||
|
// 新增权限检查日志
|
||||||
|
logger.debug("Loaded user authorities: " + userDetails.getAuthorities());
|
||||||
|
|
||||||
|
// 验证Token
|
||||||
|
if (jwtUtil.validateToken(jwt, userDetails)) {
|
||||||
|
// 创建认证令牌
|
||||||
|
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(
|
||||||
|
userDetails, null, userDetails.getAuthorities());
|
||||||
|
|
||||||
|
usernamePasswordAuthenticationToken
|
||||||
|
.setDetails(new WebAuthenticationDetailsSource().buildDetails(wrappedRequest));
|
||||||
|
|
||||||
|
// 将认证信息存入上下文
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
|
||||||
|
logger.debug("Successfully authenticated user: " + username);
|
||||||
|
} else {
|
||||||
|
logger.warn("Token验证失败 | User: " + username);
|
||||||
|
setResponse(response, Result.unAuthorized().message("Token validation failed"));
|
||||||
|
return; // 重要!验证失败时终止过滤器链
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (uripath.startsWith("/sn")) {
|
||||||
|
// 设备绑定相关鉴权
|
||||||
|
String deviceToken = wrappedRequest.getHeader("Device-Token");
|
||||||
|
String deviceId = wrappedRequest.getHeader("Device-Id");
|
||||||
|
String deviceSig = wrappedRequest.getHeader("Device-Sig");
|
||||||
|
String sn = null;
|
||||||
|
|
||||||
|
// 1. 尝试从请求参数获取sn
|
||||||
|
sn = wrappedRequest.getParameter("sn");
|
||||||
|
logger.debug("sn from parameter: {}", sn);
|
||||||
|
|
||||||
|
// 2. 如果参数中没有sn且是POST请求,尝试从JSON请求体获取
|
||||||
|
if (sn == null && "POST".equalsIgnoreCase(wrappedRequest.getMethod())) {
|
||||||
|
String contentType = wrappedRequest.getContentType();
|
||||||
|
if (contentType != null && (contentType.contains("application/json") || contentType.contains("application/*+json"))) {
|
||||||
|
try {
|
||||||
|
String body = new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8);
|
||||||
|
if (!TextUtils.isEmpty(body)) {
|
||||||
|
JsonObject jsonObject = JsonParser.parseString(body).getAsJsonObject();
|
||||||
|
if (jsonObject.has("sn")) {
|
||||||
|
sn = jsonObject.get("sn").getAsString();
|
||||||
|
logger.debug("sn from request body: {}", sn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to parse sn from request body", e);
|
||||||
|
// 解析失败不中断,继续后续检查
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deviceToken == null || deviceId == null || deviceSig == null || sn == null) {
|
||||||
|
logger.warn("Missing device headers | DeviceToken: {} | DeviceID: {} | DeviceSig: {} | SN: {}", deviceToken, deviceId, deviceSig, sn);
|
||||||
|
setResponse(response, Result.unAuthorized().message("缺少设备认证信息"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceInfo deviceInfo = deviceSnService.findBySn(sn);
|
||||||
|
if (deviceInfo == null) {
|
||||||
|
logger.warn("SN not found | SN: {}", sn);
|
||||||
|
setResponse(response, Result.notFound().message("sn not found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceInfo.getBindSig().equals(deviceSig)) {
|
||||||
|
setResponse(response, Result.error().message("device sig not match"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(deviceInfo.getBindPhone())) {
|
||||||
|
setResponse(response, Result.error().message("sn not bind"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 检查Authorization头是否存在且以Bearer开头
|
|
||||||
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
|
|
||||||
jwt = authorizationHeader.substring(7);
|
|
||||||
try {
|
try {
|
||||||
username = jwtUtil.getUsernameFromToken(jwt);
|
// 调用SN验证服务(假设有SNService)
|
||||||
|
if (!jwtUtil.validateDeviceToken(deviceToken, deviceId, sn)) {
|
||||||
|
logger.warn("SN验证失败 | DeviceID: {} | SN: {}", deviceId, sn);
|
||||||
|
setResponse(response, Result.unAuthorized().message("设备验证失败"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.debug("SN验证成功 | DeviceID: {} | SN: {}", deviceId, sn);
|
||||||
|
|
||||||
|
// 3. 设置认证信息到SecurityContext,解决403问题
|
||||||
|
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||||
|
sn, null, Collections.emptyList()); // 可根据需要添加角色权限
|
||||||
|
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(wrappedRequest));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
|
logger.debug("Set authentication for SN: {}", sn);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("无法获取用户信息或Token无效", e);
|
logger.error("SN验证异常 | DeviceID: {} | SN: {}", deviceId, sn, e);
|
||||||
|
setResponse(response, Result.error().message("设备验证服务异常"));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 如果获取到用户名且当前上下文没有认证信息
|
|
||||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
|
||||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
|
||||||
|
|
||||||
// 验证Token
|
|
||||||
if (jwtUtil.validateToken(jwt, userDetails)) {
|
|
||||||
// 创建认证令牌
|
|
||||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
|
||||||
new UsernamePasswordAuthenticationToken(
|
|
||||||
userDetails, null, userDetails.getAuthorities());
|
|
||||||
|
|
||||||
usernamePasswordAuthenticationToken
|
|
||||||
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
|
||||||
|
|
||||||
// 将认证信息存入上下文
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 继续过滤器链
|
// 继续过滤器链
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(wrappedRequest, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setResponse(HttpServletResponse response, Result result) throws IOException {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
|
||||||
|
response.setStatus(HttpServletResponse.SC_OK); // 设置为200状态码
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().write(GsonUtils.toJSONString(result));
|
||||||
|
response.getWriter().flush();
|
||||||
|
response.getWriter().close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
153
src/main/java/com/onekeycall/videotablet/gson/GsonUtils.java
Normal file
153
src/main/java/com/onekeycall/videotablet/gson/GsonUtils.java
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package com.onekeycall.videotablet.gson;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.onekeycall.videotablet.filter.JwtAuthenticationFilter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
|
public class GsonUtils {
|
||||||
|
//https://blog.csdn.net/zte1055889498/article/details/122400299
|
||||||
|
static Logger logger = LoggerFactory.getLogger(GsonUtils.class);
|
||||||
|
|
||||||
|
public static JsonObject getJsonObject(String jsonString) {
|
||||||
|
JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject();
|
||||||
|
return jsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Gson gson;
|
||||||
|
|
||||||
|
static {
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
builder.registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory());
|
||||||
|
builder.registerTypeAdapter(Integer.class, new IntegerDefault0Adapter());
|
||||||
|
builder.registerTypeAdapter(int.class, new IntegerDefault0Adapter());
|
||||||
|
builder.disableHtmlEscaping();
|
||||||
|
builder.enableComplexMapKeySerialization();
|
||||||
|
// builder.excludeFieldsWithoutExposeAnnotation();
|
||||||
|
builder.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
gson = builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type makeJavaType(Type rawType, Type... typeArguments) {
|
||||||
|
return TypeToken.getParameterized(rawType, typeArguments).getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(Object value) {
|
||||||
|
if (Objects.isNull(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof String) {
|
||||||
|
return (String) value;
|
||||||
|
}
|
||||||
|
return toJSONString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toJSONString(Object value) {
|
||||||
|
return gson.toJson(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toPrettyString(Object value) {
|
||||||
|
return gson.newBuilder().setPrettyPrinting().create().toJson(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonElement fromJavaObject(Object value) {
|
||||||
|
JsonElement result = null;
|
||||||
|
if (Objects.nonNull(value) && (value instanceof String)) {
|
||||||
|
result = parseObject((String) value);
|
||||||
|
} else {
|
||||||
|
result = gson.toJsonTree(value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonElement parseObject(String content) {
|
||||||
|
return JsonParser.parseString(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonElement getJsonElement(JsonObject node, String name) {
|
||||||
|
return node.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonElement getJsonElement(JsonArray node, int index) {
|
||||||
|
return node.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T toJavaObject(JsonElement node, Class<T> clazz) {
|
||||||
|
return gson.fromJson(node, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T toJavaObject(JsonElement node, Type type) {
|
||||||
|
return gson.fromJson(node, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T toJavaObject(JsonElement node, TypeToken<?> typeToken) {
|
||||||
|
return toJavaObject(node, typeToken.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> List<E> toJavaList(JsonElement node, Class<E> clazz) {
|
||||||
|
return toJavaObject(node, makeJavaType(List.class, clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Object> toJavaList(JsonElement node) {
|
||||||
|
return toJavaObject(node, new TypeToken<List<Object>>() {
|
||||||
|
}.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> Map<String, V> toJavaMap(JsonElement node, Class<V> clazz) {
|
||||||
|
return toJavaObject(node, makeJavaType(Map.class, String.class, clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> toJavaMap(JsonElement node) {
|
||||||
|
return toJavaObject(node, new TypeToken<Map<String, Object>>() {
|
||||||
|
}.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T toJavaObject(String content, Class<T> clazz) {
|
||||||
|
JsonObject jsonObject = getJsonObject(content);
|
||||||
|
String jsonString = jsonObject.toString();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(jsonString, clazz);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("GsonUtils", "toJavaObject: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T toJavaObject(String content, Type type) {
|
||||||
|
return gson.fromJson(content, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T toJavaObject(String content, TypeToken<?> typeToken) {
|
||||||
|
return toJavaObject(content, typeToken.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> List<E> toJavaList(String content, Class<E> clazz) {
|
||||||
|
return toJavaObject(content, makeJavaType(List.class, clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Object> toJavaList(String content) {
|
||||||
|
return toJavaObject(content, new TypeToken<List<Object>>() {
|
||||||
|
}.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> Map<String, V> toJavaMap(String content, Class<V> clazz) {
|
||||||
|
return toJavaObject(content, makeJavaType(Map.class, String.class, clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> toJavaMap(String content) {
|
||||||
|
return toJavaObject(content, new TypeToken<Map<String, Object>>() {
|
||||||
|
}.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.onekeycall.videotablet.gson;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
public class IntegerDefault0Adapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
|
||||||
|
@Override
|
||||||
|
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
try {
|
||||||
|
if (json.getAsString().equals("")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return json.getAsInt();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new JsonSyntaxException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.onekeycall.videotablet.gson;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonToken;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class NullStringToEmptyAdapterFactory<T> implements TypeAdapterFactory {
|
||||||
|
@Override
|
||||||
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||||
|
|
||||||
|
Class<T> rawType = (Class<T>) type.getRawType();
|
||||||
|
if (rawType != String.class) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (TypeAdapter<T>) new StringAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StringAdapter extends TypeAdapter<String> {
|
||||||
|
@Override
|
||||||
|
public String read(JsonReader reader) throws IOException {
|
||||||
|
if (reader.peek() == JsonToken.NULL) {
|
||||||
|
reader.nextNull();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return reader.nextString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter writer, String value) throws IOException {
|
||||||
|
if (value == null) {
|
||||||
|
writer.nullValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writer.value(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -18,6 +18,8 @@ public class CustomWebSocketHandler extends TextWebSocketHandler {
|
|||||||
// 存储活跃会话(线程安全)
|
// 存储活跃会话(线程安全)
|
||||||
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
|
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterConnectionEstablished(WebSocketSession session) {
|
public void afterConnectionEstablished(WebSocketSession session) {
|
||||||
String sessionId = session.getId();
|
String sessionId = session.getId();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class AuthHandshakeInterceptor implements HandshakeInterceptor {
|
|||||||
String deviceId = request.getHeaders().getFirst("Device-ID");
|
String deviceId = request.getHeaders().getFirst("Device-ID");
|
||||||
String userId = request.getHeaders().getFirst("user_id");
|
String userId = request.getHeaders().getFirst("user_id");
|
||||||
|
|
||||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
if (authHeader == null || !authHeader.startsWith("Bearer ")||userId==null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String token = authHeader.substring(7); // 去掉 "Bearer " 前缀
|
String token = authHeader.substring(7); // 去掉 "Bearer " 前缀
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.onekeycall.videotablet.repository.UserRepository;
|
|||||||
import com.onekeycall.videotablet.result.Result;
|
import com.onekeycall.videotablet.result.Result;
|
||||||
import com.onekeycall.videotablet.utils.AESUtil;
|
import com.onekeycall.videotablet.utils.AESUtil;
|
||||||
import com.onekeycall.videotablet.utils.SecureIdGenerator;
|
import com.onekeycall.videotablet.utils.SecureIdGenerator;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@@ -17,18 +18,19 @@ import java.util.Date;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class UserService implements UserDetailsService {
|
public class UserService implements UserDetailsService {
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final RedisTemplate<String, Object> redisTemplate;
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
@Autowired
|
// @Autowired
|
||||||
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, RedisTemplate<String, Object> redisTemplate) {
|
// public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, RedisTemplate<String, Object> redisTemplate) {
|
||||||
this.userRepository = userRepository;
|
// this.userRepository = userRepository;
|
||||||
this.passwordEncoder = passwordEncoder;
|
// this.passwordEncoder = passwordEncoder;
|
||||||
this.redisTemplate = redisTemplate;
|
// this.redisTemplate = redisTemplate;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public User getUserByPhone(String phone) {
|
public User getUserByPhone(String phone) {
|
||||||
return userRepository.findUserByPhone(phone).orElse(null);
|
return userRepository.findUserByPhone(phone).orElse(null);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.onekeycall.videotablet.utils;
|
package com.onekeycall.videotablet.utils;
|
||||||
|
|
||||||
import com.onekeycall.videotablet.controller.LoginController;
|
|
||||||
import com.onekeycall.videotablet.dto.TokenPair;
|
import com.onekeycall.videotablet.dto.TokenPair;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.JwtException;
|
import io.jsonwebtoken.JwtException;
|
||||||
@@ -207,7 +206,7 @@ public class JwtUtil {
|
|||||||
// 从Token中获取所有声明
|
// 从Token中获取所有声明
|
||||||
private Claims getAllClaimsFromToken(String token) {
|
private Claims getAllClaimsFromToken(String token) {
|
||||||
return Jwts.parser()
|
return Jwts.parser()
|
||||||
.setSigningKey(secret)
|
.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
|
||||||
.build()
|
.build()
|
||||||
.parseClaimsJws(token)
|
.parseClaimsJws(token)
|
||||||
.getBody();
|
.getBody();
|
||||||
@@ -239,14 +238,14 @@ public class JwtUtil {
|
|||||||
// 验证Token
|
// 验证Token
|
||||||
public Boolean validateToken(String token, UserDetails userDetails) {
|
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||||
final String username = getUsernameFromToken(token);
|
final String username = getUsernameFromToken(token);
|
||||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
boolean isExpired = isTokenExpired(token);
|
||||||
|
boolean result = username.equals(userDetails.getUsername());
|
||||||
|
return result && !isExpired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Value("${jwt.tablet.secret}")
|
@Value("${jwt.tablet.secret}")
|
||||||
private String TABLET_SECRET;
|
private String TABLET_SECRET;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成设备签名(首次绑定)
|
* 生成设备签名(首次绑定)
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -56,5 +56,8 @@ logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger - %msg%n
|
|||||||
logging.logback.rollingpolicy.max-file-size=10MB
|
logging.logback.rollingpolicy.max-file-size=10MB
|
||||||
logging.logback.rollingpolicy.max-history=30
|
logging.logback.rollingpolicy.max-history=30
|
||||||
|
|
||||||
|
logging.level.com.onekeycall.videotablet.filter=DEBUG
|
||||||
|
logging.level.org.springframework.security=DEBUG
|
||||||
|
|
||||||
mybatis.type-aliases-package=com.onekeycall.videotablet.entity
|
mybatis.type-aliases-package=com.onekeycall.videotablet.entity
|
||||||
mybatis.mapperLocations=classpath:mapper/*.xml
|
mybatis.mapperLocations=classpath:mapper/*.xml
|
||||||
Reference in New Issue
Block a user