From cd4f14e643d9118a04d5a44ad2fbf54131eb586d Mon Sep 17 00:00:00 2001 From: haoxr <1490493387@qq.com> Date: Wed, 13 Dec 2023 18:33:11 +0800 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20=E5=8C=85=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=8Cwebsocket=20=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++- pom.xml | 2 +- .../system/common/util/SecurityUtils.java | 2 +- .../MybatisConfig.java} | 8 +- .../security => }/config/SecurityConfig.java | 10 +- .../youlai/system/config/WebSocketConfig.java | 111 ++++++++++++++++++ .../xxljob => config}/XxlJobConfig.java | 2 +- .../controller/WebsocketController.java | 13 +- .../mybatis}/annotation/DataPermission.java | 2 +- .../handler/MyDataPermissionHandler.java | 4 +- .../mybatis}/handler/MyMetaObjectHandler.java | 2 +- .../exception/MyAccessDeniedHandler.java | 2 +- .../exception/MyAuthenticationEntryPoint.java | 2 +- .../security/jwt/JwtTokenFilter.java | 2 +- .../security/jwt/JwtTokenProvider.java | 4 +- .../security/model/SysUserDetails.java | 2 +- .../security/service/PermissionService.java | 2 +- .../service/SysUserDetailsService.java | 4 +- .../youlai/system/mapper/SysDeptMapper.java | 2 +- .../youlai/system/mapper/SysUserMapper.java | 2 +- .../{SocketMessage.java => ChatMessage.java} | 2 +- .../aspect/DuplicateSubmitAspect.java | 2 +- .../plugin/websocket/WebSocketConfig.java | 63 ---------- .../WebsocketChannelInterceptor.java | 55 --------- .../system/service/impl/AuthServiceImpl.java | 3 +- .../service/impl/SysRoleServiceImpl.java | 2 +- 26 files changed, 160 insertions(+), 159 deletions(-) rename src/main/java/com/youlai/system/{base/mybatisplus/config/MybatisPlusConfig.java => config/MybatisConfig.java} (86%) rename src/main/java/com/youlai/system/{base/security => }/config/SecurityConfig.java (93%) create mode 100644 src/main/java/com/youlai/system/config/WebSocketConfig.java rename src/main/java/com/youlai/system/{plugin/xxljob => config}/XxlJobConfig.java (97%) rename src/main/java/com/youlai/system/{base/mybatisplus => core/mybatis}/annotation/DataPermission.java (90%) rename src/main/java/com/youlai/system/{base/mybatisplus => core/mybatis}/handler/MyDataPermissionHandler.java (96%) rename src/main/java/com/youlai/system/{base/mybatisplus => core/mybatis}/handler/MyMetaObjectHandler.java (94%) rename src/main/java/com/youlai/system/{base => core}/security/exception/MyAccessDeniedHandler.java (94%) rename src/main/java/com/youlai/system/{base => core}/security/exception/MyAuthenticationEntryPoint.java (95%) rename src/main/java/com/youlai/system/{base => core}/security/jwt/JwtTokenFilter.java (97%) rename src/main/java/com/youlai/system/{base => core}/security/jwt/JwtTokenProvider.java (98%) rename src/main/java/com/youlai/system/{base => core}/security/model/SysUserDetails.java (98%) rename src/main/java/com/youlai/system/{base => core}/security/service/PermissionService.java (98%) rename src/main/java/com/youlai/system/{base => core}/security/service/SysUserDetailsService.java (89%) rename src/main/java/com/youlai/system/model/dto/{SocketMessage.java => ChatMessage.java} (91%) delete mode 100644 src/main/java/com/youlai/system/plugin/websocket/WebSocketConfig.java delete mode 100644 src/main/java/com/youlai/system/plugin/websocket/WebsocketChannelInterceptor.java diff --git a/README.md b/README.md index f72dbcff..db95f5d8 100644 --- a/README.md +++ b/README.md @@ -45,17 +45,22 @@ youlai-boot ├── mysql5 # MySQL5 脚本 ├── mysql8 # MySQL8 脚本 ├── src # 源码目录 - ├── base # 核心模块 - ├── mybatisplus # Mybatis-Plus 配置和插件 - ├── security # Spring Security 安全配置和扩展 ├── common # 公共模块 ├── config # 自动装配配置 ├── CorsConfig # 跨域共享配置 - ├── RedisConfig # Redis 配置 + ├── MybatisConfig # Mybatis 自动装配配置 + ├── RedisCacheConfig # Redis 缓存自动装配配置 + ├── RedisConfig # Redis 自动装配配置 + ├── SecurityConfig # Spring Security 自动装配配置 ├── SwaggerConfig # API 接口文档配置 ├── WebMvcConfig # WebMvc 配置 + ├── WebSocketConfig # WebSocket 自动装配配置 + ├── XxlJobConfig # XXL-JOB 自动装配配置 ├── controller # 控制层 ├── converter # MapStruct转换器 + ├── core # 核心模块 + ├── security # Spring Security 安全配置和扩展 + ├── mybatis # Mybatis-Plus 配置和插件 ├── filter # 过滤器 ├── RequestLogFilter # 请求日志过滤器 ├── VerifyCodeFilter # 验证码过滤器 @@ -68,6 +73,7 @@ youlai-boot ├── vo # 视图对象 ├── mapper # 数据库访问层 ├── plugin # 插件(可选) + ├── captcha # 验证码插件,用于生成验证码 ├── dupsubmit # 防重提交插件,用于防止表单重复提交 ├── easyexcel # EasyExcel 插件,Excel 文件的读写 ├── rabbitmq # RabbitMQ 插件,消息队列交互 diff --git a/pom.xml b/pom.xml index 5f41cbf9..97cdaefb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.youlai youlai-boot - 2.5.0 + 2.5.1 基于 Java 17 + SpringBoot 3 构建的权限管理系统。 diff --git a/src/main/java/com/youlai/system/common/util/SecurityUtils.java b/src/main/java/com/youlai/system/common/util/SecurityUtils.java index 9a64a377..48a7631f 100644 --- a/src/main/java/com/youlai/system/common/util/SecurityUtils.java +++ b/src/main/java/com/youlai/system/common/util/SecurityUtils.java @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import com.youlai.system.common.constant.SystemConstants; -import com.youlai.system.base.security.model.SysUserDetails; +import com.youlai.system.core.security.model.SysUserDetails; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; diff --git a/src/main/java/com/youlai/system/base/mybatisplus/config/MybatisPlusConfig.java b/src/main/java/com/youlai/system/config/MybatisConfig.java similarity index 86% rename from src/main/java/com/youlai/system/base/mybatisplus/config/MybatisPlusConfig.java rename to src/main/java/com/youlai/system/config/MybatisConfig.java index 018d9ef0..74274c85 100644 --- a/src/main/java/com/youlai/system/base/mybatisplus/config/MybatisPlusConfig.java +++ b/src/main/java/com/youlai/system/config/MybatisConfig.java @@ -1,12 +1,12 @@ -package com.youlai.system.base.mybatisplus.config; +package com.youlai.system.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import com.youlai.system.base.mybatisplus.handler.MyDataPermissionHandler; -import com.youlai.system.base.mybatisplus.handler.MyMetaObjectHandler; +import com.youlai.system.core.mybatis.handler.MyDataPermissionHandler; +import com.youlai.system.core.mybatis.handler.MyMetaObjectHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -19,7 +19,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; */ @Configuration @EnableTransactionManagement -public class MybatisPlusConfig { +public class MybatisConfig { /** * 分页插件和数据权限插件 diff --git a/src/main/java/com/youlai/system/base/security/config/SecurityConfig.java b/src/main/java/com/youlai/system/config/SecurityConfig.java similarity index 93% rename from src/main/java/com/youlai/system/base/security/config/SecurityConfig.java rename to src/main/java/com/youlai/system/config/SecurityConfig.java index f970e35c..01896f04 100644 --- a/src/main/java/com/youlai/system/base/security/config/SecurityConfig.java +++ b/src/main/java/com/youlai/system/config/SecurityConfig.java @@ -1,11 +1,11 @@ -package com.youlai.system.base.security.config; +package com.youlai.system.config; import com.youlai.system.common.constant.SecurityConstants; -import com.youlai.system.base.security.exception.MyAccessDeniedHandler; -import com.youlai.system.base.security.exception.MyAuthenticationEntryPoint; -import com.youlai.system.base.security.jwt.JwtTokenFilter; +import com.youlai.system.core.security.exception.MyAccessDeniedHandler; +import com.youlai.system.core.security.exception.MyAuthenticationEntryPoint; +import com.youlai.system.core.security.jwt.JwtTokenFilter; import com.youlai.system.filter.VerifyCodeFilter; -import com.youlai.system.base.security.jwt.JwtTokenProvider; +import com.youlai.system.core.security.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/youlai/system/config/WebSocketConfig.java b/src/main/java/com/youlai/system/config/WebSocketConfig.java new file mode 100644 index 00000000..c44e0fe3 --- /dev/null +++ b/src/main/java/com/youlai/system/config/WebSocketConfig.java @@ -0,0 +1,111 @@ +package com.youlai.system.config; + +import cn.hutool.core.util.StrUtil; +import com.youlai.system.core.security.jwt.JwtTokenProvider; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.config.ChannelRegistration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +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.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +/** + * WebSocket 配置 + * + * @author haoxr + * @since 2.4.0 + */ +@Configuration +@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递 +@RequiredArgsConstructor +@Slf4j +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + private final JwtTokenProvider jwtTokenProvider; + + /** + * 注册一个端点,客户端通过这个端点进行连接 + */ + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry + .addEndpoint("/ws") // 注册了一个 /ws 的端点 + .setAllowedOriginPatterns("*") // 允许跨域的 WebSocket 连接 + .withSockJS(); // 启用 SockJS (浏览器不支持WebSocket,SockJS 将会提供兼容性支持) + registry.addEndpoint("/ws-app").setAllowedOriginPatterns("*"); // 注册了一个 /ws-app 的端点,支持 uni-app 的 ws 连接协议 + } + + + /** + * 配置消息代理 + */ + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + // 客户端发送消息的请求前缀 + registry.setApplicationDestinationPrefixes("/app"); + + // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送 + registry.enableSimpleBroker("/topic", "/queue"); + + // 服务端通知客户端的前缀,可以不设置,默认为user + registry.setUserDestinationPrefix("/user"); + } + + + /** + * 配置客户端入站通道拦截器 + * + * @param registration 通道注册器 + */ + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(new ChannelInterceptor() { + @Override + public Message preSend(Message message, MessageChannel channel) { + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + + // 如果 StompHeaderAccessor 为 null,说明不是 STOMP 消息,直接放行 + if (accessor == null) { + return ChannelInterceptor.super.preSend(message, channel); + } + + // 如果是连接请求(CONNECT 命令),从请求头中取出 token 并设置到认证信息中 + if (StompCommand.CONNECT.equals(accessor.getCommand())) { + // 从连接头中提取授权令牌 + String bearerToken = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION); + + // 验证令牌格式并提取用户信息 + if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) { + try { + // 移除 "Bearer " 前缀 + String tokenWithoutPrefix = bearerToken.substring(7); + String username = jwtTokenProvider.getUsername(tokenWithoutPrefix); + + // 如果用户名有效,设置用户到访问器中 + if (StrUtil.isNotBlank(username)) { + accessor.setUser(() -> username); + return message; + } + } catch (Exception e) { + // 异常处理,可能是解析令牌失败 + log.error("Failed to process authentication token.", e); + } + } + } + + // 如果不是连接命令或授权失败,继续执行默认逻辑 + return ChannelInterceptor.super.preSend(message, channel); + } + }); + } + +} diff --git a/src/main/java/com/youlai/system/plugin/xxljob/XxlJobConfig.java b/src/main/java/com/youlai/system/config/XxlJobConfig.java similarity index 97% rename from src/main/java/com/youlai/system/plugin/xxljob/XxlJobConfig.java rename to src/main/java/com/youlai/system/config/XxlJobConfig.java index d326dd6e..a0f802ed 100644 --- a/src/main/java/com/youlai/system/plugin/xxljob/XxlJobConfig.java +++ b/src/main/java/com/youlai/system/config/XxlJobConfig.java @@ -1,4 +1,4 @@ -package com.youlai.system.plugin.xxljob; +package com.youlai.system.config; import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/youlai/system/controller/WebsocketController.java b/src/main/java/com/youlai/system/controller/WebsocketController.java index 18d724cb..ed82a66e 100644 --- a/src/main/java/com/youlai/system/controller/WebsocketController.java +++ b/src/main/java/com/youlai/system/controller/WebsocketController.java @@ -1,6 +1,6 @@ package com.youlai.system.controller; -import com.youlai.system.model.dto.SocketMessage; +import com.youlai.system.model.dto.ChatMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.handler.annotation.DestinationVariable; @@ -48,11 +48,14 @@ public class WebsocketController { * @param message 消息内容 */ @MessageMapping("/sendToUser/{username}") - //@SendToUser(value = "/queue/greeting") public void sendToUser(Principal principal, @DestinationVariable String username, String message) { - log.info("sender:{};receiver:{}", principal.getName(), username); - messagingTemplate.convertAndSendToUser(username, "/queue/greeting", new SocketMessage(principal.getName(), message)); - /// return "Hello, " + message; + + String sender = principal.getName(); // 发送人 + String receiver = username; // 接收人 + + log.info("发送人:{}; 接收人:{}", sender, receiver); + // 发送消息给指定用户 /user/{username}/queue/greeting + messagingTemplate.convertAndSendToUser(receiver, "/queue/greeting", new ChatMessage(sender, message)); } } diff --git a/src/main/java/com/youlai/system/base/mybatisplus/annotation/DataPermission.java b/src/main/java/com/youlai/system/core/mybatis/annotation/DataPermission.java similarity index 90% rename from src/main/java/com/youlai/system/base/mybatisplus/annotation/DataPermission.java rename to src/main/java/com/youlai/system/core/mybatis/annotation/DataPermission.java index edd372a9..63e73277 100644 --- a/src/main/java/com/youlai/system/base/mybatisplus/annotation/DataPermission.java +++ b/src/main/java/com/youlai/system/core/mybatis/annotation/DataPermission.java @@ -1,4 +1,4 @@ -package com.youlai.system.base.mybatisplus.annotation; +package com.youlai.system.core.mybatis.annotation; import java.lang.annotation.*; diff --git a/src/main/java/com/youlai/system/base/mybatisplus/handler/MyDataPermissionHandler.java b/src/main/java/com/youlai/system/core/mybatis/handler/MyDataPermissionHandler.java similarity index 96% rename from src/main/java/com/youlai/system/base/mybatisplus/handler/MyDataPermissionHandler.java rename to src/main/java/com/youlai/system/core/mybatis/handler/MyDataPermissionHandler.java index 82a275a8..b39f9b10 100644 --- a/src/main/java/com/youlai/system/base/mybatisplus/handler/MyDataPermissionHandler.java +++ b/src/main/java/com/youlai/system/core/mybatis/handler/MyDataPermissionHandler.java @@ -1,9 +1,9 @@ -package com.youlai.system.base.mybatisplus.handler; +package com.youlai.system.core.mybatis.handler; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler; -import com.youlai.system.base.mybatisplus.annotation.DataPermission; +import com.youlai.system.core.mybatis.annotation.DataPermission; import com.youlai.system.common.base.IBaseEnum; import com.youlai.system.common.enums.DataScopeEnum; import com.youlai.system.common.util.SecurityUtils; diff --git a/src/main/java/com/youlai/system/base/mybatisplus/handler/MyMetaObjectHandler.java b/src/main/java/com/youlai/system/core/mybatis/handler/MyMetaObjectHandler.java similarity index 94% rename from src/main/java/com/youlai/system/base/mybatisplus/handler/MyMetaObjectHandler.java rename to src/main/java/com/youlai/system/core/mybatis/handler/MyMetaObjectHandler.java index 68bcfc40..9cf7d3d5 100644 --- a/src/main/java/com/youlai/system/base/mybatisplus/handler/MyMetaObjectHandler.java +++ b/src/main/java/com/youlai/system/core/mybatis/handler/MyMetaObjectHandler.java @@ -1,4 +1,4 @@ -package com.youlai.system.base.mybatisplus.handler; +package com.youlai.system.core.mybatis.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; diff --git a/src/main/java/com/youlai/system/base/security/exception/MyAccessDeniedHandler.java b/src/main/java/com/youlai/system/core/security/exception/MyAccessDeniedHandler.java similarity index 94% rename from src/main/java/com/youlai/system/base/security/exception/MyAccessDeniedHandler.java rename to src/main/java/com/youlai/system/core/security/exception/MyAccessDeniedHandler.java index 60bb2623..c7979301 100644 --- a/src/main/java/com/youlai/system/base/security/exception/MyAccessDeniedHandler.java +++ b/src/main/java/com/youlai/system/core/security/exception/MyAccessDeniedHandler.java @@ -1,4 +1,4 @@ -package com.youlai.system.base.security.exception; +package com.youlai.system.core.security.exception; import com.youlai.system.common.result.ResultCode; import com.youlai.system.common.util.ResponseUtils; diff --git a/src/main/java/com/youlai/system/base/security/exception/MyAuthenticationEntryPoint.java b/src/main/java/com/youlai/system/core/security/exception/MyAuthenticationEntryPoint.java similarity index 95% rename from src/main/java/com/youlai/system/base/security/exception/MyAuthenticationEntryPoint.java rename to src/main/java/com/youlai/system/core/security/exception/MyAuthenticationEntryPoint.java index a5fd840c..2a82ab8a 100644 --- a/src/main/java/com/youlai/system/base/security/exception/MyAuthenticationEntryPoint.java +++ b/src/main/java/com/youlai/system/core/security/exception/MyAuthenticationEntryPoint.java @@ -1,4 +1,4 @@ -package com.youlai.system.base.security.exception; +package com.youlai.system.core.security.exception; import com.youlai.system.common.result.ResultCode; import com.youlai.system.common.util.ResponseUtils; diff --git a/src/main/java/com/youlai/system/base/security/jwt/JwtTokenFilter.java b/src/main/java/com/youlai/system/core/security/jwt/JwtTokenFilter.java similarity index 97% rename from src/main/java/com/youlai/system/base/security/jwt/JwtTokenFilter.java rename to src/main/java/com/youlai/system/core/security/jwt/JwtTokenFilter.java index a116f1d7..5fba6042 100644 --- a/src/main/java/com/youlai/system/base/security/jwt/JwtTokenFilter.java +++ b/src/main/java/com/youlai/system/core/security/jwt/JwtTokenFilter.java @@ -1,4 +1,4 @@ -package com.youlai.system.base.security.jwt; +package com.youlai.system.core.security.jwt; import com.youlai.system.common.result.ResultCode; import com.youlai.system.common.util.ResponseUtils; diff --git a/src/main/java/com/youlai/system/base/security/jwt/JwtTokenProvider.java b/src/main/java/com/youlai/system/core/security/jwt/JwtTokenProvider.java similarity index 98% rename from src/main/java/com/youlai/system/base/security/jwt/JwtTokenProvider.java rename to src/main/java/com/youlai/system/core/security/jwt/JwtTokenProvider.java index 26725297..2f34d6c3 100644 --- a/src/main/java/com/youlai/system/base/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/youlai/system/core/security/jwt/JwtTokenProvider.java @@ -1,8 +1,8 @@ -package com.youlai.system.base.security.jwt; +package com.youlai.system.core.security.jwt; import cn.hutool.core.convert.Convert; import com.youlai.system.common.constant.JwtClaimConstants; -import com.youlai.system.base.security.model.SysUserDetails; +import com.youlai.system.core.security.model.SysUserDetails; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; diff --git a/src/main/java/com/youlai/system/base/security/model/SysUserDetails.java b/src/main/java/com/youlai/system/core/security/model/SysUserDetails.java similarity index 98% rename from src/main/java/com/youlai/system/base/security/model/SysUserDetails.java rename to src/main/java/com/youlai/system/core/security/model/SysUserDetails.java index 124c60e2..578d3a2a 100644 --- a/src/main/java/com/youlai/system/base/security/model/SysUserDetails.java +++ b/src/main/java/com/youlai/system/core/security/model/SysUserDetails.java @@ -1,4 +1,4 @@ -package com.youlai.system.base.security.model; +package com.youlai.system.core.security.model; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; diff --git a/src/main/java/com/youlai/system/base/security/service/PermissionService.java b/src/main/java/com/youlai/system/core/security/service/PermissionService.java similarity index 98% rename from src/main/java/com/youlai/system/base/security/service/PermissionService.java rename to src/main/java/com/youlai/system/core/security/service/PermissionService.java index ced6e3e8..b13b8784 100644 --- a/src/main/java/com/youlai/system/base/security/service/PermissionService.java +++ b/src/main/java/com/youlai/system/core/security/service/PermissionService.java @@ -1,4 +1,4 @@ -package com.youlai.system.base.security.service; +package com.youlai.system.core.security.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; diff --git a/src/main/java/com/youlai/system/base/security/service/SysUserDetailsService.java b/src/main/java/com/youlai/system/core/security/service/SysUserDetailsService.java similarity index 89% rename from src/main/java/com/youlai/system/base/security/service/SysUserDetailsService.java rename to src/main/java/com/youlai/system/core/security/service/SysUserDetailsService.java index 7d575451..f2f8b26f 100644 --- a/src/main/java/com/youlai/system/base/security/service/SysUserDetailsService.java +++ b/src/main/java/com/youlai/system/core/security/service/SysUserDetailsService.java @@ -1,6 +1,6 @@ -package com.youlai.system.base.security.service; +package com.youlai.system.core.security.service; -import com.youlai.system.base.security.model.SysUserDetails; +import com.youlai.system.core.security.model.SysUserDetails; import com.youlai.system.model.dto.UserAuthInfo; import com.youlai.system.service.SysUserService; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/youlai/system/mapper/SysDeptMapper.java b/src/main/java/com/youlai/system/mapper/SysDeptMapper.java index 73bf622d..3b4aaa97 100644 --- a/src/main/java/com/youlai/system/mapper/SysDeptMapper.java +++ b/src/main/java/com/youlai/system/mapper/SysDeptMapper.java @@ -3,7 +3,7 @@ package com.youlai.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.Constants; -import com.youlai.system.base.mybatisplus.annotation.DataPermission; +import com.youlai.system.core.mybatis.annotation.DataPermission; import com.youlai.system.model.entity.SysDept; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; diff --git a/src/main/java/com/youlai/system/mapper/SysUserMapper.java b/src/main/java/com/youlai/system/mapper/SysUserMapper.java index ebb59ca2..45ffca7e 100644 --- a/src/main/java/com/youlai/system/mapper/SysUserMapper.java +++ b/src/main/java/com/youlai/system/mapper/SysUserMapper.java @@ -2,7 +2,7 @@ package com.youlai.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.youlai.system.base.mybatisplus.annotation.DataPermission; +import com.youlai.system.core.mybatis.annotation.DataPermission; import com.youlai.system.model.bo.UserBO; import com.youlai.system.model.entity.SysUser; import com.youlai.system.model.dto.UserAuthInfo; diff --git a/src/main/java/com/youlai/system/model/dto/SocketMessage.java b/src/main/java/com/youlai/system/model/dto/ChatMessage.java similarity index 91% rename from src/main/java/com/youlai/system/model/dto/SocketMessage.java rename to src/main/java/com/youlai/system/model/dto/ChatMessage.java index cda51641..6dd6de32 100644 --- a/src/main/java/com/youlai/system/model/dto/SocketMessage.java +++ b/src/main/java/com/youlai/system/model/dto/ChatMessage.java @@ -10,7 +10,7 @@ import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor -public class SocketMessage { +public class ChatMessage { /** * 发送者 diff --git a/src/main/java/com/youlai/system/plugin/dupsubmit/aspect/DuplicateSubmitAspect.java b/src/main/java/com/youlai/system/plugin/dupsubmit/aspect/DuplicateSubmitAspect.java index 90a08fd6..23777e92 100644 --- a/src/main/java/com/youlai/system/plugin/dupsubmit/aspect/DuplicateSubmitAspect.java +++ b/src/main/java/com/youlai/system/plugin/dupsubmit/aspect/DuplicateSubmitAspect.java @@ -4,7 +4,7 @@ import cn.hutool.core.util.StrUtil; import com.youlai.system.plugin.dupsubmit.annotation.PreventDuplicateSubmit; import com.youlai.system.common.result.ResultCode; import com.youlai.system.common.exception.BusinessException; -import com.youlai.system.base.security.jwt.JwtTokenProvider; +import com.youlai.system.core.security.jwt.JwtTokenProvider; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/youlai/system/plugin/websocket/WebSocketConfig.java b/src/main/java/com/youlai/system/plugin/websocket/WebSocketConfig.java deleted file mode 100644 index 85f865c8..00000000 --- a/src/main/java/com/youlai/system/plugin/websocket/WebSocketConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.youlai.system.plugin.websocket; - -import lombok.RequiredArgsConstructor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.simp.config.ChannelRegistration; -import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; -import org.springframework.web.socket.config.annotation.StompEndpointRegistry; -import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; - -/** - * WebSocket 配置 - * - * @author haoxr - * @since 2.4.0 - */ -@Configuration -@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递 -@RequiredArgsConstructor -public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { - - private final WebsocketChannelInterceptor websocketChannelInterceptor; - - /** - * 注册一个端点,客户端通过这个端点进行连接 - */ - @Override - public void registerStompEndpoints(StompEndpointRegistry registry) { - registry - .addEndpoint("/ws") // 注册了一个 /ws 的端点 - .setAllowedOriginPatterns("*") // 允许跨域的 WebSocket 连接 - .withSockJS(); // 启用 SockJS (浏览器不支持WebSocket,SockJS 将会提供兼容性支持) - registry.addEndpoint("/ws-app").setAllowedOriginPatterns("*"); // 注册了一个 /ws-app 的端点,支持 uni-app 的 ws 连接协议 - } - - - /** - * 配置消息代理 - */ - @Override - public void configureMessageBroker(MessageBrokerRegistry registry) { - // 客户端发送消息的请求前缀 - registry.setApplicationDestinationPrefixes("/app"); - - // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送 - registry.enableSimpleBroker("/topic", "/queue"); - - // 服务端通知客户端的前缀,可以不设置,默认为user - registry.setUserDestinationPrefix("/user"); - } - - - /** - * 配置客户端入站通道拦截器 - * - * @param registration 通道注册器 - */ - @Override - public void configureClientInboundChannel(ChannelRegistration registration) { - registration.interceptors(websocketChannelInterceptor); - } -} diff --git a/src/main/java/com/youlai/system/plugin/websocket/WebsocketChannelInterceptor.java b/src/main/java/com/youlai/system/plugin/websocket/WebsocketChannelInterceptor.java deleted file mode 100644 index bbfca0f2..00000000 --- a/src/main/java/com/youlai/system/plugin/websocket/WebsocketChannelInterceptor.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.youlai.system.plugin.websocket; - -import cn.hutool.core.util.StrUtil; -import com.youlai.system.base.security.jwt.JwtTokenProvider; -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 WebsocketChannelInterceptor implements ChannelInterceptor { - - private final JwtTokenProvider jwtTokenProvider; - - /** - * 连接前监听 - * - * @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())) { - String bearerToken = accessor.getFirstNativeHeader("Authorization"); - if (StrUtil.isNotBlank(bearerToken)) { - bearerToken = bearerToken.substring(7); // remove "Bearer " - String username = jwtTokenProvider.getUsername(bearerToken); - if (StrUtil.isNotBlank(username)) { - Principal principal = () -> username; - accessor.setUser(principal); - return message; - } - } - } - return ChannelInterceptor.super.preSend(message, channel); - } - -} diff --git a/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java b/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java index fedac225..a34e4ed5 100644 --- a/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java @@ -1,10 +1,9 @@ package com.youlai.system.service.impl; -import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.system.common.constant.CacheConstants; -import com.youlai.system.base.security.jwt.JwtTokenProvider; +import com.youlai.system.core.security.jwt.JwtTokenProvider; import com.youlai.system.model.dto.CaptchaResult; import com.youlai.system.model.dto.LoginResult; import com.youlai.system.plugin.captcha.CaptchaGenerator; diff --git a/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java b/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java index 7561592d..4771cbba 100644 --- a/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java +++ b/src/main/java/com/youlai/system/service/impl/SysRoleServiceImpl.java @@ -10,7 +10,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.system.common.constant.SystemConstants; import com.youlai.system.common.model.Option; import com.youlai.system.converter.RoleConverter; -import com.youlai.system.base.security.service.PermissionService; +import com.youlai.system.core.security.service.PermissionService; import com.youlai.system.mapper.SysRoleMapper; import com.youlai.system.model.entity.SysRole; import com.youlai.system.model.entity.SysRoleMenu; From 974f32b268872d234ad14a03ef72899b386ad4c0 Mon Sep 17 00:00:00 2001 From: haoxr <1490493387@qq.com> Date: Thu, 14 Dec 2023 23:36:41 +0800 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20websocket=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../youlai/system/config/WebSocketConfig.java | 17 +++++------------ .../system/controller/WebsocketController.java | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/youlai/system/config/WebSocketConfig.java b/src/main/java/com/youlai/system/config/WebSocketConfig.java index c44e0fe3..9ef3eccc 100644 --- a/src/main/java/com/youlai/system/config/WebSocketConfig.java +++ b/src/main/java/com/youlai/system/config/WebSocketConfig.java @@ -63,6 +63,8 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { /** * 配置客户端入站通道拦截器 + *

+ * 添加 ChannelInterceptor 拦截器,用于在消息发送前,从请求头中获取 token 并解析出用户信息(username),用于点对点发送消息给指定用户 * * @param registration 通道注册器 */ @@ -72,37 +74,28 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public Message preSend(Message message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - - // 如果 StompHeaderAccessor 为 null,说明不是 STOMP 消息,直接放行 - if (accessor == null) { - return ChannelInterceptor.super.preSend(message, channel); - } - // 如果是连接请求(CONNECT 命令),从请求头中取出 token 并设置到认证信息中 - if (StompCommand.CONNECT.equals(accessor.getCommand())) { + if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) { // 从连接头中提取授权令牌 String bearerToken = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION); // 验证令牌格式并提取用户信息 if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) { try { - // 移除 "Bearer " 前缀 + // 移除 "Bearer " 前缀,从令牌中提取用户信息(username), 并设置到认证信息中 String tokenWithoutPrefix = bearerToken.substring(7); String username = jwtTokenProvider.getUsername(tokenWithoutPrefix); - // 如果用户名有效,设置用户到访问器中 if (StrUtil.isNotBlank(username)) { accessor.setUser(() -> username); return message; } } catch (Exception e) { - // 异常处理,可能是解析令牌失败 log.error("Failed to process authentication token.", e); } } } - - // 如果不是连接命令或授权失败,继续执行默认逻辑 + // 不是连接请求,直接放行 return ChannelInterceptor.super.preSend(message, channel); } }); diff --git a/src/main/java/com/youlai/system/controller/WebsocketController.java b/src/main/java/com/youlai/system/controller/WebsocketController.java index ed82a66e..0b0d1fe0 100644 --- a/src/main/java/com/youlai/system/controller/WebsocketController.java +++ b/src/main/java/com/youlai/system/controller/WebsocketController.java @@ -54,7 +54,7 @@ public class WebsocketController { String receiver = username; // 接收人 log.info("发送人:{}; 接收人:{}", sender, receiver); - // 发送消息给指定用户 /user/{username}/queue/greeting + // 发送消息给指定用户,拼接后路径 /user/{receiver}/queue/greeting messagingTemplate.convertAndSendToUser(receiver, "/queue/greeting", new ChatMessage(sender, message)); } From 31a2fbeb422f113f4ede8bde53d1f2a670bc4c39 Mon Sep 17 00:00:00 2001 From: haoxr <1490493387@qq.com> Date: Fri, 15 Dec 2023 23:43:09 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20knife4j=20=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=8F=82=E6=95=B0=E6=8E=92=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E9=9C=80=E9=89=B4=E6=9D=83=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../youlai/system/config/SwaggerConfig.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/youlai/system/config/SwaggerConfig.java b/src/main/java/com/youlai/system/config/SwaggerConfig.java index e557f3e8..86a91b54 100644 --- a/src/main/java/com/youlai/system/config/SwaggerConfig.java +++ b/src/main/java/com/youlai/system/config/SwaggerConfig.java @@ -5,9 +5,12 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.GlobalOpenApiCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; /** @@ -19,19 +22,26 @@ import org.springframework.http.HttpHeaders; * @since 2023/2/17 */ @Configuration +@Slf4j +@RequiredArgsConstructor public class SwaggerConfig { + private final Environment environment; + /** * 接口信息 */ @Bean public OpenAPI openApi() { + + String appVersion = environment.getProperty("project.version", "1.0.0"); + return new OpenAPI() .info(new Info() .title("系统接口文档") - .version("2.4.0") + .version(appVersion) ) - // 全局安全校验项,也可以在对应的controller上加注解SecurityRequirement + // 配置全局鉴权参数-Authorize .components(new Components() .addSecuritySchemes(HttpHeaders.AUTHORIZATION, new SecurityScheme() @@ -41,17 +51,34 @@ public class SwaggerConfig { .scheme("Bearer") .bearerFormat("JWT") ) - ) - .addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION)) ; + ); } + /** + * 全局自定义扩展 + *

+ * 在OpenAPI规范中,Operation 是一个表示 API 端点(Endpoint)或操作的对象。 + * 每个路径(Path)对象可以包含一个或多个 Operation 对象,用于描述与该路径相关联的不同 HTTP 方法(例如 GET、POST、PUT 等)。 + */ @Bean public GlobalOpenApiCustomizer globalOpenApiCustomizer() { - return openApi -> openApi.getPaths().values() - .stream() - .flatMap(pathItem -> pathItem.readOperations().stream()) - .forEach(operation -> operation.security(openApi.getSecurity())); + return openApi -> { + // 全局添加鉴权参数 + if (openApi.getPaths() != null) { + openApi.getPaths().forEach((s, pathItem) -> { + // 登录接口/验证码不需要添加鉴权参数 + if (s.equals("/api/v1/auth/login") || s.equals("/api/v1/auth/captcha")) { + return; + } + // 接口添加鉴权参数 + pathItem.readOperations() + .forEach(operation -> + operation.addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION)) + ); + }); + } + }; } } From a1a1a64f7376492fba35a74396c3ba258befd579 Mon Sep 17 00:00:00 2001 From: haoxr <1490493387@qq.com> Date: Sat, 16 Dec 2023 14:10:42 +0800 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=E9=AA=8C=E8=AF=81=E7=A0=81?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/plugin/captcha/CaptchaConfig.java | 55 ++++++++++++ .../plugin/captcha/CaptchaGenerator.java | 87 ------------------- .../system/plugin/captcha/CaptchaModel.java | 24 ----- .../plugin/captcha/CaptchaProperties.java | 5 ++ .../websocket/WebSocketEventListener.java | 68 --------------- .../system/service/impl/AuthServiceImpl.java | 39 +++++++-- src/main/resources/application-dev.yml | 8 +- src/main/resources/application-prod.yml | 8 +- 8 files changed, 103 insertions(+), 191 deletions(-) create mode 100644 src/main/java/com/youlai/system/plugin/captcha/CaptchaConfig.java delete mode 100644 src/main/java/com/youlai/system/plugin/captcha/CaptchaGenerator.java delete mode 100644 src/main/java/com/youlai/system/plugin/captcha/CaptchaModel.java delete mode 100644 src/main/java/com/youlai/system/plugin/websocket/WebSocketEventListener.java diff --git a/src/main/java/com/youlai/system/plugin/captcha/CaptchaConfig.java b/src/main/java/com/youlai/system/plugin/captcha/CaptchaConfig.java new file mode 100644 index 00000000..daf6fd35 --- /dev/null +++ b/src/main/java/com/youlai/system/plugin/captcha/CaptchaConfig.java @@ -0,0 +1,55 @@ +package com.youlai.system.plugin.captcha; + +import cn.hutool.captcha.*; +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.captcha.generator.MathGenerator; +import cn.hutool.captcha.generator.RandomGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.awt.*; + +/** + * 验证码自动装配配置 + * + * @author haoxr + * @since 2023/11/24 + */ +@Configuration +public class CaptchaConfig { + + @Autowired + private CaptchaProperties captchaProperties; + + /** + * 验证码文字生成器 + * + * @return CodeGenerator + */ + @Bean + public CodeGenerator codeGenerator() { + String codeType = captchaProperties.getCode().getType(); + int codeLength = captchaProperties.getCode().getLength(); + if ("math".equalsIgnoreCase(codeType)) { + return new MathGenerator(codeLength); + } else if ("random".equalsIgnoreCase(codeType)) { + return new RandomGenerator(codeLength); + } else { + throw new IllegalArgumentException("Invalid captcha generator type: " + codeType); + } + } + + /** + * 验证码字体 + */ + @Bean + public Font captchaFont() { + String fontName = captchaProperties.getFont().getName(); + int fontSize = captchaProperties.getFont().getSize(); + int fontWight = captchaProperties.getFont().getWeight(); + return new Font(fontName, fontWight, fontSize); + } + + +} diff --git a/src/main/java/com/youlai/system/plugin/captcha/CaptchaGenerator.java b/src/main/java/com/youlai/system/plugin/captcha/CaptchaGenerator.java deleted file mode 100644 index 6ea870df..00000000 --- a/src/main/java/com/youlai/system/plugin/captcha/CaptchaGenerator.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.youlai.system.plugin.captcha; - -import cn.hutool.captcha.AbstractCaptcha; -import cn.hutool.captcha.CircleCaptcha; -import cn.hutool.captcha.generator.CodeGenerator; -import cn.hutool.captcha.generator.MathGenerator; -import cn.hutool.captcha.generator.RandomGenerator; -import com.youlai.system.model.dto.CaptchaResult; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * 验证码自动装配配置 - * - * @author haoxr - * @since 2023/11/24 - */ -@Configuration -public class CaptchaGenerator { - - @Autowired - private CaptchaProperties captchaProperties; - - /** - * 验证码文字生成器 - * - * @return CodeGenerator - */ - @Bean - public CodeGenerator codeGenerator() { - String codeType = captchaProperties.getCode().getType(); - int codeLength = captchaProperties.getCode().getLength(); - if ("math".equalsIgnoreCase(codeType)) { - return new MathGenerator(codeLength); - } else if ("random".equalsIgnoreCase(codeType)) { - return new RandomGenerator(codeLength); - } else { - throw new IllegalArgumentException("Invalid captcha generator type: " + codeType); - } - } - - - /** - * 生成验证码 - * - * @return CaptchaModel 验证码 - */ - public CaptchaModel generate() { - AbstractCaptcha captcha = getCaptcha(); - captcha.createCode(); - return new CaptchaModel(captcha.getCode(), captcha.getImageBase64Data()); - } - - /** - * 验证码类 - * - * @return AbstractCaptcha - */ - public AbstractCaptcha getCaptcha() { - AbstractCaptcha captcha = null; - - String type = captchaProperties.getType(); - int width = captchaProperties.getWidth(); - int height = captchaProperties.getHeight(); - int interfereCount = captchaProperties.getInterfereCount(); - int codeLength = captchaProperties.getCode().getLength(); - - - if ("circle".equalsIgnoreCase(type)) { - captcha = new CircleCaptcha(width, height, codeLength, interfereCount); - } else if ("gif".equalsIgnoreCase(type)) { - return null; - } else if ("line".equalsIgnoreCase(type)) { - return null; - } else if ("shear".equalsIgnoreCase(type)) { - return null; - } else { - throw new IllegalArgumentException("Invalid captcha type: " + type); - } - - captcha.setGenerator(codeGenerator()); - return captcha; - } - - -} diff --git a/src/main/java/com/youlai/system/plugin/captcha/CaptchaModel.java b/src/main/java/com/youlai/system/plugin/captcha/CaptchaModel.java deleted file mode 100644 index cdbc9e44..00000000 --- a/src/main/java/com/youlai/system/plugin/captcha/CaptchaModel.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.youlai.system.plugin.captcha; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 验证码对象 - */ -@Data -@AllArgsConstructor -@NoArgsConstructor -public class CaptchaModel { - /** - * 验证码编码 - */ - private String code; - - /** - * 验证码图片Base64 - */ - private String base64; - -} diff --git a/src/main/java/com/youlai/system/plugin/captcha/CaptchaProperties.java b/src/main/java/com/youlai/system/plugin/captcha/CaptchaProperties.java index 1653a708..9b32f1a5 100644 --- a/src/main/java/com/youlai/system/plugin/captcha/CaptchaProperties.java +++ b/src/main/java/com/youlai/system/plugin/captcha/CaptchaProperties.java @@ -34,6 +34,11 @@ public class CaptchaProperties { */ private int interfereCount; + /** + * 文本透明度 + */ + private Float textAlpha; + /** * 验证码过期时间,单位:秒 */ diff --git a/src/main/java/com/youlai/system/plugin/websocket/WebSocketEventListener.java b/src/main/java/com/youlai/system/plugin/websocket/WebSocketEventListener.java deleted file mode 100644 index e4189d0b..00000000 --- a/src/main/java/com/youlai/system/plugin/websocket/WebSocketEventListener.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.youlai.system.plugin.websocket; - -import cn.hutool.json.JSONUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; -import org.springframework.web.socket.messaging.SessionConnectedEvent; -import org.springframework.web.socket.messaging.SessionDisconnectEvent; -import org.springframework.web.socket.messaging.SessionSubscribeEvent; -import org.springframework.web.socket.messaging.SessionUnsubscribeEvent; - -import java.security.Principal; - -/** - * Websocket 客户端事件监听器 - * - * @author haoxr - * @since 2023/10/10 - */ -@Component -@Slf4j -public class WebSocketEventListener { - - /** - * 监听客户端连接事件 - * - * @param event 连接事件对象 - */ - @EventListener - public void handleWebSocketConnectListener(SessionConnectedEvent event) { - Principal user = event.getUser(); - - log.info("客户端连接成功"); - } - - /** - * 监听客户端断开连接事件 - * - * @param event 断开连接事件对象 - */ - @EventListener - public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { - log.info("客户端断开连接"); - } - - - /** - * 监听客户端订阅事件 - * - * @param event 订阅事件对象 - */ - @EventListener - public void handleSubscription(SessionSubscribeEvent event) { - log.info("客户端订阅:{}", JSONUtil.toJsonStr(event.getMessage())); - } - - /** - * 监听客户端取消订阅事件 - * - * @param event 取消订阅事件对象 - */ - @EventListener - public void handleUnSubscription(SessionUnsubscribeEvent event) { - log.info("客户端取消订阅:{}", JSONUtil.toJsonStr(event.getMessage())); - } - - -} diff --git a/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java b/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java index a34e4ed5..ae00fb76 100644 --- a/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/youlai/system/service/impl/AuthServiceImpl.java @@ -1,13 +1,14 @@ package com.youlai.system.service.impl; +import cn.hutool.captcha.AbstractCaptcha; +import cn.hutool.captcha.CaptchaUtil; +import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.system.common.constant.CacheConstants; import com.youlai.system.core.security.jwt.JwtTokenProvider; import com.youlai.system.model.dto.CaptchaResult; import com.youlai.system.model.dto.LoginResult; -import com.youlai.system.plugin.captcha.CaptchaGenerator; -import com.youlai.system.plugin.captcha.CaptchaModel; import com.youlai.system.plugin.captcha.CaptchaProperties; import com.youlai.system.service.AuthService; import io.jsonwebtoken.Claims; @@ -22,6 +23,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import java.awt.*; import java.util.Date; import java.util.concurrent.TimeUnit; @@ -38,7 +40,8 @@ public class AuthServiceImpl implements AuthService { private final AuthenticationManager authenticationManager; private final StringRedisTemplate redisTemplate; private final JwtTokenProvider jwtTokenProvider; - private final CaptchaGenerator captchaGenerator; + private final CodeGenerator codeGenerator; + private final Font captchaFont; private final CaptchaProperties captchaProperties; /** @@ -88,16 +91,40 @@ public class AuthServiceImpl implements AuthService { */ @Override public CaptchaResult getCaptcha() { - CaptchaModel captchaModel = captchaGenerator.generate(); + + String type = captchaProperties.getType(); + int width = captchaProperties.getWidth(); + int height = captchaProperties.getHeight(); + int interfereCount = captchaProperties.getInterfereCount(); + int codeLength = captchaProperties.getCode().getLength(); + + AbstractCaptcha captcha; + if ("circle".equalsIgnoreCase(type)) { + captcha = CaptchaUtil.createCircleCaptcha(width, height, codeLength, interfereCount); + } else if ("gif".equalsIgnoreCase(type)) { + captcha = CaptchaUtil.createGifCaptcha(width, height, codeLength); + } else if ("line".equalsIgnoreCase(type)) { + captcha = CaptchaUtil.createLineCaptcha(width, height, codeLength, interfereCount); + } else if ("shear".equalsIgnoreCase(type)) { + captcha = CaptchaUtil.createShearCaptcha(width, height, codeLength, interfereCount); + } else { + throw new IllegalArgumentException("Invalid captcha type: " + type); + } + captcha.setGenerator(codeGenerator); + captcha.setTextAlpha(captchaProperties.getTextAlpha()); + captcha.setFont(captchaFont); + + String captchaCode = captcha.getCode(); + String imageBase64Data = captcha.getImageBase64Data(); // 验证码文本缓存至Redis,用于登录校验 String captchaKey = IdUtil.fastSimpleUUID(); - redisTemplate.opsForValue().set(CacheConstants.CAPTCHA_CODE_PREFIX + captchaKey, captchaModel.getCode(), + redisTemplate.opsForValue().set(CacheConstants.CAPTCHA_CODE_PREFIX + captchaKey,captchaCode, captchaProperties.getExpireSeconds(), TimeUnit.SECONDS); return CaptchaResult.builder() .captchaKey(captchaKey) - .captchaBase64(captchaModel.getBase64()) + .captchaBase64(imageBase64Data) .build(); } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 67c32380..e6dff2eb 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -136,10 +136,12 @@ captcha: # 验证码高度 height: 40 # 验证码干扰元素个数 - interfere-count: 3 + interfere-count: 4 + # 文本透明度(0.0-1.0) + text-alpha: 0.8 # 验证码字符配置 code: - # 验证码字符类型 math-算术 |random-随机字符 + # 验证码字符类型 math-算术|random-随机字符 type: math # 验证码字符长度,type=算术时,表示运算位数(1:个位数运算 2:十位数运算);type=随机字符时,表示字符个数 length: 1 @@ -150,7 +152,7 @@ captcha: # 字体样式 0-普通|1-粗体|2-斜体 weight: 1 # 字体大小 - size: 18 + size: 30 # 验证码有效期(秒) expire-seconds: 120 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 1869f002..8ce1f49c 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -136,10 +136,12 @@ captcha: # 验证码高度 height: 40 # 验证码干扰元素个数 - interfere-count: 3 + interfere-count: 4 + # 文本透明度(0.0-1.0) + text-alpha: 0.8 # 验证码字符配置 code: - # 验证码字符类型 math-算术 |random-随机字符 + # 验证码字符类型 math-算术|random-随机字符 type: math # 验证码字符长度,type=算术时,表示运算位数(1:个位数运算 2:十位数运算);type=随机字符时,表示字符个数 length: 1 @@ -150,7 +152,7 @@ captcha: # 字体样式 0-普通|1-粗体|2-斜体 weight: 1 # 字体大小 - size: 18 + size: 30 # 验证码有效期(秒) expire-seconds: 120