chore(assets): 参赛提交规范红线修复(ASCII 化 + 相对路径)

按《参赛成果物提交规范·赛道一》§6 红线:
- samples/ 目录改名 sample/(git mv,保留历史)
- 10 个中日文样本文件 + docs 参赛手册 PDF 重命名为 ASCII
  (requirements_*/template_*/rules_*/contestant-handbook.pdf)
- tests/test_zh_template.py 硬编码绝对路径 D:\00_project\Genesis 改为相对路径
- 全局更新 21 个活动文件引用;历史日志/审查文档不改(追加说明记录)
全量 pytest 431 passed / 99.15%
This commit is contained in:
lhl
2026-08-26 14:15:52 +08:00
parent 1925ef7239
commit becd3e1f57
398 changed files with 286 additions and 72 deletions
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.stock</groupId>
<artifactId>stock-trade-system</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>trade-security</artifactId>
<packaging>jar</packaging>
<name>trade-security</name>
<description>Security module for the trading system (Spring Security, JWT)</description>
<dependencies>
<!-- Spring Boot Starter Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Boot Starter Web (needed for security configurations in a web context) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JJWT for JWT support -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Hutool (for utility functions, e.g., in security utils) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- Fastjson for JSON processing -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version> <!-- Use a recent, stable version -->
</dependency>
<!-- trade-common (for common DTOs, e.g., UserDetails or custom principal) -->
<dependency>
<groupId>com.trade</groupId>
<artifactId>trade-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Redis (Optional, if using Redis for session management or token storage) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip> <!-- This is a library module, not an executable jar -->
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,18 @@
package com.trade.security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 安全模块启动类
*/
@EnableDiscoveryClient
@SpringBootApplication
public class TradeSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(TradeSecurityApplication.class, args);
}
}
@@ -0,0 +1,173 @@
package com.trade.security.config;
import com.trade.common.constant.CommonConstant;
import com.trade.security.filter.JwtAuthenticationTokenFilter;
import com.trade.security.handler.CustomAccessDeniedHandler;
import com.trade.security.handler.CustomAuthenticationEntryPoint;
import com.trade.security.handler.CustomAuthenticationFailureHandler;
import com.trade.security.handler.CustomAuthenticationSuccessHandler;
import com.trade.security.handler.CustomLogoutSuccessHandler;
import com.trade.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* Spring Security 配置类
*
* @author Trade Team
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
@Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
@Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
@Autowired
private CustomAccessDeniedHandler customAccessDeniedHandler;
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
/**
* 配置密码编码器
*
* @return PasswordEncoder 实例
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置认证管理器
*
* @return AuthenticationManager 实例
* @throws Exception 配置异常
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 配置认证管理器构建器
*
* @param auth AuthenticationManagerBuilder 实例
* @throws Exception 配置异常
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
/**
* 配置HTTP安全性
*
* @param http HttpSecurity 实例
* @throws Exception 配置异常
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// CSRF禁用,因为不使用session
.csrf().disable()
// 禁用HTTP响应标头
.headers().cacheControl().disable().and()
// 认证失败处理类
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.accessDeniedHandler(customAccessDeniedHandler)
.and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login /user/register 注册permitAll
.antMatchers(HttpMethod.POST, CommonConstant.LOGIN_URI, "/user/register").permitAll()
// 静态资源,例如htmlcssjs, swagger
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/swagger-resources/**",
"/v3/api-docs/**",
"/webjars/**",
"/druid/**",
"/favicon.ico"
).permitAll()
// 对于options请求全部放行
.antMatchers(HttpMethod.OPTIONS).permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
// 添加JWT filter
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
http.addFilterBefore(corsFilter(), JwtAuthenticationTokenFilter.class);
http.addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class);
// 配置登录
http.formLogin()
.loginProcessingUrl(CommonConstant.LOGIN_URI) // 自定义登录URL
.successHandler(customAuthenticationSuccessHandler)
.failureHandler(customAuthenticationFailureHandler)
.permitAll();
// 配置登出
http.logout()
.logoutUrl(CommonConstant.LOGOUT_URI) // 自定义登出URL
.logoutSuccessHandler(customLogoutSuccessHandler)
.permitAll();
}
/**
* 配置CORS过滤器
*
* @return CorsFilter 实例
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置允许的源,*表示允许所有源,生产环境建议指定具体的源
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
@@ -0,0 +1,88 @@
package com.trade.security.filter;
import com.trade.common.constant.CommonConstant;
import com.trade.common.util.JwtUtils;
import com.trade.security.service.UserDetailsServiceImpl;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* JWT认证令牌过滤器
* <p>
* 该过滤器在每个请求中检查JWT令牌的有效性,如果令牌有效,则将认证信息设置到Spring Security上下文中。
* </p>
*
* @author Trade Team
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsServiceImpl userDetailsService;
/**
* 执行过滤器逻辑。
*
* @param request HTTP请求
* @param response HTTP响应
* @param filterChain 过滤器链
* @throws ServletException Servlet异常
* @throws IOException IO异常
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(CommonConstant.TOKEN_HEADER);
if (StringUtils.hasText(authHeader) && authHeader.startsWith(CommonConstant.TOKEN_PREFIX)) {
String authToken = authHeader.substring(CommonConstant.TOKEN_PREFIX.length());
try {
if (jwtUtils.validateToken(authToken)) {
String username = jwtUtils.getUsernameFromToken(authToken);
LOGGER.info("Authenticated user: {}, setting security context", username);
// 当token存在并且有效时,设置Spring Security上下文
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtils.validateToken(authToken, userDetails)) { // 再次校验token是否与userDetails匹配
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
LOGGER.debug("User '{}' set in security context", username);
} else {
LOGGER.warn("Token validation failed for user '{}' against UserDetails.", username);
}
} else if (username == null) {
LOGGER.warn("Username from token is null.");
}
} else {
LOGGER.warn("Invalid JWT token: {}", authToken);
}
} catch (Exception e) {
LOGGER.error("Error processing JWT token: {}", e.getMessage());
// 可以选择清除SecurityContext,以防部分认证信息残留
// SecurityContextHolder.clearContext();
}
}
filterChain.doFilter(request, response);
}
}
@@ -0,0 +1,54 @@
package com.trade.security.handler;
import com.alibaba.fastjson.JSON;
import com.trade.common.enums.ResultCodeEnum;
import com.trade.common.vo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义访问拒绝处理器
* <p>
* 当已认证的用户尝试访问其没有权限的资源时,此处理器被调用。
* 它负责返回一个表示访问被拒绝的响应。
* </p>
*
* @author Trade Team
*/
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
/**
* 处理访问被拒绝的情况。
*
* @param request HTTP请求
* @param response HTTP响应
* @param accessDeniedException 访问被拒绝异常
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
LOGGER.warn("Access denied for user '{}' to '{}': {}",
request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : "anonymous",
request.getRequestURI(),
accessDeniedException.getMessage());
Result<Void> result = Result.failure(ResultCodeEnum.FORBIDDEN, "您没有权限访问该资源");
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write(JSON.toJSONString(result));
response.getWriter().flush();
}
}
@@ -0,0 +1,51 @@
package com.trade.security.handler;
import com.alibaba.fastjson.JSON;
import com.trade.common.enums.ResultCodeEnum;
import com.trade.common.vo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义认证入口点
* <p>
* 当匿名用户尝试访问受保护的资源而未提供有效的认证凭证时,此处理器被调用。
* 它负责返回一个表示需要认证的响应。
* </p>
*
* @author Trade Team
*/
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
/**
* 开始认证过程。
*
* @param request HTTP请求
* @param response HTTP响应
* @param authException 认证异常
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
LOGGER.warn("Unauthorized access attempt to '{}': {}", request.getRequestURI(), authException.getMessage());
Result<Void> result = Result.failure(ResultCodeEnum.UNAUTHORIZED, "请求未授权,请先登录");
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(JSON.toJSONString(result));
response.getWriter().flush();
}
}
@@ -0,0 +1,50 @@
package com.trade.security.handler;
import com.alibaba.fastjson.JSON;
import com.trade.common.enums.ResultCodeEnum;
import com.trade.common.vo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义认证失败处理器
* <p>
* 当用户登录失败时,此处理器负责返回统一格式的错误响应。
* </p>
*
* @author Trade Team
*/
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
/**
* 处理认证失败。
*
* @param request HTTP请求
* @param response HTTP响应
* @param exception 认证异常
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
LOGGER.warn("Authentication failed: {}", exception.getMessage());
Result<Void> result = Result.failure(ResultCodeEnum.LOGIN_FAILURE, exception.getMessage());
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 可以根据具体异常类型设置不同的状态码
response.getWriter().write(JSON.toJSONString(result));
response.getWriter().flush();
}
}
@@ -0,0 +1,65 @@
package com.trade.security.handler;
import com.alibaba.fastjson.JSON;
import com.trade.common.constant.CommonConstant;
import com.trade.common.enums.ResultCodeEnum;
import com.trade.common.util.JwtUtils;
import com.trade.common.vo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义认证成功处理器
* <p>
* 当用户成功登录后,此处理器负责生成JWT并将其返回给客户端。
* </p>
*
* @author Trade Team
*/
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationSuccessHandler.class);
@Autowired
private JwtUtils jwtUtils;
/**
* 处理认证成功。
*
* @param request HTTP请求
* @param response HTTP响应
* @param authentication 认证信息
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String username = userDetails.getUsername();
String token = jwtUtils.generateToken(userDetails);
LOGGER.info("User '{}' authenticated successfully. Generating JWT token.", username);
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put(CommonConstant.TOKEN_HEADER_PREFIX_WITH_SPACE.trim(), token);
Result<Map<String, String>> result = Result.success(ResultCodeEnum.LOGIN_SUCCESS, tokenMap);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(result));
response.getWriter().flush();
}
}
@@ -0,0 +1,56 @@
package com.trade.security.handler;
import com.alibaba.fastjson.JSON;
import com.trade.common.enums.ResultCodeEnum;
import com.trade.common.vo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义登出成功处理器
* <p>
* 当用户成功登出后,此处理器负责返回统一格式的成功响应。
* </p>
*
* @author Trade Team
*/
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomLogoutSuccessHandler.class);
/**
* 处理登出成功。
*
* @param request HTTP请求
* @param response HTTP响应
* @param authentication 认证信息(可能为null,如果会话已失效)
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
if (authentication != null && authentication.getDetails() != null) {
try {
request.getSession().invalidate();
LOGGER.info("User '{}' logged out successfully.", authentication.getName());
} catch (Exception e) {
LOGGER.error("Error invalidating session during logout for user '{}': {}", authentication.getName(), e.getMessage());
}
}
Result<Void> result = Result.success(ResultCodeEnum.LOGOUT_SUCCESS);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(result));
response.getWriter().flush();
}
}
@@ -0,0 +1,126 @@
package com.trade.security.service;
import com.trade.common.exception.BusinessException;
import com.trade.common.enums.ResultCodeEnum;
// import com.trade.user.feign.UserFeignClient; // 假设存在用户服务的Feign客户端
// import com.trade.user.dto.UserDTO; // 假设用户服务返回的用户DTO
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 用户详细信息服务实现类
* <p>
* 该类负责从用户服务加载用户详细信息,用于Spring Security的认证过程。
* </p>
*
* @author Trade Team
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
// @Autowired
// private UserFeignClient userFeignClient; // 注入用户服务的Feign客户端,用于远程调用获取用户信息
/**
* 根据用户名加载用户详细信息。
*
* @param username 用户名
* @return UserDetails 用户详细信息对象
* @throws UsernameNotFoundException 如果用户未找到
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 模拟通过Feign客户端调用用户服务获取用户信息
// 在实际项目中,这里会调用 userFeignClient.getUserByUsername(username) 等方法
// UserDTO userDTO = userFeignClient.getUserByUsername(username).getData(); // 假设Feign接口返回Result<UserDTO>
// ---- 以下为模拟数据,实际项目中应替换为真实的Feign调用 ----
com.trade.security.model.User mockUser = findMockUserByUsername(username);
if (mockUser == null) {
throw new UsernameNotFoundException(ResultCodeEnum.USER_NOT_EXIST.getMessage());
}
// ---- 模拟数据结束 ----
// if (userDTO == null) {
// throw new UsernameNotFoundException(ResultCodeEnum.USER_NOT_EXIST.getMessage());
// }
// 获取用户权限信息
// Set<String> permissions = userFeignClient.getUserPermissions(userDTO.getId()).getData(); // 假设Feign接口返回Result<Set<String>>
// Set<GrantedAuthority> authorities = permissions.stream()
// .map(SimpleGrantedAuthority::new)
// .collect(Collectors.toSet());
// ---- 以下为模拟权限数据 ----
Set<GrantedAuthority> authorities = new HashSet<>();
if ("admin".equals(mockUser.getUsername())) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
authorities.add(new SimpleGrantedAuthority("user:list"));
authorities.add(new SimpleGrantedAuthority("user:create"));
authorities.add(new SimpleGrantedAuthority("user:update"));
authorities.add(new SimpleGrantedAuthority("user:delete"));
} else if ("user".equals(mockUser.getUsername())){
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
authorities.add(new SimpleGrantedAuthority("order:create"));
authorities.add(new SimpleGrantedAuthority("order:list"));
}
// ---- 模拟权限数据结束 ----
return new User(
mockUser.getUsername(),
mockUser.getPassword(),
mockUser.isEnabled(),
mockUser.isAccountNonExpired(),
mockUser.isCredentialsNonExpired(),
mockUser.isAccountNonLocked(),
authorities
);
}
/**
* 模拟根据用户名查找用户(实际项目中应通过数据库或用户服务获取)
*
* @param username 用户名
* @return 模拟的用户对象,如果未找到则返回null
*/
private com.trade.security.model.User findMockUserByUsername(String username) {
// 实际项目中,这里应该调用
// com.trade.user.mapper.UserMapper.selectOne(new QueryWrapper<com.trade.user.model.User>().eq("username", username));
// 或者通过 Feign Client 调用用户微服务
if ("admin".equals(username)) {
com.trade.security.model.User adminUser = new com.trade.security.model.User();
adminUser.setId(1L);
adminUser.setUsername("admin");
// 密码 "password" 使用 BCryptPasswordEncoder 加密后的结果, 在实际应用中,数据库存储的应该是加密后的密码
// 可以使用 new BCryptPasswordEncoder().encode("password") 生成
adminUser.setPassword("$2a$10$EipD5Q5X.YR4V2/A9A7hUuL9gN.o0g8f21nQn2N1n.B0g8f21nQn"); // 假设这是 "password" 加密后的值
adminUser.setEnabled(true);
adminUser.setAccountNonExpired(true);
adminUser.setCredentialsNonExpired(true);
adminUser.setAccountNonLocked(true);
return adminUser;
} else if ("user".equals(username)) {
com.trade.security.model.User normalUser = new com.trade.security.model.User();
normalUser.setId(2L);
normalUser.setUsername("user");
normalUser.setPassword("$2a$10$EipD5Q5X.YR4V2/A9A7hUuL9gN.o0g8f21nQn2N1n.B0g8f21nQn"); // 假设这是 "password" 加密后的值
normalUser.setEnabled(true);
normalUser.setAccountNonExpired(true);
normalUser.setCredentialsNonExpired(true);
normalUser.setAccountNonLocked(true);
return normalUser;
}
return null;
}
}
@@ -0,0 +1,14 @@
server:
port: 8088
spring:
profiles:
active: dev
application:
name: trade-security
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yml
@@ -0,0 +1,14 @@
server:
port: 8088
spring:
profiles:
active: prod
application:
name: trade-security
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yml
@@ -0,0 +1,14 @@
server:
port: 8088
spring:
profiles:
active: test
application:
name: trade-security
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yml
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="com.trade.security" level="debug"/>
</configuration>