refactor(security): 重构安全配置并优化测试环境
- 移除旧的测试套件和UAT测试文件 - 更新密码编码器配置使用BCrypt strength=12 - 添加用户角色关联表和相关服务 - 优化前端日期显示格式 - 清理无用资源和配置文件 - 增强测试数据管理和清理功能
This commit is contained in:
+35
@@ -0,0 +1,35 @@
|
||||
package cn.novalon.manage.sys.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* 密码编码器配置
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@Configuration
|
||||
public class PasswordEncoderConfig {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PasswordEncoderConfig.class);
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
|
||||
logger.info("创建主密码编码器: BCryptPasswordEncoder(strength=12), 类型: {}", encoder.getClass().getName());
|
||||
return encoder;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
logger.info("PasswordEncoderConfig 已加载");
|
||||
}
|
||||
}
|
||||
+11
@@ -28,6 +28,9 @@ public class SysUser extends BaseDomain {
|
||||
@Schema(description = "手机号", example = "13800138000")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "头像", example = "https://example.com/avatar.jpg")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "角色ID", example = "1")
|
||||
private Long roleId;
|
||||
|
||||
@@ -74,6 +77,14 @@ public class SysUser extends BaseDomain {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package cn.novalon.manage.sys.core.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "用户角色关联实体")
|
||||
public class UserRole {
|
||||
|
||||
@Schema(description = "主键ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "创建人")
|
||||
private String createdBy;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public String getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(String createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package cn.novalon.manage.sys.core.repository;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.UserRole;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface IUserRoleRepository {
|
||||
|
||||
Mono<UserRole> save(UserRole userRole);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
|
||||
Mono<Void> deleteByUserId(Long userId);
|
||||
|
||||
Mono<Void> deleteByRoleId(Long roleId);
|
||||
|
||||
Flux<UserRole> findByUserId(Long userId);
|
||||
|
||||
Flux<UserRole> findByRoleId(Long roleId);
|
||||
|
||||
Mono<Long> countByUserId(Long userId);
|
||||
|
||||
Mono<Long> countByRoleId(Long roleId);
|
||||
|
||||
Flux<UserRole> findAll();
|
||||
|
||||
Mono<UserRole> findById(Long id);
|
||||
}
|
||||
+6
@@ -54,4 +54,10 @@ public interface ISysUserService {
|
||||
Mono<SysUser> changePassword(Long userId, String oldPassword, String newPassword);
|
||||
|
||||
Mono<Void> updateRoleIdToNullByRoleId(Long roleId);
|
||||
|
||||
Mono<Void> assignRolesToUser(Long userId, java.util.List<Long> roleIds);
|
||||
|
||||
Flux<cn.novalon.manage.sys.core.domain.SysRole> getUserRoles(Long userId);
|
||||
|
||||
Flux<Long> getUserRoleIds(Long userId);
|
||||
}
|
||||
|
||||
+63
-3
@@ -2,12 +2,20 @@ package cn.novalon.manage.sys.core.service.impl;
|
||||
|
||||
import cn.novalon.manage.common.util.StatusConstants;
|
||||
import cn.novalon.manage.sys.core.domain.SysUser;
|
||||
import cn.novalon.manage.sys.core.domain.SysRole;
|
||||
import cn.novalon.manage.sys.core.domain.UserRole;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import cn.novalon.manage.sys.core.repository.ISysUserRepository;
|
||||
import cn.novalon.manage.sys.core.repository.ISysRoleRepository;
|
||||
import cn.novalon.manage.sys.core.repository.IUserRoleRepository;
|
||||
import cn.novalon.manage.sys.core.service.ISysUserService;
|
||||
import cn.novalon.manage.sys.core.command.CreateUserCommand;
|
||||
import cn.novalon.manage.sys.core.command.UpdateUserCommand;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -29,14 +37,26 @@ import java.util.List;
|
||||
@Service
|
||||
public class SysUserService implements ISysUserService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysUserService.class);
|
||||
private final ISysUserRepository userRepository;
|
||||
private final ISysRoleRepository roleRepository;
|
||||
private final IUserRoleRepository userRoleRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public SysUserService(ISysUserRepository userRepository, PasswordEncoder passwordEncoder) {
|
||||
public SysUserService(ISysUserRepository userRepository,
|
||||
ISysRoleRepository roleRepository,
|
||||
IUserRoleRepository userRoleRepository,
|
||||
@Qualifier("passwordEncoder") PasswordEncoder passwordEncoder) {
|
||||
this.userRepository = userRepository;
|
||||
this.roleRepository = roleRepository;
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
|
||||
logger.info("使用的密码编码器类型: {}", passwordEncoder.getClass().getName());
|
||||
}
|
||||
|
||||
private static final BCryptPasswordEncoder directEncoder = new BCryptPasswordEncoder(12);
|
||||
|
||||
@Override
|
||||
public Mono<SysUser> findById(Long id) {
|
||||
return userRepository.findById(id);
|
||||
@@ -73,7 +93,17 @@ public class SysUserService implements ISysUserService {
|
||||
|
||||
@Override
|
||||
public Mono<SysUser> createUser(SysUser user) {
|
||||
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||
logger.info("SysUserService.createUser - 用户名: {}, 密码前缀: {}",
|
||||
user.getUsername(),
|
||||
user.getPassword() != null ? user.getPassword().substring(0, 7) : "null");
|
||||
if (user.getPassword() != null && !user.getPassword().startsWith("$2a$")
|
||||
&& !user.getPassword().startsWith("$2b$")) {
|
||||
logger.info("密码不以$2a$或$2b$开头,重新编码");
|
||||
user.setPassword(directEncoder.encode(user.getPassword()));
|
||||
logger.info("重新编码后的密码前缀: {}", user.getPassword().substring(0, 7));
|
||||
} else {
|
||||
logger.info("密码已编码,跳过重新编码");
|
||||
}
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
if (user.getStatus() == null) {
|
||||
user.setStatus(StatusConstants.ENABLED);
|
||||
@@ -85,7 +115,7 @@ public class SysUserService implements ISysUserService {
|
||||
public Mono<SysUser> createUser(CreateUserCommand command) {
|
||||
SysUser user = new SysUser();
|
||||
user.setUsername(command.username().getValue());
|
||||
user.setPassword(passwordEncoder.encode(command.password().getValue()));
|
||||
user.setPassword(directEncoder.encode(command.password().getValue()));
|
||||
user.setEmail(command.email().getValue());
|
||||
user.setNickname(command.nickname());
|
||||
user.setPhone(command.phone());
|
||||
@@ -196,4 +226,34 @@ public class SysUserService implements ISysUserService {
|
||||
public Mono<Void> restoreUsers(List<Long> ids) {
|
||||
return userRepository.restoreByIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> assignRolesToUser(Long userId, List<Long> roleIds) {
|
||||
if (roleIds == null || roleIds.isEmpty()) {
|
||||
return userRoleRepository.deleteByUserId(userId);
|
||||
}
|
||||
|
||||
return userRoleRepository.deleteByUserId(userId)
|
||||
.thenMany(Flux.fromIterable(roleIds))
|
||||
.flatMap(roleId -> {
|
||||
UserRole userRole = new UserRole();
|
||||
userRole.setUserId(userId);
|
||||
userRole.setRoleId(roleId);
|
||||
userRole.setCreatedAt(LocalDateTime.now());
|
||||
return userRoleRepository.save(userRole);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysRole> getUserRoles(Long userId) {
|
||||
return userRoleRepository.findByUserId(userId)
|
||||
.flatMap(userRole -> roleRepository.findById(userRole.getRoleId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Long> getUserRoleIds(Long userId) {
|
||||
return userRoleRepository.findByUserId(userId)
|
||||
.map(UserRole::getRoleId);
|
||||
}
|
||||
}
|
||||
|
||||
+89
-21
@@ -6,15 +6,20 @@ import cn.novalon.manage.sys.dto.response.AuthResponse;
|
||||
import cn.novalon.manage.sys.security.JwtTokenProvider;
|
||||
import cn.novalon.manage.sys.core.domain.SysUser;
|
||||
import cn.novalon.manage.sys.core.domain.SysLoginLog;
|
||||
import cn.novalon.manage.sys.core.repository.ISysUserRepository;
|
||||
import cn.novalon.manage.sys.core.service.ISysUserService;
|
||||
import cn.novalon.manage.sys.core.service.ISysLoginLogService;
|
||||
import cn.novalon.manage.sys.util.UserAgentParser;
|
||||
import cn.novalon.manage.sys.util.IpLocationParser;
|
||||
import cn.novalon.manage.common.util.StatusConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.FieldError;
|
||||
@@ -43,19 +48,40 @@ public class SysAuthHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SysAuthHandler.class);
|
||||
private final ISysUserService userService;
|
||||
private final ISysUserRepository userRepository;
|
||||
@SuppressWarnings("unused")
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
private final ISysLoginLogService loginLogService;
|
||||
private final UserAgentParser userAgentParser;
|
||||
private final IpLocationParser ipLocationParser;
|
||||
|
||||
public SysAuthHandler(ISysUserService userService, PasswordEncoder passwordEncoder,
|
||||
// 使用多个编码器来支持不同的 BCrypt 版本和 strength
|
||||
private static final BCryptPasswordEncoder directEncoder10 = new BCryptPasswordEncoder(10);
|
||||
private static final BCryptPasswordEncoder directEncoder12 = new BCryptPasswordEncoder(12);
|
||||
|
||||
public SysAuthHandler(ISysUserService userService, ISysUserRepository userRepository,
|
||||
@Qualifier("passwordEncoder") PasswordEncoder passwordEncoder,
|
||||
JwtTokenProvider jwtTokenProvider, ISysLoginLogService loginLogService,
|
||||
UserAgentParser userAgentParser) {
|
||||
UserAgentParser userAgentParser, IpLocationParser ipLocationParser) {
|
||||
this.userService = userService;
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.jwtTokenProvider = jwtTokenProvider;
|
||||
this.loginLogService = loginLogService;
|
||||
this.userAgentParser = userAgentParser;
|
||||
this.ipLocationParser = ipLocationParser;
|
||||
|
||||
logger.info("SysAuthHandler使用的密码编码器类型: {}", passwordEncoder.getClass().getName());
|
||||
|
||||
// 测试编码器
|
||||
String testPassword = "test123";
|
||||
String testHash10 = directEncoder10.encode(testPassword);
|
||||
String testHash12 = directEncoder12.encode(testPassword);
|
||||
logger.info("DirectEncoder10测试: 密码={}, 哈希={}, 前缀={}",
|
||||
testPassword, testHash10.substring(0, 10), testHash10.substring(0, 7));
|
||||
logger.info("DirectEncoder12测试: 密码={}, 哈希={}, 前缀={}",
|
||||
testPassword, testHash12.substring(0, 10), testHash12.substring(0, 7));
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登录", description = "使用用户名和密码登录系统")
|
||||
@@ -73,34 +99,64 @@ public class SysAuthHandler {
|
||||
String userAgent = request.headers().firstHeader("User-Agent");
|
||||
return userService.findByUsername(loginRequest.getUsername())
|
||||
.flatMap(user -> {
|
||||
if (!passwordEncoder.matches(loginRequest.getPassword(),
|
||||
// 尝试使用不同的编码器验证密码
|
||||
boolean passwordMatches = false;
|
||||
|
||||
// 首先尝试使用 strength=12 的编码器
|
||||
if (directEncoder12.matches(loginRequest.getPassword(),
|
||||
user.getPassword())) {
|
||||
passwordMatches = true;
|
||||
logger.info("密码验证成功: 使用strength=12编码器");
|
||||
}
|
||||
|
||||
// 如果失败,尝试使用 strength=10 的编码器
|
||||
if (!passwordMatches && directEncoder10.matches(
|
||||
loginRequest.getPassword(),
|
||||
user.getPassword())) {
|
||||
passwordMatches = true;
|
||||
logger.info("密码验证成功: 使用strength=10编码器");
|
||||
}
|
||||
|
||||
if (!passwordMatches) {
|
||||
logger.warn("用户登录失败: username={}, reason=密码错误",
|
||||
loginRequest.getUsername());
|
||||
recordLoginLog(loginRequest.getUsername(), clientIp, "1", "密码错误", userAgent);
|
||||
recordLoginLog(loginRequest.getUsername(),
|
||||
clientIp, "1", "密码错误",
|
||||
userAgent);
|
||||
return Mono.error(new RuntimeException(
|
||||
"用户名或密码错误"));
|
||||
}
|
||||
|
||||
if (user.getStatus() != 1) {
|
||||
logger.warn("用户登录失败: username={}, reason=用户已禁用",
|
||||
loginRequest.getUsername());
|
||||
recordLoginLog(loginRequest.getUsername(), clientIp, "1", "用户已禁用", userAgent);
|
||||
recordLoginLog(loginRequest.getUsername(),
|
||||
clientIp, "1", "用户已禁用",
|
||||
userAgent);
|
||||
return Mono.error(new RuntimeException(
|
||||
"用户名或密码错误"));
|
||||
}
|
||||
String token = jwtTokenProvider.generateToken(
|
||||
user.getUsername(), user.getId());
|
||||
logger.info("用户登录成功: username={}, userId={}",
|
||||
user.getUsername(), user.getId());
|
||||
recordLoginLog(loginRequest.getUsername(), clientIp, "0", "登录成功", userAgent);
|
||||
AuthResponse response = new AuthResponse(token,
|
||||
user.getId(), user.getUsername());
|
||||
return ServerResponse.ok().bodyValue(response);
|
||||
|
||||
return userService.getUserRoles(user.getId())
|
||||
.map(role -> role.getRoleKey())
|
||||
.collectList()
|
||||
.flatMap(roleKeys -> {
|
||||
String token = jwtTokenProvider.generateToken(
|
||||
user.getUsername(), user.getId(), roleKeys);
|
||||
logger.info("用户登录成功: username={}, userId={}, roles={}",
|
||||
user.getUsername(), user.getId(), roleKeys);
|
||||
recordLoginLog(loginRequest.getUsername(), clientIp,
|
||||
"0", "登录成功", userAgent);
|
||||
AuthResponse response = new AuthResponse(token,
|
||||
user.getId(), user.getUsername());
|
||||
return ServerResponse.ok().bodyValue(response);
|
||||
});
|
||||
})
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
logger.warn("用户登录失败: username={}, reason=用户不存在",
|
||||
loginRequest.getUsername());
|
||||
recordLoginLog(loginRequest.getUsername(), clientIp, "1", "用户不存在", userAgent);
|
||||
recordLoginLog(loginRequest.getUsername(), clientIp,
|
||||
"1", "用户不存在", userAgent);
|
||||
return Mono.error(new RuntimeException("用户名或密码错误"));
|
||||
}));
|
||||
})
|
||||
@@ -140,17 +196,19 @@ public class SysAuthHandler {
|
||||
SysLoginLog loginLog = new SysLoginLog();
|
||||
loginLog.setUsername(username);
|
||||
loginLog.setIp(ip);
|
||||
loginLog.setLocation(ipLocationParser.parseLocation(ip));
|
||||
loginLog.setStatus(status);
|
||||
loginLog.setMessage(message);
|
||||
loginLog.setLoginTime(LocalDateTime.now());
|
||||
|
||||
|
||||
if (userAgent != null && !userAgent.isEmpty()) {
|
||||
loginLog.setBrowser(userAgentParser.parseBrowser(userAgent));
|
||||
loginLog.setOs(userAgentParser.parseOS(userAgent));
|
||||
}
|
||||
|
||||
|
||||
loginLogService.save(loginLog)
|
||||
.doOnSuccess(saved -> logger.debug("登录日志记录成功: username={}, status={}", username, status))
|
||||
.doOnSuccess(saved -> logger.debug("登录日志记录成功: username={}, status={}", username,
|
||||
status))
|
||||
.doOnError(error -> logger.error("登录日志记录失败: {}", error.getMessage()))
|
||||
.subscribe();
|
||||
} catch (Exception e) {
|
||||
@@ -186,8 +244,16 @@ public class SysAuthHandler {
|
||||
registerRequest.getUsername(), registerRequest.getEmail());
|
||||
SysUser user = new SysUser();
|
||||
user.setUsername(registerRequest.getUsername());
|
||||
user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
|
||||
String encodedPassword = directEncoder12.encode(registerRequest.getPassword());
|
||||
logger.info("密码编码结果: {} (前缀: {})",
|
||||
encodedPassword.substring(0, 10),
|
||||
encodedPassword.substring(0, 7));
|
||||
user.setPassword(encodedPassword);
|
||||
user.setEmail(registerRequest.getEmail());
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
if (user.getStatus() == null) {
|
||||
user.setStatus(StatusConstants.ENABLED);
|
||||
}
|
||||
return userService.findByUsername(registerRequest.getUsername())
|
||||
.flatMap(existing -> {
|
||||
logger.warn("用户注册失败: username={}, reason=用户名已存在",
|
||||
@@ -195,11 +261,13 @@ public class SysAuthHandler {
|
||||
return Mono.<ServerResponse>error(
|
||||
new RuntimeException("用户名已存在"));
|
||||
})
|
||||
.switchIfEmpty(userService.createUser(user)
|
||||
.switchIfEmpty(userRepository.save(user)
|
||||
.flatMap(u -> {
|
||||
logger.info("用户注册成功: username={}, userId={}",
|
||||
logger.info("用户注册成功: username={}, userId={}, password={}",
|
||||
u.getUsername(),
|
||||
u.getId());
|
||||
u.getId(),
|
||||
u.getPassword().substring(
|
||||
0, 10));
|
||||
return ServerResponse
|
||||
.status(HttpStatus.CREATED)
|
||||
.bodyValue(u);
|
||||
|
||||
+65
-6
@@ -16,7 +16,10 @@ import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用户处理器
|
||||
@@ -74,7 +77,25 @@ public class SysUserHandler {
|
||||
public Mono<ServerResponse> getUserById(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return userService.findById(id)
|
||||
.flatMap(user -> ServerResponse.ok().bodyValue(user))
|
||||
.flatMap(user -> {
|
||||
return userService.getUserRoleIds(id)
|
||||
.collectList()
|
||||
.map(roleIds -> {
|
||||
Map<String, Object> userWithRoles = new HashMap<>();
|
||||
userWithRoles.put("id", user.getId());
|
||||
userWithRoles.put("username", user.getUsername());
|
||||
userWithRoles.put("nickname", user.getNickname());
|
||||
userWithRoles.put("email", user.getEmail());
|
||||
userWithRoles.put("phone", user.getPhone());
|
||||
userWithRoles.put("avatar", user.getAvatar());
|
||||
userWithRoles.put("status", user.getStatus());
|
||||
userWithRoles.put("roles", roleIds);
|
||||
userWithRoles.put("createdAt", user.getCreatedAt());
|
||||
userWithRoles.put("updatedAt", user.getUpdatedAt());
|
||||
return userWithRoles;
|
||||
});
|
||||
})
|
||||
.flatMap(userWithRoles -> ServerResponse.ok().bodyValue(userWithRoles))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@@ -127,8 +148,16 @@ public class SysUserHandler {
|
||||
@Operation(summary = "删除用户", description = "物理删除用户")
|
||||
public Mono<ServerResponse> deleteUser(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return userService.deleteUser(id)
|
||||
.then(ServerResponse.noContent().build());
|
||||
return userService.findById(id)
|
||||
.flatMap(user -> userService.deleteUser(id)
|
||||
.then(ServerResponse.noContent().build()))
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("User not found")))
|
||||
.onErrorResume(RuntimeException.class, ex -> {
|
||||
if (ex.getMessage().contains("not found")) {
|
||||
return ServerResponse.notFound().build();
|
||||
}
|
||||
return Mono.error(ex);
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "修改密码", description = "修改用户密码")
|
||||
@@ -142,8 +171,16 @@ public class SysUserHandler {
|
||||
@Operation(summary = "逻辑删除用户", description = "逻辑删除单个用户")
|
||||
public Mono<ServerResponse> logicalDeleteUser(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return userService.logicalDeleteUser(id)
|
||||
.then(ServerResponse.noContent().build());
|
||||
return userService.findById(id)
|
||||
.flatMap(user -> userService.logicalDeleteUser(id)
|
||||
.then(ServerResponse.noContent().build()))
|
||||
.switchIfEmpty(Mono.error(new RuntimeException("User not found")))
|
||||
.onErrorResume(RuntimeException.class, ex -> {
|
||||
if (ex.getMessage().contains("not found")) {
|
||||
return ServerResponse.notFound().build();
|
||||
}
|
||||
return Mono.error(ex);
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "批量逻辑删除用户", description = "批量逻辑删除多个用户")
|
||||
@@ -158,7 +195,13 @@ public class SysUserHandler {
|
||||
public Mono<ServerResponse> restoreUser(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return userService.restoreUser(id)
|
||||
.then(ServerResponse.noContent().build());
|
||||
.then(ServerResponse.noContent().build())
|
||||
.onErrorResume(RuntimeException.class, ex -> {
|
||||
if (ex.getMessage().contains("not found")) {
|
||||
return ServerResponse.notFound().build();
|
||||
}
|
||||
return Mono.error(ex);
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "批量恢复用户", description = "批量恢复被逻辑删除的用户")
|
||||
@@ -182,4 +225,20 @@ public class SysUserHandler {
|
||||
return userService.existsByEmail(email)
|
||||
.flatMap(exists -> ServerResponse.ok().bodyValue(exists));
|
||||
}
|
||||
|
||||
@Operation(summary = "为用户分配角色", description = "为指定用户分配角色列表")
|
||||
public Mono<ServerResponse> assignRoles(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return request.bodyToMono(new org.springframework.core.ParameterizedTypeReference<List<Long>>() {
|
||||
})
|
||||
.flatMap(roleIds -> userService.assignRolesToUser(id, roleIds))
|
||||
.then(ServerResponse.ok().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户的角色", description = "根据用户ID获取该用户拥有的所有角色")
|
||||
public Mono<ServerResponse> getUserRoles(ServerRequest request) {
|
||||
Long id = Long.valueOf(request.pathVariable("id"));
|
||||
return ServerResponse.ok()
|
||||
.body(userService.getUserRoles(id), cn.novalon.manage.sys.core.domain.SysRole.class);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -13,6 +13,9 @@ import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志过滤器
|
||||
*
|
||||
@@ -29,8 +32,11 @@ public class OperationLogFilter implements WebFilter {
|
||||
private static final Logger logger = LoggerFactory.getLogger(OperationLogFilter.class);
|
||||
|
||||
private final IOperationLogService logService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public OperationLogFilter(IOperationLogService logService, ObjectMapper objectMapper) {
|
||||
this.logService = logService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,8 +97,14 @@ public class OperationLogFilter implements WebFilter {
|
||||
log.setResult("Success");
|
||||
}
|
||||
|
||||
String queryParams = exchange.getRequest().getQueryParams().toSingleValueMap().toString();
|
||||
log.setParams(queryParams);
|
||||
Map<String, String> queryParams = new LinkedHashMap<>(exchange.getRequest().getQueryParams().toSingleValueMap());
|
||||
String formattedParams;
|
||||
try {
|
||||
formattedParams = objectMapper.writeValueAsString(queryParams);
|
||||
} catch (Exception e) {
|
||||
formattedParams = queryParams.toString();
|
||||
}
|
||||
log.setParams(formattedParams);
|
||||
|
||||
logService.save(log)
|
||||
.doOnSuccess(saved -> logger.debug("操作日志记录成功: {}", log.getOperation()))
|
||||
|
||||
+13
-3
@@ -11,7 +11,8 @@ import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* JWT认证过滤器
|
||||
@@ -33,14 +34,23 @@ public class JwtAuthenticationFilter implements WebFilter {
|
||||
String token = extractToken(exchange.getRequest());
|
||||
|
||||
if (token != null && jwtTokenProvider.validateToken(token)) {
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
||||
String username = jwtTokenProvider.getUsernameFromToken(token);
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
||||
List<String> roles = jwtTokenProvider.getRolesFromToken(token);
|
||||
|
||||
List<SimpleGrantedAuthority> authorities = roles.stream()
|
||||
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (authorities.isEmpty()) {
|
||||
authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(
|
||||
username,
|
||||
null,
|
||||
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
|
||||
authorities
|
||||
);
|
||||
|
||||
return chain.filter(exchange)
|
||||
|
||||
+24
@@ -45,6 +45,21 @@ public class JwtTokenProvider {
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String generateToken(String username, Long userId, java.util.List<String> roles) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("userId", userId);
|
||||
claims.put("username", username);
|
||||
claims.put("roles", roles);
|
||||
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(username)
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getExpiration()))
|
||||
.signWith(getSigningKey())
|
||||
.compact();
|
||||
}
|
||||
|
||||
public Claims getClaimsFromToken(String token) {
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
@@ -61,6 +76,15 @@ public class JwtTokenProvider {
|
||||
return getClaimsFromToken(token).get("userId", Long.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public java.util.List<String> getRolesFromToken(String token) {
|
||||
Object roles = getClaimsFromToken(token).get("roles");
|
||||
if (roles instanceof java.util.List) {
|
||||
return (java.util.List<String>) roles;
|
||||
}
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
|
||||
public boolean validateToken(String token) {
|
||||
try {
|
||||
getClaimsFromToken(token);
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.novalon.manage.sys.util;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* IP地址解析工具类
|
||||
*
|
||||
* 用于解析IP地址,获取地理位置信息
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@Component
|
||||
public class IpLocationParser {
|
||||
|
||||
private static final Map<String, String> IP_LOCATION_CACHE = new HashMap<>();
|
||||
|
||||
static {
|
||||
IP_LOCATION_CACHE.put("127.0.0.1", "本地");
|
||||
IP_LOCATION_CACHE.put("0:0:0:0:0:0:0:1", "本地");
|
||||
IP_LOCATION_CACHE.put("localhost", "本地");
|
||||
}
|
||||
|
||||
public String parseLocation(String ip) {
|
||||
if (ip == null || ip.isEmpty()) {
|
||||
return "未知位置";
|
||||
}
|
||||
|
||||
if (IP_LOCATION_CACHE.containsKey(ip)) {
|
||||
return IP_LOCATION_CACHE.get(ip);
|
||||
}
|
||||
|
||||
if (isInternalIp(ip)) {
|
||||
return "内网";
|
||||
}
|
||||
|
||||
return "未知位置";
|
||||
}
|
||||
|
||||
private boolean isInternalIp(String ip) {
|
||||
if (ip == null || ip.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] parts = ip.split("\\.");
|
||||
if (parts.length != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
int first = Integer.parseInt(parts[0]);
|
||||
int second = Integer.parseInt(parts[1]);
|
||||
|
||||
if (first == 10) {
|
||||
return true;
|
||||
}
|
||||
if (first == 172 && second >= 16 && second <= 31) {
|
||||
return true;
|
||||
}
|
||||
if (first == 192 && second == 168) {
|
||||
return true;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+2
-7
@@ -17,12 +17,7 @@ import java.util.regex.Pattern;
|
||||
public class UserAgentParser {
|
||||
|
||||
private static final Pattern BROWSER_PATTERN = Pattern.compile(
|
||||
"(Chrome|Firefox|Safari|Edge|MSIE|Trident|Opera)[/\\s]([\\d.]+)"
|
||||
);
|
||||
|
||||
private static final Pattern OS_PATTERN = Pattern.compile(
|
||||
"(Windows NT|Mac OS X|Linux|Android|iPhone|iPad|iPod)[\\s/_-]?([\\d._]+)?"
|
||||
);
|
||||
"(Chrome|Firefox|Safari|Edge|MSIE|Trident|Opera)[/\\s]([\\d.]+)");
|
||||
|
||||
/**
|
||||
* 解析User-Agent字符串,返回浏览器信息
|
||||
@@ -55,7 +50,7 @@ public class UserAgentParser {
|
||||
}
|
||||
|
||||
String ua = userAgent;
|
||||
|
||||
|
||||
if (ua.contains("Windows NT 10.0")) {
|
||||
return "Windows 10";
|
||||
} else if (ua.contains("Windows NT 6.3")) {
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
-- 系统菜单初始化数据
|
||||
-- @author 张翔
|
||||
-- @date 2026-03-24
|
||||
|
||||
-- 清空现有菜单数据
|
||||
DELETE FROM sys_menu WHERE id > 0;
|
||||
|
||||
-- 一级菜单
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(1, 0, '系统管理', 1, 'M', NULL, NULL, 1, NOW(), NOW()),
|
||||
(2, 0, '审计日志', 2, 'M', NULL, NULL, 1, NOW(), NOW()),
|
||||
(3, 0, '系统监控', 3, 'M', NULL, NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 系统管理子菜单
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(11, 1, '用户管理', 1, 'C', 'system:user:list', 'system/user/index', 1, NOW(), NOW()),
|
||||
(12, 1, '角色管理', 2, 'C', 'system:role:list', 'system/role/index', 1, NOW(), NOW()),
|
||||
(13, 1, '菜单管理', 3, 'C', 'system:menu:list', 'system/menu/index', 1, NOW(), NOW()),
|
||||
(14, 1, '部门管理', 4, 'C', 'system:dept:list', 'system/dept/index', 1, NOW(), NOW()),
|
||||
(15, 1, '字典管理', 5, 'C', 'system:dict:list', 'system/dict/index', 1, NOW(), NOW()),
|
||||
(16, 1, '参数管理', 6, 'C', 'system:config:list', 'system/config/index', 1, NOW(), NOW()),
|
||||
(17, 1, '通知公告', 7, 'C', 'system:notice:list', 'system/notice/index', 1, NOW(), NOW()),
|
||||
(18, 1, '文件管理', 8, 'C', 'system:file:list', 'system/file/index', 1, NOW(), NOW());
|
||||
|
||||
-- 用户管理按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(111, 11, '用户查询', 1, 'F', 'system:user:query', NULL, 1, NOW(), NOW()),
|
||||
(112, 11, '用户新增', 2, 'F', 'system:user:add', NULL, 1, NOW(), NOW()),
|
||||
(113, 11, '用户修改', 3, 'F', 'system:user:edit', NULL, 1, NOW(), NOW()),
|
||||
(114, 11, '用户删除', 4, 'F', 'system:user:remove', NULL, 1, NOW(), NOW()),
|
||||
(115, 11, '用户导出', 5, 'F', 'system:user:export', NULL, 1, NOW(), NOW()),
|
||||
(116, 11, '用户导入', 6, 'F', 'system:user:import', NULL, 1, NOW(), NOW()),
|
||||
(117, 11, '重置密码', 7, 'F', 'system:user:resetPwd', NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 角色管理按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(121, 12, '角色查询', 1, 'F', 'system:role:query', NULL, 1, NOW(), NOW()),
|
||||
(122, 12, '角色新增', 2, 'F', 'system:role:add', NULL, 1, NOW(), NOW()),
|
||||
(123, 12, '角色修改', 3, 'F', 'system:role:edit', NULL, 1, NOW(), NOW()),
|
||||
(124, 12, '角色删除', 4, 'F', 'system:role:remove', NULL, 1, NOW(), NOW()),
|
||||
(125, 12, '角色导出', 5, 'F', 'system:role:export', NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 菜单管理按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(131, 13, '菜单查询', 1, 'F', 'system:menu:query', NULL, 1, NOW(), NOW()),
|
||||
(132, 13, '菜单新增', 2, 'F', 'system:menu:add', NULL, 1, NOW(), NOW()),
|
||||
(133, 13, '菜单修改', 3, 'F', 'system:menu:edit', NULL, 1, NOW(), NOW()),
|
||||
(134, 13, '菜单删除', 4, 'F', 'system:menu:remove', NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 审计日志子菜单
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(21, 2, '操作日志', 1, 'C', 'audit:operation:list', 'audit/operation/index', 1, NOW(), NOW()),
|
||||
(22, 2, '登录日志', 2, 'C', 'audit:login:list', 'audit/login/index', 1, NOW(), NOW()),
|
||||
(23, 2, '异常日志', 3, 'C', 'audit:exception:list', 'audit/exception/index', 1, NOW(), NOW());
|
||||
|
||||
-- 操作日志按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(211, 21, '操作查询', 1, 'F', 'audit:operation:query', NULL, 1, NOW(), NOW()),
|
||||
(212, 21, '操作删除', 2, 'F', 'audit:operation:remove', NULL, 1, NOW(), NOW()),
|
||||
(213, 21, '操作导出', 3, 'F', 'audit:operation:export', NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 登录日志按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(221, 22, '登录查询', 1, 'F', 'audit:login:query', NULL, 1, NOW(), NOW()),
|
||||
(222, 22, '登录删除', 2, 'F', 'audit:login:remove', NULL, 1, NOW(), NOW()),
|
||||
(223, 22, '登录导出', 3, 'F', 'audit:login:export', NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 异常日志按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(231, 23, '异常查询', 1, 'F', 'audit:exception:query', NULL, 1, NOW(), NOW()),
|
||||
(232, 23, '异常删除', 2, 'F', 'audit:exception:remove', NULL, 1, NOW(), NOW()),
|
||||
(233, 23, '异常导出', 3, 'F', 'audit:exception:export', NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 系统监控子菜单
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(31, 3, '在线用户', 1, 'C', 'monitor:online:list', 'monitor/online/index', 1, NOW(), NOW()),
|
||||
(32, 3, '定时任务', 2, 'C', 'monitor:job:list', 'monitor/job/index', 1, NOW(), NOW()),
|
||||
(33, 3, '数据监控', 3, 'C', 'monitor:data:list', 'monitor/data/index', 1, NOW(), NOW()),
|
||||
(34, 3, '服务监控', 4, 'C', 'monitor:server:list', 'monitor/server/index', 1, NOW(), NOW()),
|
||||
(35, 3, '缓存监控', 5, 'C', 'monitor:cache:list', 'monitor/cache/index', 1, NOW(), NOW());
|
||||
|
||||
-- 在线用户按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(311, 31, '在线查询', 1, 'F', 'monitor:online:query', NULL, 1, NOW(), NOW()),
|
||||
(312, 31, '在线强退', 2, 'F', 'monitor:online:forceLogout', NULL, 1, NOW(), NOW());
|
||||
|
||||
-- 定时任务按钮权限
|
||||
INSERT INTO sys_menu (id, parent_id, menu_name, order_num, menu_type, perms, component, status, created_at, updated_at) VALUES
|
||||
(321, 32, '任务查询', 1, 'F', 'monitor:job:query', NULL, 1, NOW(), NOW()),
|
||||
(322, 32, '任务新增', 2, 'F', 'monitor:job:add', NULL, 1, NOW(), NOW()),
|
||||
(323, 32, '任务修改', 3, 'F', 'monitor:job:edit', NULL, 1, NOW(), NOW()),
|
||||
(324, 32, '任务删除', 4, 'F', 'monitor:job:remove', NULL, 1, NOW(), NOW()),
|
||||
(325, 32, '任务执行', 5, 'F', 'monitor:job:execute', NULL, 1, NOW(), NOW());
|
||||
|
||||
COMMIT;
|
||||
+7
-1
@@ -36,6 +36,12 @@ class SysUserServiceTest {
|
||||
@Mock
|
||||
private ISysUserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private cn.novalon.manage.sys.core.repository.ISysRoleRepository roleRepository;
|
||||
|
||||
@Mock
|
||||
private cn.novalon.manage.sys.core.repository.IUserRoleRepository userRoleRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@@ -45,7 +51,7 @@ class SysUserServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
userService = new SysUserService(userRepository, passwordEncoder);
|
||||
userService = new SysUserService(userRepository, roleRepository, userRoleRepository, passwordEncoder);
|
||||
|
||||
testUser = new SysUser();
|
||||
testUser.setId(1L);
|
||||
|
||||
+9
-2
@@ -7,6 +7,7 @@ import cn.novalon.manage.sys.core.domain.SysUser;
|
||||
import cn.novalon.manage.sys.core.service.ISysUserService;
|
||||
import cn.novalon.manage.sys.core.service.ISysLoginLogService;
|
||||
import cn.novalon.manage.sys.util.UserAgentParser;
|
||||
import cn.novalon.manage.sys.util.IpLocationParser;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -30,6 +31,9 @@ class SysAuthHandlerTest {
|
||||
@Mock
|
||||
private ISysUserService userService;
|
||||
|
||||
@Mock
|
||||
private cn.novalon.manage.sys.core.repository.ISysUserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@@ -42,13 +46,16 @@ class SysAuthHandlerTest {
|
||||
@Mock
|
||||
private UserAgentParser userAgentParser;
|
||||
|
||||
@Mock
|
||||
private IpLocationParser ipLocationParser;
|
||||
|
||||
private SysAuthHandler authHandler;
|
||||
private SysUser testUser;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
authHandler = new SysAuthHandler(userService, passwordEncoder, jwtTokenProvider, loginLogService,
|
||||
userAgentParser);
|
||||
authHandler = new SysAuthHandler(userService, userRepository, passwordEncoder, jwtTokenProvider, loginLogService,
|
||||
userAgentParser, ipLocationParser);
|
||||
|
||||
testUser = new SysUser();
|
||||
testUser.setId(1L);
|
||||
|
||||
-2
@@ -16,8 +16,6 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
package cn.novalon.manage.sys.util;
|
||||
|
||||
import cn.novalon.manage.sys.util.UserAgentParser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user