feat:鉴权集中到网关

This commit is contained in:
panxuejie 2026-01-14 09:59:23 +08:00
parent a85a68e8b0
commit 03a37dd51e
4 changed files with 79 additions and 138 deletions

View File

@ -1,6 +1,7 @@
package com.tacit.common.feign.interceptor;
import com.tacit.common.constant.CommonConstant;
import com.tacit.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@ -20,44 +21,56 @@ public class FeignAuthInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 从当前线程获取认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 先尝试从SecurityUtils获取用户信息推荐方式
Long userId = SecurityUtils.getCurrentUserId();
String username = SecurityUtils.getCurrentUsername();
String role = SecurityUtils.getCurrentUserRole();
if (authentication != null && !(authentication instanceof org.springframework.security.authentication.AnonymousAuthenticationToken)) {
// 获取用户名
String username = authentication.getName();
if (userId != null && username != null && role != null) {
// 传递用户信息头
template.header("X-User-Id", String.valueOf(userId));
template.header("X-Username", username);
// 获取角色信息
authentication.getAuthorities().forEach(authority -> {
template.header("X-Role", authority.getAuthority().replace("ROLE_", ""));
});
template.header("X-Role", role);
} else {
// 从当前线程获取认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !(authentication instanceof org.springframework.security.authentication.AnonymousAuthenticationToken)) {
// 获取用户名
String authUsername = authentication.getName();
template.header("X-Username", authUsername);
// 获取角色信息
authentication.getAuthorities().forEach(authority -> {
template.header("X-Role", authority.getAuthority().replace("ROLE_", ""));
});
}
// 从RequestContextHolder中获取请求信息适用于非Feign调用场景
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
// 传递JWT令牌
// 传递JWT令牌作为后备方案
String authorization = request.getHeader(CommonConstant.JWT_HEADER);
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
template.header(CommonConstant.JWT_HEADER, authorization);
}
// 传递用户上下文
String userId = request.getHeader("X-User-Id");
String username = request.getHeader("X-Username");
String role = request.getHeader("X-Role");
// 传递用户上下文如果有的话
String requestUserId = request.getHeader("X-User-Id");
String requestUsername = request.getHeader("X-Username");
String requestRole = request.getHeader("X-Role");
if (userId != null) {
template.header("X-User-Id", userId);
if (requestUserId != null) {
template.header("X-User-Id", requestUserId);
}
if (username != null) {
template.header("X-Username", username);
if (requestUsername != null) {
template.header("X-Username", requestUsername);
}
if (role != null) {
template.header("X-Role", role);
if (requestRole != null) {
template.header("X-Role", requestRole);
}
}
}

View File

@ -1,10 +1,10 @@
package com.tacit.admin.config;
import com.tacit.common.constant.CommonConstant;
import com.tacit.common.utils.AesPasswordEncoder;
import com.tacit.common.utils.JwtUtils;
import com.tacit.common.redis.utils.RedisUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -22,10 +22,6 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
@ -36,9 +32,6 @@ import java.util.HashMap;
@Slf4j
public class SecurityConfig {
@Resource
private RedisUtils redisUtils;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
@ -46,9 +39,8 @@ public class SecurityConfig {
.cors(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/login", "/auth/register","test/**", "/test/hello", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
.requestMatchers("/test/feign/**").authenticated()
.anyRequest().authenticated())
// 所有请求都允许通过鉴权由网关完成
.anyRequest().permitAll())
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
@ -59,62 +51,24 @@ public class SecurityConfig {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 先检查是否有JWT令牌直接服务间调用时可能传递JWT
String authorization = request.getHeader(CommonConstant.JWT_HEADER);
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
try {
// 验证JWT令牌
if (JwtUtils.validateToken(token)) {
// 从令牌中获取用户信息
Long userId = JwtUtils.getUserIdFromToken(token);
// 只从请求头中获取用户信息不做鉴权
String userIdStr = request.getHeader("X-User-Id");
String username = request.getHeader("X-Username");
String role = request.getHeader("X-Role");
// 构建Redis keytacit:user:token:{userId}
String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId;
if (userIdStr != null && username != null && role != null) {
// 创建认证对象
User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
// 验证Redis中的令牌是否存在且匹配
String redisToken = redisUtils.get(redisKey);
if (token.equals(redisToken)) {
String username = JwtUtils.getUsernameFromToken(token);
String role = JwtUtils.getRoleFromToken(token);
// 创建用户详情map存储userId等信息
HashMap<String, Object> userDetails = new HashMap<>();
userDetails.put("userId", Long.parseLong(userIdStr));
userDetails.put("username", username);
userDetails.put("role", role);
// 创建认证对象
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());
authentication.setDetails(userDetails);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (Exception e) {
log.error("JWT令牌验证失败: {}", e.getMessage());
}
} else {
// 检查是否有用户上下文头网关转发时添加
String userIdStr = request.getHeader("X-User-Id");
String username = request.getHeader("X-Username");
String role = request.getHeader("X-Role");
if (userIdStr != null && username != null && role != null) {
// 创建认证对象
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());
authentication.setDetails(userDetails);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
authentication.setDetails(userDetails);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);

View File

@ -1,11 +1,7 @@
package com.tacit.app.config;
import com.tacit.common.constant.CommonConstant;
import com.tacit.common.redis.utils.RedisUtils;
import com.tacit.common.utils.AesPasswordEncoder;
import com.tacit.common.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
@ -27,6 +23,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
@ -34,9 +31,6 @@ import java.util.Collections;
@Slf4j
public class AppSecurityConfig {
@Autowired
private RedisUtils redisUtils;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
@ -44,9 +38,8 @@ public class AppSecurityConfig {
.cors(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/user/info/**").permitAll()
.anyRequest().authenticated()
)
// 所有请求都允许通过鉴权由网关完成
.anyRequest().permitAll())
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
@ -57,45 +50,23 @@ public class AppSecurityConfig {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 先检查是否有JWT令牌直接服务间调用时可能传递JWT
String authorization = request.getHeader(CommonConstant.JWT_HEADER);
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
try {
// 验证JWT令牌
if (JwtUtils.validateToken(token)) {
// 从令牌中获取用户信息
Long userId = JwtUtils.getUserIdFromToken(token);
// 只从请求头中获取用户信息不做鉴权
String userIdStr = request.getHeader("X-User-Id");
String username = request.getHeader("X-Username");
String role = request.getHeader("X-Role");
// 构建Redis keytacit:user:token:{userId}
String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId;
if (userIdStr != null && username != null && role != null) {
User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
var authentication = new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
// 验证Redis中的令牌是否存在且匹配
String redisToken = redisUtils.get(redisKey);
if (token.equals(redisToken)) {
String username = JwtUtils.getUsernameFromToken(token);
String role = JwtUtils.getRoleFromToken(token);
// 创建用户详情map存储userId等信息
HashMap<String, Object> userDetails = new HashMap<>();
userDetails.put("userId", Long.parseLong(userIdStr));
userDetails.put("username", username);
userDetails.put("role", role);
authentication.setDetails(userDetails);
// 创建认证对象
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());
}
} else {
// 检查是否有用户上下文头网关转发或服务间调用时添加
String userId = request.getHeader("X-User-Id");
String username = request.getHeader("X-Username");
String role = request.getHeader("X-Role");
if (userId != null && username != null && role != null) {
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);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);

View File

@ -10,6 +10,7 @@ import com.tacit.app.service.UserService;
import com.tacit.common.constant.CommonConstant;
import com.tacit.common.exception.BusinessException;
import com.tacit.common.utils.JwtUtils;
import com.tacit.common.utils.SecurityUtils;
import jakarta.annotation.Resource;
import org.springframework.security.crypto.password.PasswordEncoder;
@ -91,6 +92,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
@Override
public User getUserInfo(Long userId) {
Long currentUserId = SecurityUtils.getCurrentUserId();
System.out.println("currentUserId: " + currentUserId);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", userId)
.eq("del_flag", 0);