修改redis的缓存
This commit is contained in:
parent
70fe35510b
commit
a174cb255f
|
|
@ -50,6 +50,13 @@
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Security -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Testing -->
|
<!-- Testing -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,9 @@ public class CommonConstant {
|
||||||
public static final String ROLE_USER = "user";
|
public static final String ROLE_USER = "user";
|
||||||
|
|
||||||
// Redis key
|
// 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_USER_TOKEN = "user:token:";
|
||||||
|
|
||||||
|
public static final String REDIS_KEY_TACIT_USER_TOKEN = "tacit:user:token:";
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package com.tacit.common.utils;
|
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.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,12 @@
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tacit</groupId>
|
||||||
|
<artifactId>common-core</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Spring Cloud Alibaba Nacos Discovery -->
|
<!-- Spring Cloud Alibaba Nacos Discovery -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.tacit.common.feign.config;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package com.tacit.common.feign.config;
|
package com.tacit.common.feign.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feign全局配置类,设置默认Feign客户端配置
|
* Feign全局配置类,设置默认Feign客户端配置
|
||||||
* 所有@FeignClient会自动应用此配置,无需显式指定
|
* 所有@FeignClient会自动应用此配置,无需显式指定
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@AutoConfiguration
|
||||||
@EnableFeignClients(defaultConfiguration = FeignClientConfig.class)
|
@EnableFeignClients(
|
||||||
|
basePackages = "com.tacit.common.feign",
|
||||||
|
defaultConfiguration = FeignClientConfig.class
|
||||||
|
)
|
||||||
public class FeignGlobalConfig {
|
public class FeignGlobalConfig {
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tacit.common.feign;
|
package com.tacit.common.feign.interceptor;
|
||||||
|
|
||||||
import com.tacit.common.constant.CommonConstant;
|
import com.tacit.common.constant.CommonConstant;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.tacit.common.feign.config.FeignGlobalConfig
|
||||||
|
|
@ -20,6 +20,11 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Spring Boot Auto Configure -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Jedis -->
|
<!-- Jedis -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,25 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
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.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.connection.RedisConnectionFactory;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
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.Jackson2JsonRedisSerializer;
|
||||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
@Configuration
|
@AutoConfiguration
|
||||||
@ComponentScan(basePackages = "com.tacit.common.redis.utils")
|
@ConditionalOnClass(RedisConnectionFactory.class)
|
||||||
|
@AutoConfigureAfter(RedisAutoConfiguration.class)
|
||||||
public class RedisConfig {
|
public class RedisConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
public RedisTemplate<String, Object> stringObjectRedisTemplate(RedisConnectionFactory factory) {
|
||||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
template.setConnectionFactory(factory);
|
template.setConnectionFactory(factory);
|
||||||
|
|
||||||
|
|
@ -51,9 +55,8 @@ public class RedisConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
|
@ConditionalOnMissingBean
|
||||||
StringRedisTemplate template = new StringRedisTemplate();
|
public RedisUtils redisUtils() {
|
||||||
template.setConnectionFactory(factory);
|
return new RedisUtils();
|
||||||
return template;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,9 +2,9 @@ package com.tacit.common.redis.utils;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.RedisTemplate;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -18,9 +18,10 @@ import java.util.concurrent.TimeUnit;
|
||||||
* @CreateTime: 2026-01-07 09:27
|
* @CreateTime: 2026-01-07 09:27
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
|
||||||
public class RedisUtils {
|
public class RedisUtils {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("stringObjectRedisTemplate")
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
@ -345,7 +346,7 @@ public class RedisUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除键
|
* 删除键
|
||||||
* @param key 键(可以多个)
|
* @param keys 键(可以多个)
|
||||||
*/
|
*/
|
||||||
public void delete(String... keys) {
|
public void delete(String... keys) {
|
||||||
if (keys != null && keys.length > 0) {
|
if (keys != null && keys.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.tacit.common.redis.config.RedisConfig
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.tacit.common.redis.config.RedisConfig
|
||||||
|
|
@ -20,7 +20,12 @@ import java.util.stream.Collectors;
|
||||||
* @Description: com.tacit.starter.xxljob
|
* @Description: com.tacit.starter.xxljob
|
||||||
* @version: 1.0
|
* @version: 1.0
|
||||||
*/
|
*/
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
|
||||||
|
@AutoConfiguration
|
||||||
@EnableConfigurationProperties(XxlJobProperties.class)
|
@EnableConfigurationProperties(XxlJobProperties.class)
|
||||||
|
@ConditionalOnClass(XxlJobSpringExecutor.class)
|
||||||
public class XxlJobAutoConfiguration {
|
public class XxlJobAutoConfiguration {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(XxlJobAutoConfiguration.class);
|
private Logger log = LoggerFactory.getLogger(XxlJobAutoConfiguration.class);
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
|
||||||
com.tacit.common.xxljob.XxlJobAutoConfiguration
|
|
||||||
|
|
@ -1,18 +1,14 @@
|
||||||
package com.tacit.admin;
|
package com.tacit.admin;
|
||||||
|
|
||||||
import com.tacit.common.redis.config.RedisConfig;
|
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Import(RedisConfig.class)
|
|
||||||
@MapperScan("com.tacit.admin.mapper")
|
@MapperScan("com.tacit.admin.mapper")
|
||||||
@EnableDiscoveryClient
|
@EnableDiscoveryClient
|
||||||
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
|
||||||
public class AdminApplication {
|
public class AdminApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(AdminApplication.class, args);
|
SpringApplication.run(AdminApplication.class, args);
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
|
|
@ -27,6 +28,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
|
|
@ -44,11 +46,10 @@ public class SecurityConfig {
|
||||||
.cors(AbstractHttpConfigurer::disable)
|
.cors(AbstractHttpConfigurer::disable)
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth -> auth
|
.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()
|
.requestMatchers("/test/feign/**").authenticated()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated())
|
||||||
)
|
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
.addFilterBefore(authenticationFilter(), org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class);
|
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
@ -63,30 +64,55 @@ public class SecurityConfig {
|
||||||
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
||||||
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
|
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
|
||||||
try {
|
try {
|
||||||
// 验证JWT令牌和Redis中的令牌是否存在
|
// 验证JWT令牌
|
||||||
if (JwtUtils.validateToken(token) && redisUtils.hasKey(token)) {
|
if (JwtUtils.validateToken(token)) {
|
||||||
// 从令牌中获取用户信息
|
// 从令牌中获取用户信息
|
||||||
|
Long userId = JwtUtils.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
// 构建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 username = JwtUtils.getUsernameFromToken(token);
|
||||||
String role = JwtUtils.getRoleFromToken(token);
|
String role = JwtUtils.getRoleFromToken(token);
|
||||||
|
|
||||||
// 创建认证对象
|
// 创建认证对象
|
||||||
User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
|
User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
|
||||||
|
|
||||||
|
// 创建用户详情map,存储userId等信息
|
||||||
|
HashMap<String, Object> userDetails = new HashMap<>();
|
||||||
|
userDetails.put("userId", userId);
|
||||||
|
userDetails.put("username", username);
|
||||||
|
userDetails.put("role", role);
|
||||||
|
|
||||||
var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
|
var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
|
||||||
|
authentication.setDetails(userDetails);
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("JWT令牌验证失败: {}", e.getMessage());
|
log.error("JWT令牌验证失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 检查是否有用户上下文头(网关转发时添加)
|
// 检查是否有用户上下文头(网关转发时添加)
|
||||||
String userId = request.getHeader("X-User-Id");
|
String userIdStr = request.getHeader("X-User-Id");
|
||||||
String username = request.getHeader("X-Username");
|
String username = request.getHeader("X-Username");
|
||||||
String role = request.getHeader("X-Role");
|
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())));
|
User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
|
||||||
|
|
||||||
|
// 创建用户详情map,存储userId等信息
|
||||||
|
HashMap<String, Object> 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());
|
var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
|
||||||
|
authentication.setDetails(userDetails);
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,16 @@ import com.tacit.admin.entity.dto.LoginRequest;
|
||||||
import com.tacit.admin.entity.dto.LoginResponse;
|
import com.tacit.admin.entity.dto.LoginResponse;
|
||||||
import com.tacit.admin.entity.dto.RegisterRequest;
|
import com.tacit.admin.entity.dto.RegisterRequest;
|
||||||
import com.tacit.admin.service.UserService;
|
import com.tacit.admin.service.UserService;
|
||||||
import com.tacit.common.constant.CommonConstant;
|
|
||||||
import com.tacit.common.entity.ResponseResult;
|
import com.tacit.common.entity.ResponseResult;
|
||||||
import com.tacit.common.utils.ResCode;
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.FilterChain;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/auth")
|
@RequestMapping("/auth")
|
||||||
|
|
@ -39,10 +38,15 @@ public class AuthController {
|
||||||
}
|
}
|
||||||
@Operation(summary = "用户退出登录", description = "用户退出登录")
|
@Operation(summary = "用户退出登录", description = "用户退出登录")
|
||||||
@PostMapping("/logout")
|
@PostMapping("/logout")
|
||||||
public ResponseResult<Void> logout(HttpServletRequest request) {
|
public ResponseResult<Void> logout() {
|
||||||
String authorization = request.getHeader("Authorization");
|
// 从SecurityContext中获取当前用户id
|
||||||
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
|
Long userId = SecurityUtils.getCurrentUserId();
|
||||||
userService.logout(token);
|
if (userId == null) {
|
||||||
|
return ResponseResult.<Void>fail(ResCode.NO_LOGIN.getResultCode(), ResCode.NO_LOGIN.getResultMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用服务层处理退出登录
|
||||||
|
userService.logoutByUserId(userId);
|
||||||
return ResponseResult.<Void>success(ResCode.LOGOUT.getResultCode(), ResCode.LOGOUT.getResultMsg(), null);
|
return ResponseResult.<Void>success(ResCode.LOGOUT.getResultCode(), ResCode.LOGOUT.getResultMsg(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,10 @@ public interface UserService extends IService<User> {
|
||||||
* 用户退出登录
|
* 用户退出登录
|
||||||
*/
|
*/
|
||||||
void logout(String token);
|
void logout(String token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID退出登录
|
||||||
|
* @param userId 用户ID
|
||||||
|
*/
|
||||||
|
void logoutByUserId(Long userId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ import com.tacit.admin.entity.dto.RegisterRequest;
|
||||||
import com.tacit.admin.mapper.UserMapper;
|
import com.tacit.admin.mapper.UserMapper;
|
||||||
import com.tacit.admin.service.RoleService;
|
import com.tacit.admin.service.RoleService;
|
||||||
import com.tacit.admin.service.UserService;
|
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.redis.utils.RedisUtils;
|
||||||
|
import com.tacit.common.utils.JwtUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
|
@ -103,8 +103,12 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||||
claims.put("username", user.getUsername());
|
claims.put("username", user.getUsername());
|
||||||
claims.put("role", user.getRole());
|
claims.put("role", user.getRole());
|
||||||
String token = JwtUtils.generateToken(claims);
|
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 秒)
|
// 将生成的 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());
|
Role roleMenu = roleService.getRoleByUserId(user.getId());
|
||||||
// 构建登录响应
|
// 构建登录响应
|
||||||
LoginResponse loginResponse = new LoginResponse();
|
LoginResponse loginResponse = new LoginResponse();
|
||||||
|
|
@ -119,8 +123,20 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void logout(String token) {
|
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
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import com.tacit.common.redis.config.RedisConfig;
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Import(RedisConfig.class)
|
@Import(RedisConfig.class)
|
||||||
@MapperScan("com.tacit.app.mapper")
|
@MapperScan("com.tacit.app.mapper")
|
||||||
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
|
||||||
public class AppApiApplication {
|
public class AppApiApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(AppApiApplication.class, args);
|
SpringApplication.run(AppApiApplication.class, args);
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,17 @@ public class AppSecurityConfig {
|
||||||
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
||||||
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
|
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
|
||||||
try {
|
try {
|
||||||
// 验证JWT令牌和Redis中的令牌是否存在
|
// 验证JWT令牌
|
||||||
if (JwtUtils.validateToken(token) && redisUtils.hasKey(token)) {
|
if (JwtUtils.validateToken(token)) {
|
||||||
// 从令牌中获取用户信息
|
// 从令牌中获取用户信息
|
||||||
Long userId = JwtUtils.getUserIdFromToken(token);
|
Long userId = JwtUtils.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
// 构建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 username = JwtUtils.getUsernameFromToken(token);
|
||||||
String role = JwtUtils.getRoleFromToken(token);
|
String role = JwtUtils.getRoleFromToken(token);
|
||||||
|
|
||||||
|
|
@ -74,6 +81,7 @@ public class AppSecurityConfig {
|
||||||
var authentication = new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
|
var authentication = new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("JWT令牌验证失败: {}", e.getMessage());
|
log.error("JWT令牌验证失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,12 +68,6 @@
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.tacit</groupId>
|
|
||||||
<artifactId>common-redis</artifactId>
|
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
@Import(RedisConfig.class)
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableDiscoveryClient
|
@EnableDiscoveryClient
|
||||||
public class GatewayApplication {
|
public class GatewayApplication {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory<JwtAut
|
||||||
private static final List<String> WHITE_LIST = List.of(
|
private static final List<String> WHITE_LIST = List.of(
|
||||||
"/auth/login",
|
"/auth/login",
|
||||||
"/auth/register",
|
"/auth/register",
|
||||||
"/auth/login",
|
|
||||||
"/auth/register",
|
"/auth/register",
|
||||||
"/swagger-ui",
|
"/swagger-ui",
|
||||||
"/v3/api-docs"
|
"/v3/api-docs"
|
||||||
|
|
@ -72,14 +71,21 @@ public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory<JwtAut
|
||||||
|
|
||||||
// 验证 JWT令牌
|
// 验证 JWT令牌
|
||||||
try {
|
try {
|
||||||
boolean isTokenValid = redisUtils.hasKey(token);
|
|
||||||
if (!isTokenValid) {
|
|
||||||
return unauthorizedResponse(exchange, "Token已被注销");
|
|
||||||
}
|
|
||||||
JwtUtils.validateToken(token);
|
JwtUtils.validateToken(token);
|
||||||
|
|
||||||
// 从令牌中获取用户信息并添加到请求头
|
// 从令牌中获取用户信息
|
||||||
Long userId = JwtUtils.getUserIdFromToken(token);
|
Long userId = JwtUtils.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
// 构建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)) {
|
||||||
|
return unauthorizedResponse(exchange, "Token已被注销");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从令牌中获取用户信息并添加到请求头
|
||||||
String username = JwtUtils.getUsernameFromToken(token);
|
String username = JwtUtils.getUsernameFromToken(token);
|
||||||
String role = JwtUtils.getRoleFromToken(token);
|
String role = JwtUtils.getRoleFromToken(token);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue