feat:鉴权集中到网关
This commit is contained in:
parent
a85a68e8b0
commit
03a37dd51e
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tacit.common.feign.interceptor;
|
package com.tacit.common.feign.interceptor;
|
||||||
|
|
||||||
import com.tacit.common.constant.CommonConstant;
|
import com.tacit.common.constant.CommonConstant;
|
||||||
|
import com.tacit.common.utils.SecurityUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
@ -20,44 +21,56 @@ public class FeignAuthInterceptor implements RequestInterceptor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(RequestTemplate template) {
|
public void apply(RequestTemplate template) {
|
||||||
// 从当前线程获取认证信息
|
// 先尝试从SecurityUtils获取用户信息(推荐方式)
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Long userId = SecurityUtils.getCurrentUserId();
|
||||||
|
String username = SecurityUtils.getCurrentUsername();
|
||||||
|
String role = SecurityUtils.getCurrentUserRole();
|
||||||
|
|
||||||
if (authentication != null && !(authentication instanceof org.springframework.security.authentication.AnonymousAuthenticationToken)) {
|
if (userId != null && username != null && role != null) {
|
||||||
// 获取用户名
|
// 传递用户信息头
|
||||||
String username = authentication.getName();
|
template.header("X-User-Id", String.valueOf(userId));
|
||||||
template.header("X-Username", username);
|
template.header("X-Username", username);
|
||||||
|
template.header("X-Role", role);
|
||||||
// 获取角色信息
|
|
||||||
authentication.getAuthorities().forEach(authority -> {
|
|
||||||
template.header("X-Role", authority.getAuthority().replace("ROLE_", ""));
|
|
||||||
});
|
|
||||||
} else {
|
} 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调用场景)
|
// 从RequestContextHolder中获取请求信息(适用于非Feign调用场景)
|
||||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
if (requestAttributes instanceof ServletRequestAttributes) {
|
if (requestAttributes instanceof ServletRequestAttributes) {
|
||||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
|
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
|
||||||
HttpServletRequest request = servletRequestAttributes.getRequest();
|
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||||
|
|
||||||
// 传递JWT令牌
|
// 传递JWT令牌(作为后备方案)
|
||||||
String authorization = request.getHeader(CommonConstant.JWT_HEADER);
|
String authorization = request.getHeader(CommonConstant.JWT_HEADER);
|
||||||
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
||||||
template.header(CommonConstant.JWT_HEADER, authorization);
|
template.header(CommonConstant.JWT_HEADER, authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 传递用户上下文
|
// 传递用户上下文头(如果有的话)
|
||||||
String userId = request.getHeader("X-User-Id");
|
String requestUserId = request.getHeader("X-User-Id");
|
||||||
String username = request.getHeader("X-Username");
|
String requestUsername = request.getHeader("X-Username");
|
||||||
String role = request.getHeader("X-Role");
|
String requestRole = request.getHeader("X-Role");
|
||||||
|
|
||||||
if (userId != null) {
|
if (requestUserId != null) {
|
||||||
template.header("X-User-Id", userId);
|
template.header("X-User-Id", requestUserId);
|
||||||
}
|
}
|
||||||
if (username != null) {
|
if (requestUsername != null) {
|
||||||
template.header("X-Username", username);
|
template.header("X-Username", requestUsername);
|
||||||
}
|
}
|
||||||
if (role != null) {
|
if (requestRole != null) {
|
||||||
template.header("X-Role", role);
|
template.header("X-Role", requestRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.tacit.admin.config;
|
package com.tacit.admin.config;
|
||||||
|
|
||||||
import com.tacit.common.constant.CommonConstant;
|
|
||||||
import com.tacit.common.utils.AesPasswordEncoder;
|
import com.tacit.common.utils.AesPasswordEncoder;
|
||||||
import com.tacit.common.utils.JwtUtils;
|
import jakarta.servlet.FilterChain;
|
||||||
import com.tacit.common.redis.utils.RedisUtils;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
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.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
@ -36,9 +32,6 @@ import java.util.HashMap;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Resource
|
|
||||||
private RedisUtils redisUtils;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
|
|
@ -46,9 +39,8 @@ 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/login", "/auth/register","test/**", "/test/hello", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
|
// 所有请求都允许通过,鉴权由网关完成
|
||||||
.requestMatchers("/test/feign/**").authenticated()
|
.anyRequest().permitAll())
|
||||||
.anyRequest().authenticated())
|
|
||||||
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
|
|
@ -59,62 +51,24 @@ public class SecurityConfig {
|
||||||
return new OncePerRequestFilter() {
|
return new OncePerRequestFilter() {
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
// 先检查是否有JWT令牌(直接服务间调用时可能传递JWT)
|
// 只从请求头中获取用户信息,不做鉴权
|
||||||
String authorization = request.getHeader(CommonConstant.JWT_HEADER);
|
String userIdStr = request.getHeader("X-User-Id");
|
||||||
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
String username = request.getHeader("X-Username");
|
||||||
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
|
String role = request.getHeader("X-Role");
|
||||||
try {
|
|
||||||
// 验证JWT令牌
|
|
||||||
if (JwtUtils.validateToken(token)) {
|
|
||||||
// 从令牌中获取用户信息
|
|
||||||
Long userId = JwtUtils.getUserIdFromToken(token);
|
|
||||||
|
|
||||||
// 构建Redis key:tacit:user:token:{userId}
|
if (userIdStr != null && username != null && role != null) {
|
||||||
String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId;
|
// 创建认证对象
|
||||||
|
User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
|
||||||
|
|
||||||
// 验证Redis中的令牌是否存在且匹配
|
// 创建用户详情map,存储userId等信息
|
||||||
String redisToken = redisUtils.get(redisKey);
|
HashMap<String, Object> userDetails = new HashMap<>();
|
||||||
if (token.equals(redisToken)) {
|
userDetails.put("userId", Long.parseLong(userIdStr));
|
||||||
String username = JwtUtils.getUsernameFromToken(token);
|
userDetails.put("username", username);
|
||||||
String role = JwtUtils.getRoleFromToken(token);
|
userDetails.put("role", role);
|
||||||
|
|
||||||
// 创建认证对象
|
var authentication = new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities());
|
||||||
User principal = new User(username, "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
|
authentication.setDetails(userDetails);
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
// 创建用户详情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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
package com.tacit.app.config;
|
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.AesPasswordEncoder;
|
||||||
import com.tacit.common.utils.JwtUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
|
@ -27,6 +23,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
|
||||||
|
|
@ -34,9 +31,6 @@ import java.util.Collections;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AppSecurityConfig {
|
public class AppSecurityConfig {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisUtils redisUtils;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
|
|
@ -44,9 +38,8 @@ public class AppSecurityConfig {
|
||||||
.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/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/user/info/**").permitAll()
|
// 所有请求都允许通过,鉴权由网关完成
|
||||||
.anyRequest().authenticated()
|
.anyRequest().permitAll())
|
||||||
)
|
|
||||||
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
|
|
@ -57,45 +50,23 @@ public class AppSecurityConfig {
|
||||||
return new OncePerRequestFilter() {
|
return new OncePerRequestFilter() {
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
// 先检查是否有JWT令牌(直接服务间调用时可能传递JWT)
|
// 只从请求头中获取用户信息,不做鉴权
|
||||||
String authorization = request.getHeader(CommonConstant.JWT_HEADER);
|
String userIdStr = request.getHeader("X-User-Id");
|
||||||
if (authorization != null && authorization.startsWith(CommonConstant.JWT_PREFIX)) {
|
String username = request.getHeader("X-Username");
|
||||||
String token = authorization.substring(CommonConstant.JWT_PREFIX.length());
|
String role = request.getHeader("X-Role");
|
||||||
try {
|
|
||||||
// 验证JWT令牌
|
|
||||||
if (JwtUtils.validateToken(token)) {
|
|
||||||
// 从令牌中获取用户信息
|
|
||||||
Long userId = JwtUtils.getUserIdFromToken(token);
|
|
||||||
|
|
||||||
// 构建Redis key:tacit:user:token:{userId}
|
if (userIdStr != null && username != null && role != null) {
|
||||||
String redisKey = CommonConstant.REDIS_KEY_TACIT_USER_TOKEN + userId;
|
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中的令牌是否存在且匹配
|
// 创建用户详情map,存储userId等信息
|
||||||
String redisToken = redisUtils.get(redisKey);
|
HashMap<String, Object> userDetails = new HashMap<>();
|
||||||
if (token.equals(redisToken)) {
|
userDetails.put("userId", Long.parseLong(userIdStr));
|
||||||
String username = JwtUtils.getUsernameFromToken(token);
|
userDetails.put("username", username);
|
||||||
String role = JwtUtils.getRoleFromToken(token);
|
userDetails.put("role", role);
|
||||||
|
authentication.setDetails(userDetails);
|
||||||
|
|
||||||
// 创建认证对象
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import com.tacit.app.service.UserService;
|
||||||
import com.tacit.common.constant.CommonConstant;
|
import com.tacit.common.constant.CommonConstant;
|
||||||
import com.tacit.common.exception.BusinessException;
|
import com.tacit.common.exception.BusinessException;
|
||||||
import com.tacit.common.utils.JwtUtils;
|
import com.tacit.common.utils.JwtUtils;
|
||||||
|
import com.tacit.common.utils.SecurityUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
@ -91,6 +92,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserInfo(Long userId) {
|
public User getUserInfo(Long userId) {
|
||||||
|
Long currentUserId = SecurityUtils.getCurrentUserId();
|
||||||
|
System.out.println("currentUserId: " + currentUserId);
|
||||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.eq("id", userId)
|
queryWrapper.eq("id", userId)
|
||||||
.eq("del_flag", 0);
|
.eq("del_flag", 0);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue