refactor: 添加 websocket 连接认证拦截器实现点对点指定用户发送消息;移除 easy-captcha 替换为 hutool-captcha验证码实现代码简化;重构认证接口控制层代码。
This commit is contained in:
22
pom.xml
22
pom.xml
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
<groupId>com.youlai</groupId>
|
<groupId>com.youlai</groupId>
|
||||||
<artifactId>youlai-boot</artifactId>
|
<artifactId>youlai-boot</artifactId>
|
||||||
<version>2.3.0</version>
|
<version>2.4.0</version>
|
||||||
<description>基于 SpringBoot3 快速构建的权限管理系统</description>
|
<description>基于Spring Boot 3快速构建的高效权限管理系统。</description>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -43,10 +43,6 @@
|
|||||||
|
|
||||||
<aliyun-sdk-oss.version>3.16.3</aliyun-sdk-oss.version>
|
<aliyun-sdk-oss.version>3.16.3</aliyun-sdk-oss.version>
|
||||||
|
|
||||||
<!-- 验证码 -->
|
|
||||||
<easy-captcha.version>1.6.2</easy-captcha.version>
|
|
||||||
<nashorn.version>15.4</nashorn.version>
|
|
||||||
|
|
||||||
<!-- redisson 分布式锁 -->
|
<!-- redisson 分布式锁 -->
|
||||||
<redisson.version>3.21.0</redisson.version>
|
<redisson.version>3.21.0</redisson.version>
|
||||||
</properties>
|
</properties>
|
||||||
@@ -189,20 +185,6 @@
|
|||||||
<version>${aliyun-sdk-oss.version}</version>
|
<version>${aliyun-sdk-oss.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 验证码 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.whvcse</groupId>
|
|
||||||
<artifactId>easy-captcha</artifactId>
|
|
||||||
<version>${easy-captcha.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Java8 之后JavaScript引擎nashorn被移除导致验证码解析报错-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.openjdk.nashorn</groupId>
|
|
||||||
<artifactId>nashorn-core</artifactId>
|
|
||||||
<version>${nashorn.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- redisson 分布式锁 -->
|
<!-- redisson 分布式锁 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.redisson</groupId>
|
<groupId>org.redisson</groupId>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.youlai.system.aspect;
|
package com.youlai.system.aspect;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.common.exception.BusinessException;
|
import com.youlai.system.exception.BusinessException;
|
||||||
import com.youlai.system.common.result.ResultCode;
|
import com.youlai.system.common.result.ResultCode;
|
||||||
import com.youlai.system.common.util.RequestUtils;
|
import com.youlai.system.util.RequestUtils;
|
||||||
import com.youlai.system.common.annotation.PreventDuplicateSubmit;
|
import com.youlai.system.common.annotation.PreventDuplicateSubmit;
|
||||||
import com.youlai.system.security.JwtTokenManager;
|
import com.youlai.system.security.JwtTokenManager;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ package com.youlai.system.common.constant;
|
|||||||
* Security 常量
|
* Security 常量
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
* @since 3.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public interface SecurityConstants {
|
public interface SecurityConstants {
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
package com.youlai.system.config;
|
|
||||||
|
|
||||||
import com.youlai.system.common.enums.CaptchaTypeEnum;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EasyCaptcha 配置类
|
|
||||||
*
|
|
||||||
* @author haoxr
|
|
||||||
* @since 2023/03/24
|
|
||||||
*/
|
|
||||||
@ConfigurationProperties(prefix = "easy-captcha")
|
|
||||||
@Configuration
|
|
||||||
@Data
|
|
||||||
public class CaptchaConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证码类型
|
|
||||||
*/
|
|
||||||
private CaptchaTypeEnum type = CaptchaTypeEnum.ARITHMETIC;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证码缓存过期时间(单位:秒)
|
|
||||||
*/
|
|
||||||
private long ttl = 120l;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证码内容长度
|
|
||||||
*/
|
|
||||||
private int length = 4;
|
|
||||||
/**
|
|
||||||
* 验证码宽度
|
|
||||||
*/
|
|
||||||
private int width = 120;
|
|
||||||
/**
|
|
||||||
* 验证码高度
|
|
||||||
*/
|
|
||||||
private int height = 36;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证码字体
|
|
||||||
*/
|
|
||||||
private String fontName = "Verdana";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字体风格
|
|
||||||
*/
|
|
||||||
private Integer fontStyle = Font.PLAIN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字体大小
|
|
||||||
*/
|
|
||||||
private int fontSize = 20;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -33,8 +33,6 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
|||||||
|
|
||||||
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
|
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
|
||||||
ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
|
ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
|
||||||
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
|
|
||||||
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
|
||||||
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
||||||
|
|
||||||
// 后台Long值传递给前端精度丢失问题(JS最大精度整数是Math.pow(2,53))
|
// 后台Long值传递给前端精度丢失问题(JS最大精度整数是Math.pow(2,53))
|
||||||
|
|||||||
@@ -1,28 +1,32 @@
|
|||||||
package com.youlai.system.config;
|
package com.youlai.system.config;
|
||||||
|
|
||||||
|
import com.youlai.system.interceptor.AuthChannelInterceptor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket 配置类
|
* WebSocket 配置
|
||||||
* <p>
|
|
||||||
* STOMP 不是一个传输协议,而是一个简单的文本消息协议,定义消息格式和交换规则
|
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
* @since 2.3.0
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnProperty(name = "system.config.websocket-enabled")// system.config.websocket-enabled = true 才会自动装配
|
@ConditionalOnProperty(name = "system.config.websocket-enabled")// system.config.websocket-enabled = true 才会自动装配
|
||||||
@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
|
@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||||
|
|
||||||
|
private final AuthChannelInterceptor authChannelInterceptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置和注册WebSocket端点(endpoints)
|
* 注册一个端点,客户端通过这个端点进行连接
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||||
@@ -35,9 +39,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置消息代理(Message Broker)
|
* 配置消息代理
|
||||||
* <p>
|
|
||||||
* 设置消息传输的规则和前缀
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||||
@@ -51,4 +53,14 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
|||||||
registry.setUserDestinationPrefix("/user");
|
registry.setUserDestinationPrefix("/user");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置客户端入站通道拦截器
|
||||||
|
*
|
||||||
|
* @param registration 通道注册器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||||
|
registration.interceptors(authChannelInterceptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,26 @@
|
|||||||
package com.youlai.system.controller;
|
package com.youlai.system.controller;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.common.result.Result;
|
import com.youlai.system.common.result.Result;
|
||||||
import com.youlai.system.common.util.RequestUtils;
|
|
||||||
import com.youlai.system.security.captcha.EasyCaptchaService;
|
|
||||||
import com.youlai.system.model.dto.CaptchaResult;
|
import com.youlai.system.model.dto.CaptchaResult;
|
||||||
import com.youlai.system.model.dto.LoginResult;
|
import com.youlai.system.model.dto.LoginResult;
|
||||||
import com.youlai.system.security.JwtTokenManager;
|
import com.youlai.system.service.AuthService;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Tag(name = "01.认证中心")
|
@Tag(name = "01.认证中心")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/auth")
|
@RequestMapping("/api/v1/auth")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
private final AuthenticationManager authenticationManager;
|
|
||||||
private final JwtTokenManager jwtTokenManager;
|
private final AuthService authService;
|
||||||
private final EasyCaptchaService easyCaptchaService;
|
|
||||||
private final RedisTemplate redisTemplate;
|
|
||||||
|
|
||||||
@Operation(summary = "登录")
|
@Operation(summary = "登录")
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
@@ -43,46 +28,21 @@ public class AuthController {
|
|||||||
@Parameter(description = "用户名", example = "admin") @RequestParam String username,
|
@Parameter(description = "用户名", example = "admin") @RequestParam String username,
|
||||||
@Parameter(description = "密码", example = "123456") @RequestParam String password
|
@Parameter(description = "密码", example = "123456") @RequestParam String password
|
||||||
) {
|
) {
|
||||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
|
LoginResult loginResult = authService.login(username, password);
|
||||||
username.toLowerCase().trim(),
|
|
||||||
password
|
|
||||||
);
|
|
||||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
|
||||||
// 生成token
|
|
||||||
String accessToken = jwtTokenManager.createToken(authentication);
|
|
||||||
LoginResult loginResult = LoginResult.builder()
|
|
||||||
.tokenType("Bearer")
|
|
||||||
.accessToken(accessToken)
|
|
||||||
.build();
|
|
||||||
return Result.success(loginResult);
|
return Result.success(loginResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "注销", security = {@SecurityRequirement(name = SecurityConstants.TOKEN_KEY)})
|
@Operation(summary = "注销", security = {@SecurityRequirement(name = SecurityConstants.TOKEN_KEY)})
|
||||||
@DeleteMapping("/logout")
|
@DeleteMapping("/logout")
|
||||||
public Result logout(HttpServletRequest request) {
|
public Result logout() {
|
||||||
String token = RequestUtils.resolveToken(request);
|
authService.logout();
|
||||||
if (StrUtil.isNotBlank(token)) {
|
return Result.success();
|
||||||
Claims claims = jwtTokenManager.getTokenClaims(token);
|
|
||||||
String jti = claims.get("jti", String.class);
|
|
||||||
|
|
||||||
Date expiration = claims.getExpiration();
|
|
||||||
if (expiration != null) {
|
|
||||||
// 有过期时间,在token有效时间内存入黑名单,超出时间移除黑名单节省内存占用
|
|
||||||
long ttl = (expiration.getTime() - System.currentTimeMillis());
|
|
||||||
redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
|
|
||||||
} else {
|
|
||||||
// 无过期时间,永久加入黑名单
|
|
||||||
redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SecurityContextHolder.clearContext();
|
|
||||||
return Result.success("注销成功");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取验证码")
|
@Operation(summary = "获取验证码")
|
||||||
@GetMapping("/captcha")
|
@GetMapping("/captcha")
|
||||||
public Result getCaptcha() {
|
public Result getCaptcha() {
|
||||||
CaptchaResult captcha = easyCaptchaService.getCaptcha();
|
CaptchaResult captcha = authService.getCaptcha();
|
||||||
return Result.success(captcha);
|
return Result.success(captcha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.youlai.system.common.constant.ExcelConstants;
|
import com.youlai.system.common.constant.ExcelConstants;
|
||||||
import com.youlai.system.common.result.PageResult;
|
import com.youlai.system.common.result.PageResult;
|
||||||
import com.youlai.system.common.result.Result;
|
import com.youlai.system.common.result.Result;
|
||||||
import com.youlai.system.common.util.ExcelUtils;
|
import com.youlai.system.util.ExcelUtils;
|
||||||
import com.youlai.system.common.annotation.PreventDuplicateSubmit;
|
import com.youlai.system.common.annotation.PreventDuplicateSubmit;
|
||||||
import com.youlai.system.listener.easyexcel.UserImportListener;
|
import com.youlai.system.listener.easyexcel.UserImportListener;
|
||||||
import com.youlai.system.model.vo.UserImportVO;
|
import com.youlai.system.model.vo.UserImportVO;
|
||||||
@@ -128,8 +128,8 @@ public class SysUserController {
|
|||||||
|
|
||||||
@Operation(summary = "获取当前登录用户信息", security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "获取当前登录用户信息", security = {@SecurityRequirement(name = "Authorization")})
|
||||||
@GetMapping("/me")
|
@GetMapping("/me")
|
||||||
public Result<UserInfoVO> getUserLoginInfo() {
|
public Result<UserInfoVO> getCurrentUserInfo() {
|
||||||
UserInfoVO userInfoVO = userService.getUserLoginInfo();
|
UserInfoVO userInfoVO = userService.getCurrentUserInfo();
|
||||||
return Result.success(userInfoVO);
|
return Result.success(userInfoVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
package com.youlai.system.controller.demo;
|
package com.youlai.system.controller.demo;
|
||||||
|
|
||||||
import com.youlai.system.common.result.Result;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.messaging.handler.annotation.DestinationVariable;
|
import org.springframework.messaging.handler.annotation.DestinationVariable;
|
||||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
import org.springframework.messaging.handler.annotation.SendTo;
|
import org.springframework.messaging.handler.annotation.SendTo;
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket 测试控制器
|
* WebSocket 测试控制器
|
||||||
*
|
*
|
||||||
@@ -24,28 +23,35 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class WebsocketController {
|
public class WebsocketController {
|
||||||
|
|
||||||
|
private final SimpMessagingTemplate messagingTemplate;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 广播发送消息
|
* 广播发送消息
|
||||||
*
|
*
|
||||||
* @param message 消息内容
|
* @param message 消息内容
|
||||||
*/
|
*/
|
||||||
@MessageMapping("/sendToAll")
|
@MessageMapping("/sendToAll")
|
||||||
@SendTo("/topic/all")
|
@SendTo("/topic/notice")
|
||||||
public String sendToAll(String message) {
|
public String sendToAll(String message) {
|
||||||
// 处理消息
|
return "System Notice: " + message;
|
||||||
return "Hello, " + message + "!";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 点对点发送消息
|
* 点对点发送消息
|
||||||
|
* <p>
|
||||||
|
* 模拟 张三 给 李四 发送消息场景
|
||||||
|
*
|
||||||
|
* @param principal 当前用户
|
||||||
|
* @param username 接收消息的用户
|
||||||
|
* @param message 消息内容
|
||||||
*/
|
*/
|
||||||
// 处理发送到"/app/sendToUser/{username}"的消息
|
|
||||||
@MessageMapping("/sendToUser/{username}")
|
@MessageMapping("/sendToUser/{username}")
|
||||||
// 将消息处理器的返回值发送到指定用户
|
//@SendToUser(value = "/queue/greeting")
|
||||||
@SendTo("/queue/user")
|
public void sendToUser(Principal principal, @DestinationVariable String username, String message) {
|
||||||
public String sendToUser(@DestinationVariable("username") String username, String message) {
|
log.info("sender:{};receiver:{}", username, principal.getName());
|
||||||
// 处理消息
|
messagingTemplate.convertAndSendToUser(username, "/queue/greeting", "Hello," + message);
|
||||||
return "Hello, " + username + ", your message is: " + message;
|
/// return "Hello, " + message;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public interface UserConverter {
|
|||||||
@Mappings({
|
@Mappings({
|
||||||
@Mapping(target = "userId", source = "id")
|
@Mapping(target = "userId", source = "id")
|
||||||
})
|
})
|
||||||
UserInfoVO entity2UserInfoVo(SysUser entity);
|
UserInfoVO toUserInfoVo(SysUser entity);
|
||||||
|
|
||||||
SysUser importVo2Entity(UserImportVO vo);
|
SysUser importVo2Entity(UserImportVO vo);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.exception;
|
package com.youlai.system.exception;
|
||||||
|
|
||||||
import com.youlai.system.common.result.IResultCode;
|
import com.youlai.system.common.result.IResultCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.exception;
|
package com.youlai.system.exception;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
@@ -3,8 +3,8 @@ package com.youlai.system.filter;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.common.result.ResultCode;
|
import com.youlai.system.common.result.ResultCode;
|
||||||
import com.youlai.system.common.util.RequestUtils;
|
import com.youlai.system.util.RequestUtils;
|
||||||
import com.youlai.system.common.util.ResponseUtils;
|
import com.youlai.system.util.ResponseUtils;
|
||||||
import com.youlai.system.security.JwtTokenManager;
|
import com.youlai.system.security.JwtTokenManager;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.common.result.ResultCode;
|
import com.youlai.system.common.result.ResultCode;
|
||||||
import com.youlai.system.common.util.ResponseUtils;
|
import com.youlai.system.util.ResponseUtils;
|
||||||
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;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
|
|||||||
import com.youlai.system.common.annotation.DataPermission;
|
import com.youlai.system.common.annotation.DataPermission;
|
||||||
import com.youlai.system.common.base.IBaseEnum;
|
import com.youlai.system.common.base.IBaseEnum;
|
||||||
import com.youlai.system.common.enums.DataScopeEnum;
|
import com.youlai.system.common.enums.DataScopeEnum;
|
||||||
import com.youlai.system.common.util.SecurityUtils;
|
import com.youlai.system.util.SecurityUtils;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.youlai.system.interceptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
|
import com.youlai.system.security.JwtTokenManager;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.messaging.Message;
|
||||||
|
import org.springframework.messaging.MessageChannel;
|
||||||
|
import org.springframework.messaging.simp.stomp.StompCommand;
|
||||||
|
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
|
||||||
|
import org.springframework.messaging.support.ChannelInterceptor;
|
||||||
|
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Websocket 连接认证拦截器
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthChannelInterceptor implements ChannelInterceptor {
|
||||||
|
|
||||||
|
private final JwtTokenManager jwtTokenManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接前监听
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @param channel
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||||
|
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
|
||||||
|
assert accessor != null;
|
||||||
|
|
||||||
|
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
|
||||||
|
// get token from header
|
||||||
|
String token = accessor.getFirstNativeHeader("Authorization");
|
||||||
|
// if token is not null
|
||||||
|
if (StrUtil.isNotBlank(token)) {
|
||||||
|
|
||||||
|
token = token.substring(SecurityConstants.TOKEN_PREFIX.length());
|
||||||
|
Claims claims = jwtTokenManager.parseAndValidateToken(token);
|
||||||
|
|
||||||
|
String username = claims.get("username", String.class);
|
||||||
|
// if the username is not null, assign it to the Principal.
|
||||||
|
if (StrUtil.isNotBlank(username)) {
|
||||||
|
Principal principal = () -> username;
|
||||||
|
accessor.setUser(principal);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ChannelInterceptor.super.preSend(message, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ public class UserInfoVO {
|
|||||||
@Schema(description="用户ID")
|
@Schema(description="用户ID")
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
@Schema(description="用户名")
|
||||||
|
private String username;
|
||||||
|
|
||||||
@Schema(description="用户昵称")
|
@Schema(description="用户昵称")
|
||||||
private String nickname;
|
private String nickname;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.youlai.system.security;
|
|||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.security.userdetails.SysUserDetails;
|
import com.youlai.system.security.model.SysUserDetails;
|
||||||
import io.jsonwebtoken.*;
|
import io.jsonwebtoken.*;
|
||||||
import io.jsonwebtoken.io.Decoders;
|
import io.jsonwebtoken.io.Decoders;
|
||||||
import io.jsonwebtoken.io.DecodingException;
|
import io.jsonwebtoken.io.DecodingException;
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
package com.youlai.system.security.captcha;
|
|
||||||
|
|
||||||
import com.wf.captcha.*;
|
|
||||||
import com.wf.captcha.base.Captcha;
|
|
||||||
import com.youlai.system.config.CaptchaConfig;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证码生成器
|
|
||||||
*
|
|
||||||
* @author haoxr
|
|
||||||
* @since 2023/03/24
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class EasyCaptchaProducer {
|
|
||||||
private final CaptchaConfig captchaConfig;
|
|
||||||
|
|
||||||
public Captcha getCaptcha() {
|
|
||||||
Captcha captcha;
|
|
||||||
int width = captchaConfig.getWidth();
|
|
||||||
int height = captchaConfig.getHeight();
|
|
||||||
int length = captchaConfig.getLength();
|
|
||||||
String fontName = captchaConfig.getFontName();
|
|
||||||
|
|
||||||
switch (captchaConfig.getType()) {
|
|
||||||
case ARITHMETIC:
|
|
||||||
captcha = new ArithmeticCaptcha(width, height);
|
|
||||||
//固定设置为两位,图片为算数运算表达式
|
|
||||||
captcha.setLen(2);
|
|
||||||
break;
|
|
||||||
case CHINESE:
|
|
||||||
captcha = new ChineseCaptcha(width, height);
|
|
||||||
captcha.setLen(length);
|
|
||||||
break;
|
|
||||||
case CHINESE_GIF:
|
|
||||||
captcha = new ChineseGifCaptcha(width, height);
|
|
||||||
captcha.setLen(length);
|
|
||||||
break;
|
|
||||||
case GIF:
|
|
||||||
captcha = new GifCaptcha(width, height);//最后一位是位数
|
|
||||||
captcha.setLen(length);
|
|
||||||
break;
|
|
||||||
case SPEC:
|
|
||||||
captcha = new SpecCaptcha(width, height);
|
|
||||||
captcha.setLen(length);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("验证码配置信息错误!正确配置查看 CaptchaTypeEnum ");
|
|
||||||
}
|
|
||||||
captcha.setFont(new Font(fontName, captchaConfig.getFontStyle(), captchaConfig.getFontSize()));
|
|
||||||
return captcha;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package com.youlai.system.security.captcha;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.IdUtil;
|
|
||||||
import com.wf.captcha.base.Captcha;
|
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
|
||||||
import com.youlai.system.config.CaptchaConfig;
|
|
||||||
import com.youlai.system.model.dto.CaptchaResult;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EasyCaptcha 业务类
|
|
||||||
*
|
|
||||||
* @author haoxr
|
|
||||||
* @since 2023/03/24
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class EasyCaptchaService {
|
|
||||||
|
|
||||||
private final EasyCaptchaProducer easyCaptchaProducer;
|
|
||||||
|
|
||||||
private final RedisTemplate redisTemplate;
|
|
||||||
|
|
||||||
private final CaptchaConfig captchaConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取验证码
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public CaptchaResult getCaptcha() {
|
|
||||||
// 获取验证码
|
|
||||||
Captcha captcha = easyCaptchaProducer.getCaptcha();
|
|
||||||
String captchaText = captcha.text(); // 验证码文本
|
|
||||||
String captchaBase64 = captcha.toBase64(); // 验证码图片Base64字符串
|
|
||||||
|
|
||||||
// 验证码文本缓存至Redis,用于登录校验
|
|
||||||
String verifyCodeKey = IdUtil.fastSimpleUUID();
|
|
||||||
redisTemplate.opsForValue().set(SecurityConstants.VERIFY_CODE_CACHE_PREFIX + verifyCodeKey, captchaText,
|
|
||||||
captchaConfig.getTtl(), TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
return CaptchaResult.builder()
|
|
||||||
.verifyCodeKey(verifyCodeKey)
|
|
||||||
.verifyCodeBase64(captchaBase64)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.youlai.system.security.exception;
|
package com.youlai.system.security.exception;
|
||||||
|
|
||||||
import com.youlai.system.common.result.ResultCode;
|
import com.youlai.system.common.result.ResultCode;
|
||||||
import com.youlai.system.common.util.ResponseUtils;
|
import com.youlai.system.util.ResponseUtils;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.youlai.system.security.exception;
|
package com.youlai.system.security.exception;
|
||||||
|
|
||||||
import com.youlai.system.common.result.ResultCode;
|
import com.youlai.system.common.result.ResultCode;
|
||||||
import com.youlai.system.common.util.ResponseUtils;
|
import com.youlai.system.util.ResponseUtils;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.security.userdetails;
|
package com.youlai.system.security.model;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
@@ -3,12 +3,11 @@ package com.youlai.system.security.service;
|
|||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.common.util.SecurityUtils;
|
import com.youlai.system.util.SecurityUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.PatternMatchUtils;
|
import org.springframework.util.PatternMatchUtils;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.youlai.system.security.userdetails;
|
package com.youlai.system.security.service;
|
||||||
|
|
||||||
import com.youlai.system.model.dto.UserAuthInfo;
|
import com.youlai.system.model.dto.UserAuthInfo;
|
||||||
|
import com.youlai.system.security.model.SysUserDetails;
|
||||||
import com.youlai.system.service.SysUserService;
|
import com.youlai.system.service.SysUserService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
34
src/main/java/com/youlai/system/service/AuthService.java
Normal file
34
src/main/java/com/youlai/system/service/AuthService.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package com.youlai.system.service;
|
||||||
|
|
||||||
|
import com.youlai.system.model.dto.CaptchaResult;
|
||||||
|
import com.youlai.system.model.dto.LoginResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证服务接口
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public interface AuthService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param password 密码
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
LoginResult login(String username, String password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登出
|
||||||
|
*/
|
||||||
|
void logout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码
|
||||||
|
*
|
||||||
|
* @return 验证码
|
||||||
|
*/
|
||||||
|
CaptchaResult getCaptcha();
|
||||||
|
}
|
||||||
@@ -98,5 +98,5 @@ public interface SysUserService extends IService<SysUser> {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
UserInfoVO getUserLoginInfo();
|
UserInfoVO getCurrentUserInfo();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.youlai.system.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.CaptchaUtil;
|
||||||
|
import cn.hutool.captcha.GifCaptcha;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
|
import com.youlai.system.service.AuthService;
|
||||||
|
import com.youlai.system.util.RequestUtils;
|
||||||
|
import com.youlai.system.model.dto.CaptchaResult;
|
||||||
|
import com.youlai.system.model.dto.LoginResult;
|
||||||
|
import com.youlai.system.security.JwtTokenManager;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthServiceImpl implements AuthService {
|
||||||
|
|
||||||
|
private final AuthenticationManager authenticationManager;
|
||||||
|
private final JwtTokenManager jwtTokenManager;
|
||||||
|
private final RedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param password 密码
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LoginResult login(String username, String password) {
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(username.toLowerCase().trim(), password);
|
||||||
|
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||||
|
String accessToken = jwtTokenManager.createToken(authentication);
|
||||||
|
return LoginResult.builder()
|
||||||
|
.tokenType("Bearer")
|
||||||
|
.accessToken(accessToken)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登出
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void logout() {
|
||||||
|
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||||
|
String token = RequestUtils.resolveToken(request);
|
||||||
|
if (StrUtil.isNotBlank(token)) {
|
||||||
|
Claims claims = jwtTokenManager.getTokenClaims(token);
|
||||||
|
String jti = claims.get("jti", String.class);
|
||||||
|
Date expiration = claims.getExpiration();
|
||||||
|
if (expiration != null) {
|
||||||
|
long ttl = expiration.getTime() - System.currentTimeMillis();
|
||||||
|
redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
|
||||||
|
} else {
|
||||||
|
redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码
|
||||||
|
*
|
||||||
|
* @return 验证码
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CaptchaResult getCaptcha() {
|
||||||
|
// 获取验证码
|
||||||
|
GifCaptcha captcha = CaptchaUtil.createGifCaptcha(120, 36, 4); // 宽、高、位数
|
||||||
|
String captchaCode = captcha.getCode(); // 验证码
|
||||||
|
String captchaBase64 = captcha.getImageBase64Data(); // 验证码图片Base64
|
||||||
|
|
||||||
|
// 验证码文本缓存至Redis,用于登录校验
|
||||||
|
String verifyCodeKey = IdUtil.fastSimpleUUID();
|
||||||
|
redisTemplate.opsForValue().set(SecurityConstants.VERIFY_CODE_CACHE_PREFIX + verifyCodeKey, captchaCode,
|
||||||
|
120, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
return CaptchaResult.builder()
|
||||||
|
.verifyCodeKey(verifyCodeKey)
|
||||||
|
.verifyCodeBase64(captchaBase64)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import com.youlai.system.model.vo.RolePageVO;
|
|||||||
import com.youlai.system.service.SysRoleMenuService;
|
import com.youlai.system.service.SysRoleMenuService;
|
||||||
import com.youlai.system.service.SysRoleService;
|
import com.youlai.system.service.SysRoleService;
|
||||||
import com.youlai.system.service.SysUserRoleService;
|
import com.youlai.system.service.SysUserRoleService;
|
||||||
import com.youlai.system.common.util.SecurityUtils;
|
import com.youlai.system.util.SecurityUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.common.constant.SystemConstants;
|
import com.youlai.system.common.constant.SystemConstants;
|
||||||
import com.youlai.system.converter.UserConverter;
|
import com.youlai.system.converter.UserConverter;
|
||||||
import com.youlai.system.common.util.SecurityUtils;
|
import com.youlai.system.util.SecurityUtils;
|
||||||
import com.youlai.system.mapper.SysUserMapper;
|
import com.youlai.system.mapper.SysUserMapper;
|
||||||
import com.youlai.system.model.dto.UserAuthInfo;
|
import com.youlai.system.model.dto.UserAuthInfo;
|
||||||
import com.youlai.system.model.bo.UserBO;
|
import com.youlai.system.model.bo.UserBO;
|
||||||
@@ -233,18 +233,22 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public UserInfoVO getUserLoginInfo() {
|
public UserInfoVO getCurrentUserInfo() {
|
||||||
// 登录用户entity
|
|
||||||
|
String username = SecurityUtils.getUser().getUsername(); // 登录用户名
|
||||||
|
|
||||||
|
// 获取登录用户基础信息
|
||||||
SysUser user = this.getOne(new LambdaQueryWrapper<SysUser>()
|
SysUser user = this.getOne(new LambdaQueryWrapper<SysUser>()
|
||||||
.eq(SysUser::getUsername, SecurityUtils.getUser().getUsername())
|
.eq(SysUser::getUsername, username)
|
||||||
.select(
|
.select(
|
||||||
SysUser::getId,
|
SysUser::getId,
|
||||||
|
SysUser::getUsername,
|
||||||
SysUser::getNickname,
|
SysUser::getNickname,
|
||||||
SysUser::getAvatar
|
SysUser::getAvatar
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// entity->VO
|
// entity->VO
|
||||||
UserInfoVO userInfoVO = userConverter.entity2UserInfoVo(user);
|
UserInfoVO userInfoVO = userConverter.toUserInfoVo(user);
|
||||||
|
|
||||||
// 用户角色集合
|
// 用户角色集合
|
||||||
Set<String> roles = SecurityUtils.getRoles();
|
Set<String> roles = SecurityUtils.getRoles();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.util;
|
package com.youlai.system.util;
|
||||||
|
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.youlai.system.listener.easyexcel.MyAnalysisEventListener;
|
import com.youlai.system.listener.easyexcel.MyAnalysisEventListener;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.util;
|
package com.youlai.system.util;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.youlai.system.common.util;
|
package com.youlai.system.util;
|
||||||
|
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.youlai.system.common.result.Result;
|
import com.youlai.system.common.result.Result;
|
||||||
@@ -14,7 +14,7 @@ import java.io.IOException;
|
|||||||
* 响应工具类
|
* 响应工具类
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
* @since 2022/10/18
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public class ResponseUtils {
|
public class ResponseUtils {
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.youlai.system.common.util;
|
package com.youlai.system.util;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.common.constant.SystemConstants;
|
import com.youlai.system.common.constant.SystemConstants;
|
||||||
import com.youlai.system.security.userdetails.SysUserDetails;
|
import com.youlai.system.security.model.SysUserDetails;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@@ -81,8 +81,6 @@ oss:
|
|||||||
# 存储桶名称
|
# 存储桶名称
|
||||||
bucket-name: default
|
bucket-name: default
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# springdoc配置: https://springdoc.org/properties.html
|
# springdoc配置: https://springdoc.org/properties.html
|
||||||
springdoc:
|
springdoc:
|
||||||
swagger-ui:
|
swagger-ui:
|
||||||
@@ -92,13 +90,6 @@ springdoc:
|
|||||||
api-docs:
|
api-docs:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
# 验证码配置
|
|
||||||
easy-captcha:
|
|
||||||
# 验证码类型: arithmetic-算术
|
|
||||||
type: arithmetic
|
|
||||||
# 验证码有效时间(单位:秒)
|
|
||||||
ttl: 120
|
|
||||||
|
|
||||||
# xxl-job 定时任务配置
|
# xxl-job 定时任务配置
|
||||||
xxl:
|
xxl:
|
||||||
job:
|
job:
|
||||||
|
|||||||
@@ -86,14 +86,6 @@ springdoc:
|
|||||||
api-docs:
|
api-docs:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
# 验证码配置
|
|
||||||
easy-captcha:
|
|
||||||
enable: true
|
|
||||||
# 验证码类型: arithmetic-算术
|
|
||||||
type: arithmetic
|
|
||||||
# 验证码有效时间(单位:秒)
|
|
||||||
ttl: 120
|
|
||||||
|
|
||||||
# xxl-job 定时任务配置
|
# xxl-job 定时任务配置
|
||||||
xxl:
|
xxl:
|
||||||
job:
|
job:
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.youlai.system.controller;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
class WebsocketControllerTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSendToAll() throws Exception {
|
|
||||||
String message = "Hello! I'm server. This is a topic message.";
|
|
||||||
|
|
||||||
mockMvc.perform(
|
|
||||||
post("/messages/sendToAll")
|
|
||||||
.param("message", message)
|
|
||||||
)
|
|
||||||
.andExpect(status().isOk());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSendToUser() throws Exception {
|
|
||||||
|
|
||||||
Long userId = 2L;// 系统管理员用户ID=2
|
|
||||||
|
|
||||||
mockMvc.perform(post("/messages/sendToUser/{userId}", userId)
|
|
||||||
.param("message", "Hello! I'm server. This is a point-to-point message.")
|
|
||||||
)
|
|
||||||
//.content(JSONUtil.toJsonStr(webSocketMessage))
|
|
||||||
//.contentType(MediaType.APPLICATION_JSON))
|
|
||||||
.andExpect(status().isOk());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user