diff --git a/common/common-core/pom.xml b/common/common-core/pom.xml index 257124d..bbd54d5 100644 --- a/common/common-core/pom.xml +++ b/common/common-core/pom.xml @@ -49,6 +49,13 @@ org.apache.commons commons-lang3 + + + + org.springframework.boot + spring-boot-starter-security + provided + diff --git a/common/common-model/src/main/java/com/tacit/common/constant/CommonConstant.java b/common/common-core/src/main/java/com/tacit/common/constant/CommonConstant.java similarity index 88% rename from common/common-model/src/main/java/com/tacit/common/constant/CommonConstant.java rename to common/common-core/src/main/java/com/tacit/common/constant/CommonConstant.java index 1eb5d89..a600e14 100644 --- a/common/common-model/src/main/java/com/tacit/common/constant/CommonConstant.java +++ b/common/common-core/src/main/java/com/tacit/common/constant/CommonConstant.java @@ -26,5 +26,9 @@ public class CommonConstant { public static final String ROLE_USER = "user"; // Redis key + public static final String REDIS_KEY_TACIT = "tacit:"; + public static final String REDIS_KEY_USER_TOKEN = "user:token:"; + + public static final String REDIS_KEY_TACIT_USER_TOKEN = "tacit:user:token:"; } \ No newline at end of file diff --git a/common/common-core/src/main/java/com/tacit/common/utils/SecurityUtils.java b/common/common-core/src/main/java/com/tacit/common/utils/SecurityUtils.java new file mode 100644 index 0000000..e381c4c --- /dev/null +++ b/common/common-core/src/main/java/com/tacit/common/utils/SecurityUtils.java @@ -0,0 +1,89 @@ +package com.tacit.common.utils; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; + +/** + * Security工具类,提供获取当前用户信息的方法 + * @author lidongjin + * @date 2026-01-09 + */ +public class SecurityUtils { + + /** + * 从当前认证上下文获取用户ID + * @return 用户ID + */ + public static Long getCurrentUserId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return null; + } + + // 获取认证对象的details,这里存储了userId等信息 + Object details = authentication.getDetails(); + if (details == null || !(details instanceof java.util.Map)) { + return null; + } + + // 从map中获取userId + java.util.Map userDetails = (java.util.Map) details; + Object userIdObj = userDetails.get("userId"); + if (userIdObj == null) { + return null; + } + + if (userIdObj instanceof Long) { + return (Long) userIdObj; + } + + if (userIdObj instanceof Integer) { + return ((Integer) userIdObj).longValue(); + } + + return Long.parseLong(userIdObj.toString()); + } + + /** + * 从当前认证上下文获取用户名 + * @return 用户名 + */ + public static String getCurrentUsername() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return null; + } + + Object principal = authentication.getPrincipal(); + if (principal == null || !(principal instanceof User)) { + return null; + } + + return ((User) principal).getUsername(); + } + + /** + * 从当前认证上下文获取用户角色 + * @return 用户角色 + */ + public static String getCurrentUserRole() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return null; + } + + // 获取第一个角色 + return authentication.getAuthorities().stream() + .findFirst() + .map(grantedAuthority -> { + String role = grantedAuthority.getAuthority(); + // 移除ROLE_前缀 + if (role.startsWith("ROLE_")) { + return role.substring(5); + } + return role; + }) + .orElse(null); + } +} diff --git a/common/common-core/src/test/java/com/tacit/common/utils/JwtUtilsTest.java b/common/common-core/src/test/java/com/tacit/common/utils/JwtUtilsTest.java index 4b953d6..a4517b2 100644 --- a/common/common-core/src/test/java/com/tacit/common/utils/JwtUtilsTest.java +++ b/common/common-core/src/test/java/com/tacit/common/utils/JwtUtilsTest.java @@ -1,7 +1,5 @@ package com.tacit.common.utils; -import com.tacit.common.constant.CommonConstant; -import io.jsonwebtoken.Claims; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/common/common-feign/pom.xml b/common/common-feign/pom.xml index fd0d945..4865db9 100644 --- a/common/common-feign/pom.xml +++ b/common/common-feign/pom.xml @@ -22,6 +22,12 @@ ${project.parent.version} + + com.tacit + common-core + ${project.parent.version} + + com.alibaba.cloud diff --git a/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignClientConfig.java b/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignClientConfig.java index 50f6f51..7062c19 100644 --- a/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignClientConfig.java +++ b/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignClientConfig.java @@ -1,6 +1,6 @@ package com.tacit.common.feign.config; -import com.tacit.common.feign.FeignAuthInterceptor; +import com.tacit.common.feign.interceptor.FeignAuthInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignGlobalConfig.java b/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignGlobalConfig.java index 74c3b10..6724b58 100644 --- a/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignGlobalConfig.java +++ b/common/common-feign/src/main/java/com/tacit/common/feign/config/FeignGlobalConfig.java @@ -1,13 +1,16 @@ package com.tacit.common.feign.config; -import org.springframework.context.annotation.Configuration; +import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.cloud.openfeign.EnableFeignClients; /** * Feign全局配置类,设置默认Feign客户端配置 * 所有@FeignClient会自动应用此配置,无需显式指定 */ -@Configuration -@EnableFeignClients(defaultConfiguration = FeignClientConfig.class) +@AutoConfiguration +@EnableFeignClients( + basePackages = "com.tacit.common.feign", + defaultConfiguration = FeignClientConfig.class +) public class FeignGlobalConfig { } \ No newline at end of file diff --git a/common/common-feign/src/main/java/com/tacit/common/feign/FeignAuthInterceptor.java b/common/common-feign/src/main/java/com/tacit/common/feign/interceptor/FeignAuthInterceptor.java similarity index 98% rename from common/common-feign/src/main/java/com/tacit/common/feign/FeignAuthInterceptor.java rename to common/common-feign/src/main/java/com/tacit/common/feign/interceptor/FeignAuthInterceptor.java index 6a1c722..29c4865 100644 --- a/common/common-feign/src/main/java/com/tacit/common/feign/FeignAuthInterceptor.java +++ b/common/common-feign/src/main/java/com/tacit/common/feign/interceptor/FeignAuthInterceptor.java @@ -1,4 +1,4 @@ -package com.tacit.common.feign; +package com.tacit.common.feign.interceptor; import com.tacit.common.constant.CommonConstant; import lombok.extern.slf4j.Slf4j; diff --git a/common/common-feign/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/common/common-feign/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..99cc335 --- /dev/null +++ b/common/common-feign/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.tacit.common.feign.config.FeignGlobalConfig diff --git a/common/common-redis/pom.xml b/common/common-redis/pom.xml index cd90fa7..7c45f67 100644 --- a/common/common-redis/pom.xml +++ b/common/common-redis/pom.xml @@ -20,6 +20,11 @@ org.springframework.boot spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-autoconfigure + diff --git a/common/common-redis/src/main/java/com/tacit/common/redis/config/RedisConfig.java b/common/common-redis/src/main/java/com/tacit/common/redis/config/RedisConfig.java index 6931b3a..775e62c 100644 --- a/common/common-redis/src/main/java/com/tacit/common/redis/config/RedisConfig.java +++ b/common/common-redis/src/main/java/com/tacit/common/redis/config/RedisConfig.java @@ -5,21 +5,25 @@ import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.tacit.common.redis.utils.RedisUtils; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; -@Configuration -@ComponentScan(basePackages = "com.tacit.common.redis.utils") +@AutoConfiguration +@ConditionalOnClass(RedisConnectionFactory.class) +@AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisConfig { - + @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + public RedisTemplate stringObjectRedisTemplate(RedisConnectionFactory factory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(factory); @@ -51,9 +55,8 @@ public class RedisConfig { } @Bean - public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { - StringRedisTemplate template = new StringRedisTemplate(); - template.setConnectionFactory(factory); - return template; + @ConditionalOnMissingBean + public RedisUtils redisUtils() { + return new RedisUtils(); } } \ No newline at end of file diff --git a/common/common-redis/src/main/java/com/tacit/common/redis/utils/RedisUtils.java b/common/common-redis/src/main/java/com/tacit/common/redis/utils/RedisUtils.java index d1586dd..1eb79d5 100644 --- a/common/common-redis/src/main/java/com/tacit/common/redis/utils/RedisUtils.java +++ b/common/common-redis/src/main/java/com/tacit/common/redis/utils/RedisUtils.java @@ -2,9 +2,9 @@ package com.tacit.common.redis.utils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.List; @@ -18,9 +18,10 @@ import java.util.concurrent.TimeUnit; * @CreateTime: 2026-01-07 09:27 */ @Slf4j -@Component public class RedisUtils { + @Autowired + @Qualifier("stringObjectRedisTemplate") private RedisTemplate redisTemplate; @Autowired @@ -345,7 +346,7 @@ public class RedisUtils { /** * 删除键 - * @param key 键(可以多个) + * @param keys 键(可以多个) */ public void delete(String... keys) { if (keys != null && keys.length > 0) { diff --git a/common/common-redis/src/main/resources/META-INF/spring.factories b/common/common-redis/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 55b9302..0000000 --- a/common/common-redis/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.tacit.common.redis.config.RedisConfig \ No newline at end of file diff --git a/common/common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/common/common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..05ec75e --- /dev/null +++ b/common/common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.tacit.common.redis.config.RedisConfig \ No newline at end of file diff --git a/common/common-xxl-job/src/main/java/com/tacit/common/xxljob/XxlJobAutoConfiguration.java b/common/common-xxl-job/src/main/java/com/tacit/common/xxljob/XxlJobAutoConfiguration.java index 630a141..ab50701 100644 --- a/common/common-xxl-job/src/main/java/com/tacit/common/xxljob/XxlJobAutoConfiguration.java +++ b/common/common-xxl-job/src/main/java/com/tacit/common/xxljob/XxlJobAutoConfiguration.java @@ -20,7 +20,12 @@ import java.util.stream.Collectors; * @Description: com.tacit.starter.xxljob * @version: 1.0 */ +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; + +@AutoConfiguration @EnableConfigurationProperties(XxlJobProperties.class) +@ConditionalOnClass(XxlJobSpringExecutor.class) public class XxlJobAutoConfiguration { private Logger log = LoggerFactory.getLogger(XxlJobAutoConfiguration.class); diff --git a/common/common-xxl-job/src/main/resources/META-INF/spring.factories b/common/common-xxl-job/src/main/resources/META-INF/spring.factories deleted file mode 100644 index f2e205b..0000000 --- a/common/common-xxl-job/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.tacit.common.xxljob.XxlJobAutoConfiguration \ No newline at end of file diff --git a/common/common-xxl-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/common/common-xxl-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e69de29 diff --git a/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java index a0a7f55..5ecb78b 100644 --- a/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java +++ b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java @@ -1,18 +1,14 @@ package com.tacit.admin; -import com.tacit.common.redis.config.RedisConfig; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Import; @SpringBootApplication -@Import(RedisConfig.class) @MapperScan("com.tacit.admin.mapper") @EnableDiscoveryClient -@EnableFeignClients(basePackages = "com.tacit.common.feign") public class AdminApplication { public static void main(String[] args) { SpringApplication.run(AdminApplication.class, args); diff --git a/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java index b570025..339f070 100644 --- a/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java +++ b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java @@ -19,6 +19,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.filter.OncePerRequestFilter; import jakarta.servlet.FilterChain; @@ -27,6 +28,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; @Configuration @EnableWebSecurity @@ -44,11 +46,10 @@ public class SecurityConfig { .cors(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth - .requestMatchers("/auth/**","test/**", "/test/hello", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() + .requestMatchers("/auth/login", "/auth/register","test/**", "/test/hello", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() .requestMatchers("/test/feign/**").authenticated() - .anyRequest().authenticated() - ) - .addFilterBefore(authenticationFilter(), org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class); + .anyRequest().authenticated()) + .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } @@ -63,30 +64,55 @@ public class SecurityConfig { if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) { String token = authorization.substring(CommonConstant.JWT_PREFIX.length()); try { - // 验证JWT令牌和Redis中的令牌是否存在 - if (JwtUtils.validateToken(token) && redisUtils.hasKey(token)) { + // 验证JWT令牌 + if (JwtUtils.validateToken(token)) { // 从令牌中获取用户信息 - String username = JwtUtils.getUsernameFromToken(token); - String role = JwtUtils.getRoleFromToken(token); + Long userId = JwtUtils.getUserIdFromToken(token); - // 创建认证对象 - User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))); - var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authentication); + // 构建Redis key:tacit:user:token:{userId} + String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId; + + // 验证Redis中的令牌是否存在且匹配 + String redisToken = redisUtils.get(redisKey); + if (token.equals(redisToken)) { + String username = JwtUtils.getUsernameFromToken(token); + String role = JwtUtils.getRoleFromToken(token); + + // 创建认证对象 + User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))); + + // 创建用户详情map,存储userId等信息 + HashMap userDetails = new HashMap<>(); + userDetails.put("userId", userId); + userDetails.put("username", username); + userDetails.put("role", role); + + var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities()); + authentication.setDetails(userDetails); + SecurityContextHolder.getContext().setAuthentication(authentication); + } } } catch (Exception e) { log.error("JWT令牌验证失败: {}", e.getMessage()); } } else { // 检查是否有用户上下文头(网关转发时添加) - String userId = request.getHeader("X-User-Id"); + String userIdStr = request.getHeader("X-User-Id"); String username = request.getHeader("X-Username"); String role = request.getHeader("X-Role"); - if (userId != null && username != null && role != null) { + if (userIdStr != null && username != null && role != null) { // 创建认证对象 User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))); + + // 创建用户详情map,存储userId等信息 + HashMap userDetails = new HashMap<>(); + userDetails.put("userId", Long.parseLong(userIdStr)); + userDetails.put("username", username); + userDetails.put("role", role); + var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities()); + authentication.setDetails(userDetails); SecurityContextHolder.getContext().setAuthentication(authentication); } } diff --git a/tacit-admin/src/main/java/com/tacit/admin/controller/AuthController.java b/tacit-admin/src/main/java/com/tacit/admin/controller/AuthController.java index 9e06be2..701ee41 100644 --- a/tacit-admin/src/main/java/com/tacit/admin/controller/AuthController.java +++ b/tacit-admin/src/main/java/com/tacit/admin/controller/AuthController.java @@ -4,17 +4,16 @@ import com.tacit.admin.entity.dto.LoginRequest; import com.tacit.admin.entity.dto.LoginResponse; import com.tacit.admin.entity.dto.RegisterRequest; import com.tacit.admin.service.UserService; -import com.tacit.common.constant.CommonConstant; import com.tacit.common.entity.ResponseResult; import com.tacit.common.utils.ResCode; +import com.tacit.common.utils.SecurityUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; -import jakarta.servlet.FilterChain; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/auth") @@ -39,10 +38,15 @@ public class AuthController { } @Operation(summary = "用户退出登录", description = "用户退出登录") @PostMapping("/logout") - public ResponseResult logout(HttpServletRequest request) { - String authorization = request.getHeader("Authorization"); - String token = authorization.substring(CommonConstant.JWT_PREFIX.length()); - userService.logout(token); + public ResponseResult logout() { + // 从SecurityContext中获取当前用户id + Long userId = SecurityUtils.getCurrentUserId(); + if (userId == null) { + return ResponseResult.fail(ResCode.NO_LOGIN.getResultCode(), ResCode.NO_LOGIN.getResultMsg()); + } + + // 调用服务层处理退出登录 + userService.logoutByUserId(userId); return ResponseResult.success(ResCode.LOGOUT.getResultCode(), ResCode.LOGOUT.getResultMsg(), null); } } diff --git a/tacit-admin/src/main/java/com/tacit/admin/service/UserService.java b/tacit-admin/src/main/java/com/tacit/admin/service/UserService.java index 75d3294..6f666dc 100644 --- a/tacit-admin/src/main/java/com/tacit/admin/service/UserService.java +++ b/tacit-admin/src/main/java/com/tacit/admin/service/UserService.java @@ -66,4 +66,10 @@ public interface UserService extends IService { * 用户退出登录 */ void logout(String token); + + /** + * 根据用户ID退出登录 + * @param userId 用户ID + */ + void logoutByUserId(Long userId); } diff --git a/tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java b/tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java index 376becc..8b1fcc3 100644 --- a/tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java +++ b/tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java @@ -10,11 +10,11 @@ import com.tacit.admin.entity.dto.RegisterRequest; import com.tacit.admin.mapper.UserMapper; import com.tacit.admin.service.RoleService; import com.tacit.admin.service.UserService; -import com.tacit.common.utils.JwtUtils; +import com.tacit.common.constant.CommonConstant; import com.tacit.common.redis.utils.RedisUtils; +import com.tacit.common.utils.JwtUtils; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -103,8 +103,12 @@ public class UserServiceImpl extends ServiceImpl implements Us claims.put("username", user.getUsername()); claims.put("role", user.getRole()); String token = JwtUtils.generateToken(claims); + + // 构建Redis key:tacit:user:token:{userId} + String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + user.getId(); // 将生成的 JWT 令牌存储到 Redis 缓存中,设置过期时间为 7 天(7 * 24 * 60 * 60 秒) - redisUtils.setObject(token, user.getId(),7 * 24 * 60 * 60, TimeUnit.SECONDS); + redisUtils.set(redisKey, token ,7 * 24 * 60 * 60, TimeUnit.SECONDS); + Role roleMenu = roleService.getRoleByUserId(user.getId()); // 构建登录响应 LoginResponse loginResponse = new LoginResponse(); @@ -119,8 +123,20 @@ public class UserServiceImpl extends ServiceImpl implements Us */ @Override public void logout(String token) { + // 从token中解析userId + Long userId = JwtUtils.getUserIdFromToken(token); + // 构建Redis key:tacit:user:token:{userId} + String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId; //清除缓存 - redisUtils.delete(token); + redisUtils.delete(redisKey); + } + + @Override + public void logoutByUserId(Long userId) { + // 构建Redis key:tacit:user:token:{userId} + String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId; + //清除缓存 + redisUtils.delete(redisKey); } @Override diff --git a/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java b/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java index d8d9789..d2e590f 100644 --- a/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java +++ b/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java @@ -10,7 +10,6 @@ import com.tacit.common.redis.config.RedisConfig; @SpringBootApplication @Import(RedisConfig.class) @MapperScan("com.tacit.app.mapper") -@EnableFeignClients(basePackages = "com.tacit.common.feign") public class AppApiApplication { public static void main(String[] args) { SpringApplication.run(AppApiApplication.class, args); diff --git a/tacit-app-api/src/main/java/com/tacit/app/config/AppSecurityConfig.java b/tacit-app-api/src/main/java/com/tacit/app/config/AppSecurityConfig.java index ab54def..de1a78b 100644 --- a/tacit-app-api/src/main/java/com/tacit/app/config/AppSecurityConfig.java +++ b/tacit-app-api/src/main/java/com/tacit/app/config/AppSecurityConfig.java @@ -62,17 +62,25 @@ public class AppSecurityConfig { if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) { String token = authorization.substring(CommonConstant.JWT_PREFIX.length()); try { - // 验证JWT令牌和Redis中的令牌是否存在 - if (JwtUtils.validateToken(token) && redisUtils.hasKey(token)) { + // 验证JWT令牌 + if (JwtUtils.validateToken(token)) { // 从令牌中获取用户信息 Long userId = JwtUtils.getUserIdFromToken(token); - String username = JwtUtils.getUsernameFromToken(token); - String role = JwtUtils.getRoleFromToken(token); - // 创建认证对象 - User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))); - var authentication = new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authentication); + // 构建Redis key:tacit:user:token:{userId} + String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId; + + // 验证Redis中的令牌是否存在且匹配 + String redisToken = redisUtils.get(redisKey); + if (token.equals(redisToken)) { + String username = JwtUtils.getUsernameFromToken(token); + String role = JwtUtils.getRoleFromToken(token); + + // 创建认证对象 + User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))); + var authentication = new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } } } catch (Exception e) { log.error("JWT令牌验证失败: {}", e.getMessage()); diff --git a/tacit-gateway/pom.xml b/tacit-gateway/pom.xml index 41bd088..99961ea 100644 --- a/tacit-gateway/pom.xml +++ b/tacit-gateway/pom.xml @@ -68,12 +68,6 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery - - com.tacit - common-redis - 1.0.0-SNAPSHOT - compile - diff --git a/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java b/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java index 2dc8df7..aa37a66 100644 --- a/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java +++ b/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java @@ -7,7 +7,6 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; -@Import(RedisConfig.class) @SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { diff --git a/tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java b/tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java index 4c004e0..4191eea 100644 --- a/tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java +++ b/tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java @@ -30,7 +30,6 @@ public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory WHITE_LIST = List.of( "/auth/login", "/auth/register", - "/auth/login", "/auth/register", "/swagger-ui", "/v3/api-docs" @@ -53,33 +52,40 @@ public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory