service auth
This commit is contained in:
@@ -12,10 +12,132 @@
|
|||||||
<groupId>org.xyzh</groupId>
|
<groupId>org.xyzh</groupId>
|
||||||
<artifactId>auth</artifactId>
|
<artifactId>auth</artifactId>
|
||||||
<version>${school-news.version}</version>
|
<version>${school-news.version}</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 公共模块依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>common-core</artifactId>
|
||||||
|
<version>${school-news.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>common-dto</artifactId>
|
||||||
|
<version>${school-news.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>common-exception</artifactId>
|
||||||
|
<version>${school-news.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>common-redis</artifactId>
|
||||||
|
<version>${school-news.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>common-util</artifactId>
|
||||||
|
<version>${school-news.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>api-auth</artifactId>
|
||||||
|
<version>${school-news.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh</groupId>
|
||||||
|
<artifactId>api-system</artifactId>
|
||||||
|
<version>${school-news.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Spring Boot Starter -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<!-- 排除默认的logback依赖 -->
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Security -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
<!-- 排除默认的logback依赖 -->
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Log4j2 日志依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JWT 依赖 -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- 配置处理 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 数据库 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jdbc</artifactId>
|
||||||
|
<!-- 排除默认的logback依赖 -->
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package org.xyzh;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
System.out.println("Hello world!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package org.xyzh.auth.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description AuthProperties.java文件描述 认证配置属性
|
||||||
|
* @filename AuthProperties.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "school-news.auth")
|
||||||
|
public class AuthProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 免登录白名单路径
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private List<String> whiteList = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description JWT密钥
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private String jwtSecret = "schoolNewsDefaultSecretKeyForJWT2025";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description JWT过期时间(秒)
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private Long jwtExpiration = 86400L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 最大登录失败次数
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private Integer maxLoginAttempts = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 账户锁定时间(分钟)
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private Integer lockoutDuration = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取白名单
|
||||||
|
* @return List<String> 白名单路径列表
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public List<String> getWhiteList() {
|
||||||
|
return whiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置白名单
|
||||||
|
* @param whiteList 白名单路径列表
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public void setWhiteList(List<String> whiteList) {
|
||||||
|
this.whiteList = whiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取JWT密钥
|
||||||
|
* @return String JWT密钥
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public String getJwtSecret() {
|
||||||
|
return jwtSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置JWT密钥
|
||||||
|
* @param jwtSecret JWT密钥
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public void setJwtSecret(String jwtSecret) {
|
||||||
|
this.jwtSecret = jwtSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取JWT过期时间
|
||||||
|
* @return Long JWT过期时间(秒)
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public Long getJwtExpiration() {
|
||||||
|
return jwtExpiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置JWT过期时间
|
||||||
|
* @param jwtExpiration JWT过期时间(秒)
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public void setJwtExpiration(Long jwtExpiration) {
|
||||||
|
this.jwtExpiration = jwtExpiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取最大登录失败次数
|
||||||
|
* @return Integer 最大登录失败次数
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public Integer getMaxLoginAttempts() {
|
||||||
|
return maxLoginAttempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置最大登录失败次数
|
||||||
|
* @param maxLoginAttempts 最大登录失败次数
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public void setMaxLoginAttempts(Integer maxLoginAttempts) {
|
||||||
|
this.maxLoginAttempts = maxLoginAttempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取账户锁定时间
|
||||||
|
* @return Integer 账户锁定时间(分钟)
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public Integer getLockoutDuration() {
|
||||||
|
return lockoutDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置账户锁定时间
|
||||||
|
* @param lockoutDuration 账户锁定时间(分钟)
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public void setLockoutDuration(Integer lockoutDuration) {
|
||||||
|
this.lockoutDuration = lockoutDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package org.xyzh.auth.config;
|
||||||
|
|
||||||
|
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.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
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.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
import org.xyzh.auth.filter.JwtAuthenticationFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description SecurityConfig.java文件描述 Spring Security配置
|
||||||
|
* @filename SecurityConfig.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthProperties authProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 密码编码器
|
||||||
|
* @return PasswordEncoder 密码编码器
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 安全过滤器链配置
|
||||||
|
* @param http HTTP安全配置
|
||||||
|
* @return SecurityFilterChain 安全过滤器链
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// 白名单路径转换为数组
|
||||||
|
String[] whiteListArray = authProperties.getWhiteList().toArray(new String[0]);
|
||||||
|
|
||||||
|
http
|
||||||
|
// 禁用CSRF
|
||||||
|
.csrf(csrf -> csrf.disable())
|
||||||
|
// 无状态session
|
||||||
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
|
// 配置授权规则
|
||||||
|
.authorizeHttpRequests(authz -> authz
|
||||||
|
.requestMatchers(whiteListArray).permitAll()
|
||||||
|
.requestMatchers("/auth/login", "/auth/logout", "/auth/captcha").permitAll()
|
||||||
|
.requestMatchers("/actuator/**").permitAll()
|
||||||
|
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
|
||||||
|
.requestMatchers("/favicon.ico", "/error").permitAll()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
// 添加JWT过滤器
|
||||||
|
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package org.xyzh.auth.controller;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.xyzh.api.auth.login.LoginService;
|
||||||
|
import org.xyzh.common.core.domain.LoginParam;
|
||||||
|
import org.xyzh.common.core.domain.LoginDomain;
|
||||||
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
|
import org.xyzh.common.utils.IDUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description AuthController.java文件描述 认证控制器
|
||||||
|
* @filename AuthController.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/auth")
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LoginService loginService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 用户登录
|
||||||
|
* @param loginParam 登录参数
|
||||||
|
* @return ResultDomain<String> 登录结果
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResultDomain<String> login(@RequestBody LoginParam loginParam) {
|
||||||
|
return loginService.login(loginParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 用户退出登录
|
||||||
|
* @param loginDomain 登录域对象
|
||||||
|
* @return ResultDomain<String> 退出结果
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public ResultDomain<String> logout(@RequestBody LoginDomain loginDomain) {
|
||||||
|
return loginService.logout(loginDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取验证码
|
||||||
|
* @return ResultDomain<String> 验证码
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@GetMapping("/captcha")
|
||||||
|
public ResultDomain<String> getCaptcha() {
|
||||||
|
// TODO: 实现验证码生成逻辑
|
||||||
|
ResultDomain<String> result = new ResultDomain<>();
|
||||||
|
|
||||||
|
// 生成验证码会话ID,用于验证时匹配
|
||||||
|
String captchaId = IDUtils.generateID();
|
||||||
|
String captchaData = captchaId + ":captcha-placeholder"; // 格式: ID:验证码内容
|
||||||
|
|
||||||
|
result.success("验证码获取成功", captchaData);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 刷新令牌
|
||||||
|
* @param token 原令牌
|
||||||
|
* @return ResultDomain<String> 新令牌
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@PostMapping("/refresh")
|
||||||
|
public ResultDomain<String> refreshToken(@RequestHeader("Authorization") String token) {
|
||||||
|
// TODO: 实现令牌刷新逻辑
|
||||||
|
ResultDomain<String> result = new ResultDomain<>();
|
||||||
|
|
||||||
|
// 为新令牌生成唯一ID
|
||||||
|
String newTokenId = IDUtils.generateID();
|
||||||
|
String newToken = "new-token-" + newTokenId; // 临时占位符
|
||||||
|
|
||||||
|
result.success("令牌刷新成功", newToken);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 健康检查
|
||||||
|
* @return ResultDomain<String> 健康状态
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@GetMapping("/health")
|
||||||
|
public ResultDomain<String> health() {
|
||||||
|
ResultDomain<String> result = new ResultDomain<>();
|
||||||
|
result.success("认证服务运行正常", "OK");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package org.xyzh.auth.domain;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.common.dto.dept.TbSysDeptRole;
|
||||||
|
import org.xyzh.common.dto.permission.TbSysPermission;
|
||||||
|
import org.xyzh.common.core.enums.UserStatus;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description UserPrincipal.java文件描述 用户主体类
|
||||||
|
* @filename UserPrincipal.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public class UserPrincipal implements UserDetails {
|
||||||
|
|
||||||
|
private TbSysUser user;
|
||||||
|
private List<TbSysDeptRole> roles;
|
||||||
|
private List<TbSysPermission> permissions;
|
||||||
|
|
||||||
|
private UserPrincipal(TbSysUser user, List<TbSysDeptRole> roles, List<TbSysPermission> permissions) {
|
||||||
|
this.user = user;
|
||||||
|
this.roles = roles;
|
||||||
|
this.permissions = permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserPrincipal create(TbSysUser user, List<TbSysDeptRole> roles, List<TbSysPermission> permissions) {
|
||||||
|
return new UserPrincipal(user, roles, permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
// 角色权限
|
||||||
|
List<GrantedAuthority> roleAuthorities = roles.stream()
|
||||||
|
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getID()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 功能权限
|
||||||
|
List<GrantedAuthority> permissionAuthorities = permissions.stream()
|
||||||
|
.map(permission -> new SimpleGrantedAuthority(permission.getCode()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return Stream.concat(roleAuthorities.stream(), permissionAuthorities.stream())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return user.getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return user.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
UserStatus status = UserStatus.fromCode(String.valueOf(user.getStatus()));
|
||||||
|
return status != UserStatus.LOCKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
UserStatus status = UserStatus.fromCode(String.valueOf(user.getStatus()));
|
||||||
|
return status == UserStatus.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TbSysUser getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TbSysDeptRole> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TbSysPermission> getPermissions() {
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package org.xyzh.auth.filter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
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 org.xyzh.auth.service.UserDetailsServiceImpl;
|
||||||
|
import org.xyzh.auth.util.JwtTokenUtil;
|
||||||
|
import org.xyzh.auth.config.AuthProperties;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description JwtAuthenticationFilter.java文件描述 JWT认证过滤器
|
||||||
|
* @filename JwtAuthenticationFilter.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtTokenUtil jwtTokenUtil;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsServiceImpl userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthProperties authProperties;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
|
||||||
|
// 检查是否在白名单中
|
||||||
|
if (isWhitelisted(requestURI)) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = getTokenFromRequest(request);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(token)) {
|
||||||
|
try {
|
||||||
|
String userId = jwtTokenUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUserId(userId);
|
||||||
|
|
||||||
|
if (jwtTokenUtil.validateToken(token, userId)) {
|
||||||
|
UsernamePasswordAuthenticationToken authentication =
|
||||||
|
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
||||||
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("JWT token validation failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从请求中获取Token
|
||||||
|
* @param request HTTP请求
|
||||||
|
* @return String Token
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private String getTokenFromRequest(HttpServletRequest request) {
|
||||||
|
String bearerToken = request.getHeader("Authorization");
|
||||||
|
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||||
|
return bearerToken.substring(7);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 检查请求路径是否在白名单中
|
||||||
|
* @param requestURI 请求URI
|
||||||
|
* @return boolean 是否在白名单中
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private boolean isWhitelisted(String requestURI) {
|
||||||
|
return authProperties.getWhiteList().stream()
|
||||||
|
.anyMatch(whitePath -> requestURI.matches(whitePath.replace("*", ".*")));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package org.xyzh.auth.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.xyzh.common.dto.log.TbSysLoginLog;
|
||||||
|
import org.xyzh.common.utils.IDUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description LoginLogService.java文件描述 登录日志服务
|
||||||
|
* @filename LoginLogService.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class LoginLogService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 保存登录日志
|
||||||
|
* @param loginLog 登录日志
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public void saveLoginLog(TbSysLoginLog loginLog) {
|
||||||
|
// 确保登录日志有ID,如果没有则生成一个
|
||||||
|
if (loginLog.getID() == null || loginLog.getID().isEmpty()) {
|
||||||
|
loginLog.setID(IDUtils.generateID());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 实现登录日志保存逻辑
|
||||||
|
// 这里应该调用数据层保存日志
|
||||||
|
System.out.println("保存登录日志: " + loginLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据用户ID查询登录日志
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return List<TbSysLoginLog> 登录日志列表
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public java.util.List<TbSysLoginLog> findLoginLogsByUserId(String userId) {
|
||||||
|
// TODO: 实现根据用户ID查询登录日志的逻辑
|
||||||
|
return new java.util.ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 查询登录失败次数
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param timeRange 时间范围(分钟)
|
||||||
|
* @return int 失败次数
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public int countFailedLoginAttempts(String userId, int timeRange) {
|
||||||
|
// TODO: 实现查询指定时间范围内的登录失败次数
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 清除登录失败记录
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public void clearFailedLoginAttempts(String userId) {
|
||||||
|
// TODO: 实现清除登录失败记录的逻辑
|
||||||
|
System.out.println("清除用户 " + userId + " 的登录失败记录");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
package org.xyzh.auth.service;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.xyzh.api.auth.login.LoginService;
|
||||||
|
import org.xyzh.auth.strategy.LoginStrategy;
|
||||||
|
import org.xyzh.auth.strategy.LoginStrategyFactory;
|
||||||
|
import org.xyzh.auth.util.JwtTokenUtil;
|
||||||
|
import org.xyzh.common.core.domain.LoginParam;
|
||||||
|
import org.xyzh.common.core.domain.LoginDomain;
|
||||||
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
|
import org.xyzh.common.core.enums.UserStatus;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.common.dto.log.TbSysLoginLog;
|
||||||
|
import org.xyzh.common.exception.auth.AuthException;
|
||||||
|
import org.xyzh.common.utils.IDUtils;
|
||||||
|
import org.xyzh.api.system.user.UserService;
|
||||||
|
import org.xyzh.api.system.role.RoleService;
|
||||||
|
import org.xyzh.api.system.permission.PermissionService;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description LoginServiceImpl.java文件描述 登录服务实现
|
||||||
|
* @filename LoginServiceImpl.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class LoginServiceImpl implements LoginService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LoginStrategyFactory loginStrategyFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private RoleService roleService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private PermissionService permissionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtTokenUtil jwtTokenUtil;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LoginLogService loginLogService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultDomain<String> login(LoginParam loginParam) {
|
||||||
|
ResultDomain<String> result = new ResultDomain<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 自动检测登录类型
|
||||||
|
String loginType = detectLoginType(loginParam);
|
||||||
|
loginParam.setLoginType(loginType);
|
||||||
|
|
||||||
|
// 获取对应的登录策略
|
||||||
|
LoginStrategy strategy = loginStrategyFactory.getStrategy(loginType);
|
||||||
|
|
||||||
|
// 验证登录参数
|
||||||
|
if (!strategy.validate(loginParam)) {
|
||||||
|
result.fail("登录参数不正确");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找用户
|
||||||
|
TbSysUser user = strategy.findUser(loginParam);
|
||||||
|
if (user == null) {
|
||||||
|
result.fail("用户不存在");
|
||||||
|
logLoginAttempt(loginParam, null, false, "用户不存在");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户状态
|
||||||
|
UserStatus userStatus = UserStatus.fromCode(String.valueOf(user.getStatus()));
|
||||||
|
if (userStatus != UserStatus.NORMAL) {
|
||||||
|
result.fail("用户状态异常: " + userStatus.getName());
|
||||||
|
logLoginAttempt(loginParam, user, false, "用户状态异常: " + userStatus.getName());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
if (!strategy.verifyPassword(loginParam.getPassword(), user.getPassword())) {
|
||||||
|
result.fail("密码错误");
|
||||||
|
logLoginAttempt(loginParam, user, false, "密码错误");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建登录域对象
|
||||||
|
LoginDomain loginDomain = buildLoginDomain(user, loginType);
|
||||||
|
|
||||||
|
// 生成JWT令牌
|
||||||
|
String token = jwtTokenUtil.generateToken(loginDomain);
|
||||||
|
|
||||||
|
// 记录成功登录日志
|
||||||
|
logLoginAttempt(loginParam, user, true, "登录成功");
|
||||||
|
|
||||||
|
result.success("登录成功", (String)null);
|
||||||
|
result.setData(token);
|
||||||
|
|
||||||
|
} catch (AuthException e) {
|
||||||
|
result.fail(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.fail("登录失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultDomain<String> logout(LoginDomain loginDomain) {
|
||||||
|
ResultDomain<String> result = new ResultDomain<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: 将token加入黑名单或从Redis中删除
|
||||||
|
// 这里可以实现token黑名单机制
|
||||||
|
|
||||||
|
result.success("退出登录成功", (String)null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.fail("退出登录失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 自动检测登录类型
|
||||||
|
* @param loginParam 登录参数
|
||||||
|
* @return String 登录类型
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private String detectLoginType(LoginParam loginParam) {
|
||||||
|
if (StringUtils.hasText(loginParam.getLoginType())) {
|
||||||
|
return loginParam.getLoginType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(loginParam.getEmail())) {
|
||||||
|
return "email";
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(loginParam.getUsername())) {
|
||||||
|
return "username";
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(loginParam.getPhone())) {
|
||||||
|
return "phone";
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(loginParam.getWechatID())) {
|
||||||
|
return "wechat";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AuthException("INVALID_LOGIN_TYPE", "无法确定登录类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 构建登录域对象
|
||||||
|
* @param user 用户对象
|
||||||
|
* @param loginType 登录类型
|
||||||
|
* @return LoginDomain 登录域对象
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private LoginDomain buildLoginDomain(TbSysUser user, String loginType) {
|
||||||
|
LoginDomain loginDomain = new LoginDomain();
|
||||||
|
loginDomain.setUser(user);
|
||||||
|
loginDomain.setLoginType(loginType);
|
||||||
|
loginDomain.setLoginTime(new Date());
|
||||||
|
|
||||||
|
// 获取用户角色和权限(如果服务可用)
|
||||||
|
if (roleService != null) {
|
||||||
|
try {
|
||||||
|
// TODO: 需要在RoleService中实现findRolesByUserId方法
|
||||||
|
// loginDomain.setRoles(roleService.findRolesByUserId(user.getID()));
|
||||||
|
loginDomain.setRoles(new ArrayList<>());
|
||||||
|
} catch (Exception e) {
|
||||||
|
loginDomain.setRoles(new ArrayList<>());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loginDomain.setRoles(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissionService != null) {
|
||||||
|
try {
|
||||||
|
// TODO: 需要在PermissionService中实现findPermissionsByUserId方法
|
||||||
|
// loginDomain.setPermissions(permissionService.findPermissionsByUserId(user.getID()));
|
||||||
|
loginDomain.setPermissions(new ArrayList<>());
|
||||||
|
} catch (Exception e) {
|
||||||
|
loginDomain.setPermissions(new ArrayList<>());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loginDomain.setPermissions(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return loginDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 记录登录日志
|
||||||
|
* @param loginParam 登录参数
|
||||||
|
* @param user 用户对象
|
||||||
|
* @param success 是否成功
|
||||||
|
* @param message 消息
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private void logLoginAttempt(LoginParam loginParam, TbSysUser user, boolean success, String message) {
|
||||||
|
TbSysLoginLog loginLog = new TbSysLoginLog();
|
||||||
|
|
||||||
|
// 使用IDUtils生成登录日志ID
|
||||||
|
loginLog.setID(IDUtils.generateID());
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
loginLog.setUserID(user.getID());
|
||||||
|
loginLog.setUsername(user.getUsername());
|
||||||
|
}
|
||||||
|
// 注意:实际生产中不应记录密码
|
||||||
|
// loginLog.setPassword(loginParam.getPassword());
|
||||||
|
loginLog.setStatus(success ? 1 : 0);
|
||||||
|
loginLog.setMessage(message);
|
||||||
|
loginLog.setLoginTime(new Date().toString());
|
||||||
|
|
||||||
|
loginLogService.saveLoginLog(loginLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package org.xyzh.auth.service;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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 org.xyzh.auth.domain.UserPrincipal;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.common.dto.dept.TbSysDeptRole;
|
||||||
|
import org.xyzh.common.dto.permission.TbSysPermission;
|
||||||
|
import org.xyzh.api.system.user.UserService;
|
||||||
|
import org.xyzh.api.system.role.RoleService;
|
||||||
|
import org.xyzh.api.system.permission.PermissionService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description UserDetailsServiceImpl.java文件描述 用户详情服务实现
|
||||||
|
* @filename UserDetailsServiceImpl.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private RoleService roleService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private PermissionService permissionService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
TbSysUser filter = new TbSysUser();
|
||||||
|
filter.setUsername(username);
|
||||||
|
TbSysUser user = userService.find(filter);
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
throw new UsernameNotFoundException("用户不存在: " + username);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadUserByUserId(user.getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据用户ID加载用户详情
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return UserDetails 用户详情
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public UserDetails loadUserByUserId(String userId) {
|
||||||
|
TbSysUser filter = new TbSysUser();
|
||||||
|
filter.setID(userId);
|
||||||
|
TbSysUser user = userService.find(filter);
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
throw new UsernameNotFoundException("用户不存在: " + userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户角色(如果角色服务可用)
|
||||||
|
List<TbSysDeptRole> roles = new ArrayList<>();
|
||||||
|
if (roleService != null) {
|
||||||
|
try {
|
||||||
|
// TODO: 需要在RoleService中实现findRolesByUserId方法
|
||||||
|
// roles = roleService.findRolesByUserId(userId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("无法获取用户角色: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户权限(如果权限服务可用)
|
||||||
|
List<TbSysPermission> permissions = new ArrayList<>();
|
||||||
|
if (permissionService != null) {
|
||||||
|
try {
|
||||||
|
// TODO: 需要在PermissionService中实现findPermissionsByUserId方法
|
||||||
|
// permissions = permissionService.findPermissionsByUserId(userId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("无法获取用户权限: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserPrincipal.create(user, roles, permissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package org.xyzh.auth.strategy;
|
||||||
|
|
||||||
|
import org.xyzh.common.core.domain.LoginParam;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description LoginStrategy.java文件描述 登录策略接口
|
||||||
|
* @filename LoginStrategy.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public interface LoginStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 支持的登录类型
|
||||||
|
* @return String 登录类型
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
String getLoginType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 验证登录参数
|
||||||
|
* @param loginParam 登录参数
|
||||||
|
* @return boolean 是否有效
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
boolean validate(LoginParam loginParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据登录参数查找用户
|
||||||
|
* @param loginParam 登录参数
|
||||||
|
* @return TbSysUser 用户对象
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
TbSysUser findUser(LoginParam loginParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 验证密码
|
||||||
|
* @param inputPassword 输入密码
|
||||||
|
* @param storedPassword 存储密码
|
||||||
|
* @return boolean 是否匹配
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
boolean verifyPassword(String inputPassword, String storedPassword);
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package org.xyzh.auth.strategy;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.xyzh.common.exception.auth.AuthException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description LoginStrategyFactory.java文件描述 登录策略工厂
|
||||||
|
* @filename LoginStrategyFactory.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class LoginStrategyFactory {
|
||||||
|
|
||||||
|
private final Map<String, LoginStrategy> strategies;
|
||||||
|
|
||||||
|
public LoginStrategyFactory(List<LoginStrategy> loginStrategies) {
|
||||||
|
this.strategies = loginStrategies.stream()
|
||||||
|
.collect(Collectors.toMap(LoginStrategy::getLoginType, Function.identity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取登录策略
|
||||||
|
* @param loginType 登录类型
|
||||||
|
* @return LoginStrategy 登录策略
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public LoginStrategy getStrategy(String loginType) {
|
||||||
|
LoginStrategy strategy = strategies.get(loginType);
|
||||||
|
if (strategy == null) {
|
||||||
|
throw new AuthException("UNSUPPORTED_LOGIN_TYPE", "不支持的登录类型: " + loginType);
|
||||||
|
}
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取所有支持的登录类型
|
||||||
|
* @return Set<String> 登录类型集合
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public java.util.Set<String> getSupportedLoginTypes() {
|
||||||
|
return strategies.keySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.xyzh.auth.strategy.impl;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.xyzh.auth.strategy.LoginStrategy;
|
||||||
|
import org.xyzh.common.core.domain.LoginParam;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.api.system.user.UserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description EmailLoginStrategy.java文件描述 邮箱登录策略
|
||||||
|
* @filename EmailLoginStrategy.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class EmailLoginStrategy implements LoginStrategy {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoginType() {
|
||||||
|
return "email";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate(LoginParam loginParam) {
|
||||||
|
return loginParam.getEmail() != null && !loginParam.getEmail().trim().isEmpty()
|
||||||
|
&& loginParam.getPassword() != null && !loginParam.getPassword().trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TbSysUser findUser(LoginParam loginParam) {
|
||||||
|
TbSysUser filter = new TbSysUser();
|
||||||
|
filter.setEmail(loginParam.getEmail());
|
||||||
|
return userService.find(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyPassword(String inputPassword, String storedPassword) {
|
||||||
|
return passwordEncoder.matches(inputPassword, storedPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package org.xyzh.auth.strategy.impl;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.xyzh.auth.strategy.LoginStrategy;
|
||||||
|
import org.xyzh.common.core.domain.LoginParam;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.api.system.user.UserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description PhoneLoginStrategy.java文件描述 手机号登录策略
|
||||||
|
* @filename PhoneLoginStrategy.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class PhoneLoginStrategy implements LoginStrategy {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoginType() {
|
||||||
|
return "phone";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate(LoginParam loginParam) {
|
||||||
|
return loginParam.getPhone() != null && !loginParam.getPhone().trim().isEmpty()
|
||||||
|
&& (loginParam.getPassword() != null || loginParam.getCaptcha() != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TbSysUser findUser(LoginParam loginParam) {
|
||||||
|
TbSysUser filter = new TbSysUser();
|
||||||
|
filter.setPhone(loginParam.getPhone());
|
||||||
|
return userService.find(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyPassword(String inputPassword, String storedPassword) {
|
||||||
|
// 手机号登录可能使用验证码,如果有验证码则跳过密码验证
|
||||||
|
if (inputPassword == null) {
|
||||||
|
return true; // 假设验证码已经在其他地方验证过了
|
||||||
|
}
|
||||||
|
return passwordEncoder.matches(inputPassword, storedPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.xyzh.auth.strategy.impl;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.xyzh.auth.strategy.LoginStrategy;
|
||||||
|
import org.xyzh.common.core.domain.LoginParam;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.api.system.user.UserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description UsernameLoginStrategy.java文件描述 用户名登录策略
|
||||||
|
* @filename UsernameLoginStrategy.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class UsernameLoginStrategy implements LoginStrategy {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoginType() {
|
||||||
|
return "username";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate(LoginParam loginParam) {
|
||||||
|
return loginParam.getUsername() != null && !loginParam.getUsername().trim().isEmpty()
|
||||||
|
&& loginParam.getPassword() != null && !loginParam.getPassword().trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TbSysUser findUser(LoginParam loginParam) {
|
||||||
|
TbSysUser filter = new TbSysUser();
|
||||||
|
filter.setUsername(loginParam.getUsername());
|
||||||
|
return userService.find(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyPassword(String inputPassword, String storedPassword) {
|
||||||
|
return passwordEncoder.matches(inputPassword, storedPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.xyzh.auth.strategy.impl;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.xyzh.auth.strategy.LoginStrategy;
|
||||||
|
import org.xyzh.common.core.domain.LoginParam;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.api.system.user.UserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description WechatLoginStrategy.java文件描述 微信登录策略
|
||||||
|
* @filename WechatLoginStrategy.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class WechatLoginStrategy implements LoginStrategy {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoginType() {
|
||||||
|
return "wechat";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate(LoginParam loginParam) {
|
||||||
|
return loginParam.getWechatID() != null && !loginParam.getWechatID().trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TbSysUser findUser(LoginParam loginParam) {
|
||||||
|
TbSysUser filter = new TbSysUser();
|
||||||
|
filter.setWechatID(loginParam.getWechatID());
|
||||||
|
return userService.find(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyPassword(String inputPassword, String storedPassword) {
|
||||||
|
// 微信登录通常不需要密码验证,通过微信授权码验证
|
||||||
|
// 这里可以添加微信授权验证逻辑
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package org.xyzh.auth.util;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.*;
|
||||||
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.xyzh.common.core.domain.LoginDomain;
|
||||||
|
import org.xyzh.common.dto.user.TbSysUser;
|
||||||
|
import org.xyzh.common.utils.IDUtils;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description JwtTokenUtil.java文件描述 JWT工具类
|
||||||
|
* @filename JwtTokenUtil.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtTokenUtil {
|
||||||
|
|
||||||
|
@Value("${school-news.auth.jwt-secret:schoolNewsDefaultSecretKeyForJWT2025}")
|
||||||
|
private String secret;
|
||||||
|
|
||||||
|
@Value("${school-news.auth.jwt-expiration:86400}")
|
||||||
|
private Long expiration;
|
||||||
|
|
||||||
|
private SecretKey getSigningKey() {
|
||||||
|
return Keys.hmacShaKeyFor(secret.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 生成JWT令牌
|
||||||
|
* @param loginDomain 登录域对象
|
||||||
|
* @return String JWT令牌
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public String generateToken(LoginDomain loginDomain) {
|
||||||
|
Map<String, Object> claims = new HashMap<>();
|
||||||
|
TbSysUser user = loginDomain.getUser();
|
||||||
|
|
||||||
|
claims.put("userId", user.getID());
|
||||||
|
claims.put("username", user.getUsername());
|
||||||
|
claims.put("email", user.getEmail());
|
||||||
|
claims.put("loginType", loginDomain.getLoginType());
|
||||||
|
claims.put("ipAddress", loginDomain.getIpAddress());
|
||||||
|
|
||||||
|
return Jwts.builder()
|
||||||
|
.setClaims(claims)
|
||||||
|
.setSubject(user.getID())
|
||||||
|
.setId(IDUtils.generateID()) // 使用IDUtils生成JWT ID
|
||||||
|
.setIssuedAt(new Date())
|
||||||
|
.setExpiration(generateExpirationDate())
|
||||||
|
.signWith(getSigningKey())
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从令牌中获取用户ID
|
||||||
|
* @param token JWT令牌
|
||||||
|
* @return String 用户ID
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public String getUserIdFromToken(String token) {
|
||||||
|
return getClaimFromToken(token, Claims::getSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从令牌中获取过期时间
|
||||||
|
* @param token JWT令牌
|
||||||
|
* @return Date 过期时间
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public Date getExpirationDateFromToken(String token) {
|
||||||
|
return getClaimFromToken(token, Claims::getExpiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从令牌中获取指定信息
|
||||||
|
* @param token JWT令牌
|
||||||
|
* @param claimsResolver 信息解析器
|
||||||
|
* @return T 指定信息
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public <T> T getClaimFromToken(String token, java.util.function.Function<Claims, T> claimsResolver) {
|
||||||
|
final Claims claims = getAllClaimsFromToken(token);
|
||||||
|
return claimsResolver.apply(claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从令牌中获取所有信息
|
||||||
|
* @param token JWT令牌
|
||||||
|
* @return Claims 所有信息
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public Claims getAllClaimsFromToken(String token) {
|
||||||
|
return Jwts.parserBuilder()
|
||||||
|
.setSigningKey(getSigningKey())
|
||||||
|
.build()
|
||||||
|
.parseClaimsJws(token)
|
||||||
|
.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 验证令牌
|
||||||
|
* @param token JWT令牌
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return boolean 是否有效
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public boolean validateToken(String token, String userId) {
|
||||||
|
try {
|
||||||
|
final String tokenUserId = getUserIdFromToken(token);
|
||||||
|
return (userId.equals(tokenUserId) && !isTokenExpired(token));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 检查令牌是否过期
|
||||||
|
* @param token JWT令牌
|
||||||
|
* @return boolean 是否过期
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
public boolean isTokenExpired(String token) {
|
||||||
|
final Date expiration = getExpirationDateFromToken(token);
|
||||||
|
return expiration.before(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 生成过期时间
|
||||||
|
* @return Date 过期时间
|
||||||
|
* @author yslg
|
||||||
|
* @since 2025-09-28
|
||||||
|
*/
|
||||||
|
private Date generateExpirationDate() {
|
||||||
|
return new Date(System.currentTimeMillis() + expiration * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
schoolNewsServ/auth/src/main/resources/application.yml
Normal file
63
schoolNewsServ/auth/src/main/resources/application.yml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
server:
|
||||||
|
port: 8081
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: school-news-auth
|
||||||
|
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/school_news?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
hikari:
|
||||||
|
maximum-pool-size: 20
|
||||||
|
minimum-idle: 5
|
||||||
|
|
||||||
|
# 认证配置
|
||||||
|
school-news:
|
||||||
|
auth:
|
||||||
|
# JWT配置
|
||||||
|
jwt-secret: schoolNewsSecretKeyForJWT2025SecureEnough
|
||||||
|
jwt-expiration: 86400 # 24小时
|
||||||
|
|
||||||
|
# 安全配置
|
||||||
|
max-login-attempts: 5
|
||||||
|
lockout-duration: 30 # 锁定30分钟
|
||||||
|
|
||||||
|
# 免登录白名单
|
||||||
|
white-list:
|
||||||
|
- "/auth/login"
|
||||||
|
- "/auth/logout"
|
||||||
|
- "/auth/captcha"
|
||||||
|
- "/auth/health"
|
||||||
|
- "/actuator/**"
|
||||||
|
- "/swagger-ui/**"
|
||||||
|
- "/v3/api-docs/**"
|
||||||
|
- "/favicon.ico"
|
||||||
|
- "/error"
|
||||||
|
- "/public/**"
|
||||||
|
- "/static/**"
|
||||||
|
|
||||||
|
# 日志配置(使用log4j2配置文件)
|
||||||
|
logging:
|
||||||
|
config: classpath:log4j2-spring.xml
|
||||||
|
|
||||||
|
# 管理端点配置
|
||||||
|
management:
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
exposure:
|
||||||
|
include: health,info,metrics
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
show-details: when-authorized
|
||||||
|
|
||||||
|
# 文档配置
|
||||||
|
springdoc:
|
||||||
|
api-docs:
|
||||||
|
path: /v3/api-docs
|
||||||
|
swagger-ui:
|
||||||
|
path: /swagger-ui.html
|
||||||
|
tags-sorter: alpha
|
||||||
|
operations-sorter: alpha
|
||||||
107
schoolNewsServ/auth/src/main/resources/log4j2-spring.xml
Normal file
107
schoolNewsServ/auth/src/main/resources/log4j2-spring.xml
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
|
||||||
|
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
|
||||||
|
<configuration status="WARN" monitorInterval="30">
|
||||||
|
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
|
||||||
|
|
||||||
|
<!--变量配置-->
|
||||||
|
<Properties>
|
||||||
|
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
|
||||||
|
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
|
||||||
|
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
|
||||||
|
<!-- 定义日志存储的路径 -->
|
||||||
|
<property name="FILE_PATH" value="./logs" />
|
||||||
|
<property name="FILE_NAME" value="school-news-auth" />
|
||||||
|
</Properties>
|
||||||
|
|
||||||
|
<appenders>
|
||||||
|
|
||||||
|
<console name="Console" target="SYSTEM_OUT">
|
||||||
|
<!--输出日志的格式-->
|
||||||
|
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||||
|
<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
|
||||||
|
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||||
|
</console>
|
||||||
|
|
||||||
|
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
|
||||||
|
<File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
|
||||||
|
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||||
|
</File>
|
||||||
|
|
||||||
|
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
|
||||||
|
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
|
||||||
|
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
|
||||||
|
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||||
|
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||||
|
<Policies>
|
||||||
|
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
|
||||||
|
<TimeBasedTriggeringPolicy interval="1"/>
|
||||||
|
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||||
|
</Policies>
|
||||||
|
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
|
||||||
|
<DefaultRolloverStrategy max="15"/>
|
||||||
|
</RollingFile>
|
||||||
|
|
||||||
|
<!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
|
||||||
|
<RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
|
||||||
|
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
|
||||||
|
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||||
|
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||||
|
<Policies>
|
||||||
|
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
|
||||||
|
<TimeBasedTriggeringPolicy interval="1"/>
|
||||||
|
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||||
|
</Policies>
|
||||||
|
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
|
||||||
|
<DefaultRolloverStrategy max="15"/>
|
||||||
|
</RollingFile>
|
||||||
|
|
||||||
|
<!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
|
||||||
|
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
|
||||||
|
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
|
||||||
|
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||||
|
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||||
|
<Policies>
|
||||||
|
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
|
||||||
|
<TimeBasedTriggeringPolicy interval="1"/>
|
||||||
|
<SizeBasedTriggeringPolicy size="10MB"/>
|
||||||
|
</Policies>
|
||||||
|
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
|
||||||
|
<DefaultRolloverStrategy max="15"/>
|
||||||
|
</RollingFile>
|
||||||
|
|
||||||
|
</appenders>
|
||||||
|
|
||||||
|
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
|
||||||
|
<!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
|
||||||
|
<loggers>
|
||||||
|
|
||||||
|
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
|
||||||
|
<logger name="org.mybatis" level="info" additivity="false">
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
</logger>
|
||||||
|
<!--监控系统信息-->
|
||||||
|
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,不会在 父Logger 的appender里输出。-->
|
||||||
|
<Logger name="org.springframework" level="info" additivity="false">
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
</Logger>
|
||||||
|
|
||||||
|
<!-- 项目包日志配置 -->
|
||||||
|
<Logger name="org.xyzh" level="debug" additivity="false">
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
<AppenderRef ref="Filelog"/>
|
||||||
|
<AppenderRef ref="RollingFileInfo"/>
|
||||||
|
<AppenderRef ref="RollingFileWarn"/>
|
||||||
|
<AppenderRef ref="RollingFileError"/>
|
||||||
|
</Logger>
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="Console"/>
|
||||||
|
<appender-ref ref="Filelog"/>
|
||||||
|
<appender-ref ref="RollingFileInfo"/>
|
||||||
|
<appender-ref ref="RollingFileWarn"/>
|
||||||
|
<appender-ref ref="RollingFileError"/>
|
||||||
|
</root>
|
||||||
|
</loggers>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
Reference in New Issue
Block a user