- 完善Spring Security
This commit is contained in:
yulinling 2025-06-15 00:41:20 +08:00
parent 717395f8b3
commit 23f65a4e5d
16 changed files with 256 additions and 309 deletions

View File

@ -1,31 +0,0 @@
package asia.yulinling.workflow.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* <p>
* JWT配置类
* </p>
*
* @author YLL
* @since 2025/6/11
*/
@ConfigurationProperties(prefix = "jwt.config")
@Data
public class JwtConfig {
/**
* jwt 加密 key默认值kw.
*/
private String key = "kw";
/**
* jwt 过期时间默认值600000 {@code 10 分钟}.
*/
private Long ttl = 600000L;
/**
* 开启 记住我 之后 jwt 过期时间默认值 604800000 {@code 7 }
*/
private Long remember = 604800000L;
}

View File

@ -1,16 +1,12 @@
package asia.yulinling.workflow.config;
import asia.yulinling.workflow.model.vo.user.UserPrincipal;
import asia.yulinling.workflow.security.AuthEntryPoint;
import asia.yulinling.workflow.security.JwtAuthenticationEntryPoint;
import asia.yulinling.workflow.security.JwtAuthenticationFilter;
import asia.yulinling.workflow.service.CustomerDetailsService;
import asia.yulinling.workflow.security.JwtUserDetailsService;
import asia.yulinling.workflow.utils.JwtUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
@ -18,16 +14,10 @@ import org.springframework.security.config.annotation.authentication.configurati
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.util.List;
@Configuration
@EnableWebSecurity
@ -35,14 +25,14 @@ import java.util.List;
@Slf4j
public class SecurityConfig {
private final CustomerDetailsService customerDetailService;
private final JwtUserDetailsService jwtUserDetailsService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtUtil jwtUtil, AuthEntryPoint authEntryPoint, RequestMatcher requestMatcher) throws Exception {
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtUtil jwtUtil, JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.exceptionHandling(ex -> ex
.authenticationEntryPoint(authEntryPoint)
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied"))
)
.authorizeHttpRequests(auth -> auth
@ -52,9 +42,10 @@ public class SecurityConfig {
)
.sessionManagement(AbstractHttpConfigurer::disable)
.addFilterBefore(
new JwtAuthenticationFilter(jwtUtil, authEntryPoint, requestMatcher),
new JwtAuthenticationFilter(jwtUtil, jwtUserDetailsService),
UsernamePasswordAuthenticationFilter.class
);
)
.userDetailsService(jwtUserDetailsService);
return http.build();
}
@ -67,29 +58,6 @@ public class SecurityConfig {
return new BCryptPasswordEncoder();
}
@Bean RequestMatcher requestMatcher() {
return new AntPathRequestMatcher("/login", "POST");
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(customerDetailService);
return authProvider;
}
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
UserPrincipal user = new UserPrincipal(
1L,
"admin",
passwordEncoder.encode("123456"),
List.of("ADMIN"),
List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))
);
return new InMemoryUserDetailsManager(user);
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();

View File

@ -0,0 +1,33 @@
package asia.yulinling.workflow.controller;
import asia.yulinling.workflow.dto.request.LoginRequest;
import asia.yulinling.workflow.service.AuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 登录控制层
* </p>
*
* @author YLL
* @since 2025/6/13
*/
@RestController
@RequiredArgsConstructor
@Slf4j
public class AuthController {
private final AuthService authenticate;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
String token = authenticate.login(loginRequest);
return ResponseEntity.ok(token);
}
}

View File

@ -1,51 +0,0 @@
package asia.yulinling.workflow.controller;
import asia.yulinling.workflow.dto.request.LoginRequest;
import asia.yulinling.workflow.utils.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 登录控制层
* </p>
*
* @author YLL
* @since 2025/6/13
*/
@RestController
@RequiredArgsConstructor
@Slf4j
public class LoginController {
private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
try {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
Authentication authentication = authenticationManager.authenticate(authToken);
log.info("{}", authentication.getPrincipal());
String token = jwtUtil.generateToken(authentication, false); // 可根据需要传 true/false
return ResponseEntity.ok(token);
} catch (AuthenticationException ex) {
log.info("{}", ex.getMessage());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
}
}

View File

@ -0,0 +1,21 @@
package asia.yulinling.workflow.dto.response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
* JWT响应体
* </p>
*
* @author YLL
* @since 2025/6/15
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JWTAuthResponse {
private String accessToken;
private String tokenType = "Bearer";
}

View File

@ -17,7 +17,7 @@ import java.io.IOException;
* @since 2025/6/13
*/
@Component
public class AuthEntryPoint implements AuthenticationEntryPoint {
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {

View File

@ -7,11 +7,12 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@ -28,39 +29,40 @@ import java.io.IOException;
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final AuthenticationEntryPoint authenticationEntryPoint;
private final RequestMatcher requestMatcher;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
)
throws ServletException, IOException {
log.info("{}", request.getRequestURI());
if (requestMatcher.matches(request)) {
log.info("{}", request.getRequestURI());
filterChain.doFilter(request, response);
return;
}
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) {
String token = null;
if (header != null) {
token = header.substring(7);
}
if (jwtUtil.validateToken(token)) {
String username = jwtUtil.parseToken(token).getSubject();
String roles = jwtUtil.parseToken(token).get("roles").toString();
Authentication auth = new JwtAuthenticationToken(username, roles);
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
authenticationEntryPoint.commence(request, response, new AuthenticationException("") {
});
return;
}
FilterChain filterChain) throws ServletException, IOException {
String token = getTokenFromRequest(request);
log.info("token: {}", token);
if (StringUtils.hasText(token) && jwtUtil.validateToken(token)) {
// get username from token
String username = jwtUtil.parseToken(token).getSubject();
// load the user associated with token
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
filterChain.doFilter(request, response);
}
private String getTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}

View File

@ -1,37 +0,0 @@
package asia.yulinling.workflow.security;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.Collections;
/**
* <p>
* JWT认证
* </p>
*
* @author YLL
* @since 2025/6/13
*/
public class JwtAuthenticationToken extends AbstractAuthenticationToken {
private final String principal;
private final String role;
public JwtAuthenticationToken(String username, String role) {
super(Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role)));
this.principal = username;
this.role = role;
setAuthenticated(true);
}
@Override
public Object getPrincipal() {
return principal;
}
@Override
public Object getCredentials() {
return "";
}
}

View File

@ -1,16 +1,15 @@
package asia.yulinling.workflow.service;
package asia.yulinling.workflow.security;
import asia.yulinling.workflow.mapper.PermissionMapper;
import asia.yulinling.workflow.mapper.RoleMapper;
import asia.yulinling.workflow.mapper.UserMapper;
import asia.yulinling.workflow.model.entity.Permission;
import asia.yulinling.workflow.model.entity.Role;
import asia.yulinling.workflow.model.entity.User;
import asia.yulinling.workflow.model.vo.user.UserPrincipal;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ -18,6 +17,7 @@ import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -31,10 +31,9 @@ import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Slf4j
public class CustomerDetailsService implements UserDetailsService {
public class JwtUserDetailsService implements UserDetailsService {
private final UserMapper userMapper;
private final RoleMapper roleMapper;
private final PermissionMapper permissionMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
@ -47,9 +46,15 @@ public class CustomerDetailsService implements UserDetailsService {
User user = userMapper.selectOne(queryWrapper);
List<Role> roles = roleMapper.selectByIds(Collections.singleton(user.getId()));
List<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());
List<Permission> permissions = permissionMapper.selectByIds(roleIds);
return UserPrincipal.create(user, roles, permissions);
Set<GrantedAuthority> authorities = roles.stream()
.map((role) -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toSet());
return new org.springframework.security.core.userdetails.User(
username,
user.getPassword(),
authorities
);
}
}

View File

@ -0,0 +1,15 @@
package asia.yulinling.workflow.service;
import asia.yulinling.workflow.dto.request.LoginRequest;
/**
* <p>
* 账号权限类
* </p>
*
* @author YLL
* @since 2025/6/14
*/
public interface AuthService {
String login(LoginRequest loginRequest);
}

View File

@ -0,0 +1,36 @@
package asia.yulinling.workflow.service.impl;
import asia.yulinling.workflow.dto.request.LoginRequest;
import asia.yulinling.workflow.service.AuthService;
import asia.yulinling.workflow.utils.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
/**
* <p>
* 账号权限类实现
* </p>
*
* @author YLL
* @since 2025/6/14
*/
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {
private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil;
@Override
public String login(LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(), loginRequest.getPassword()
));
SecurityContextHolder.getContext().setAuthentication(authentication);
return jwtUtil.generateToken(authentication, false);
}
}

View File

@ -1,21 +1,16 @@
package asia.yulinling.workflow.utils;
import asia.yulinling.workflow.config.JwtConfig;
import asia.yulinling.workflow.model.vo.user.UserPrincipal;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* <p>
@ -25,47 +20,27 @@ import java.util.List;
* @author YLL
* @since 2025/6/11
*/
@EnableConfigurationProperties(JwtConfig.class)
@RequiredArgsConstructor
@Component
@Slf4j
public class JwtUtil {
private final JwtConfig jwtConfig;
/**
* jwt 加密 key默认值kw.
*/
@Value("${jwt.config.key}")
private String key = "daf66e01593f61a15b857cf433aae03a005812b31234e149036bcc8dee755dbb";
/**
* 创建JWT
*
* @param isRememberMe 记住我
* @param userId 用户id
* @param subject 用户名
* @param roles 用户角色
* @param authorities 用户权限
* @return JWT
* jwt 过期时间默认值600000 {@code 10 分钟}.
*/
public String generateToken(boolean isRememberMe, Long userId, String subject, List<String> roles, Collection<? extends GrantedAuthority> authorities) {
// 1. 构建签名密钥
Key key = getSigningKey();
@Value("${jwt.config.ttl}")
private Long ttl = 600000L;
// 2. 当前时间
Date now = new Date();
// 3. 计算过期时间
long ttl = isRememberMe ? jwtConfig.getRemember() : jwtConfig.getTtl();
Date expiration = new Date(now.getTime() + ttl);
// 4. 构建 JWT
JwtBuilder builder = Jwts.builder()
.setId(String.valueOf(userId))
.setSubject(subject)
.setIssuedAt(now)
.setExpiration(expiration)
.claim("roles", roles)
.claim("authorities", authorities)
.signWith(key);
// 5. 返回生成的 token 字符串
return builder.compact();
}
/**
* 开启 记住我 之后 jwt 过期时间默认值 604800000 {@code 7 }
*/
@Value("${jwt.config.remember}")
private Long remember = 604800000L;
/**
* 创建JWT
@ -74,15 +49,27 @@ public class JwtUtil {
* @param isRememberMe 记住我
* @return JWT
*/
public String generateToken(Authentication authentication, Boolean isRememberMe) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
return generateToken(
isRememberMe,
userPrincipal.getId(),
userPrincipal.getUsername(),
userPrincipal.getRoles(),
userPrincipal.getAuthorities()
);
public String generateToken(Authentication authentication, boolean isRememberMe) {
// 1. 构建签名密钥
Key key = key();
// 2. 当前时间
Date now = new Date();
// 3. 计算过期时间
long ttl = isRememberMe ? this.remember : this.ttl;
Date expiration = new Date(now.getTime() + ttl);
// 4. 构建 JWT
String username = authentication.getName();
JwtBuilder builder = Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(key);
// 5. 返回生成的 token 字符串
return builder.compact();
}
/**
@ -93,7 +80,7 @@ public class JwtUtil {
*/
public Claims parseToken(String token) {
try {
Key key = getSigningKey();
Key key = key();
return Jwts.parserBuilder()
.setSigningKey(key)
@ -120,7 +107,7 @@ public class JwtUtil {
*/
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token);
Jwts.parserBuilder().setSigningKey(key()).build().parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
@ -132,11 +119,11 @@ public class JwtUtil {
*
* @return 返回key
*/
private Key getSigningKey() {
String secret = jwtConfig.getKey();
private Key key() {
String secret = this.key;
if (secret == null || secret.isEmpty()) {
throw new IllegalStateException("JWT 签名密钥未配置");
}
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
}
}

View File

@ -38,6 +38,6 @@ spring.mail.properties.mail.display.sendmail=spring-boot-demo
# Jasypt配置
jasypt.encryptor.password=abc
# JWT配置
jwt.config.key=kw
jwt.config.key=daf66e01593f61a15b857cf433aae03a005812b31234e149036bcc8dee755dbb
jwt.config.ttl=600000
jwt.config.remember=604800000

View File

@ -45,41 +45,37 @@ COMMIT;
BEGIN;
INSERT INTO `wk_user` (
id, username, nickname, password, email, birthday, sex, phone, status,
create_time, update_time, last_login_time
) VALUES (
1072806377661009920,
'admin',
'管理员',
'ff342e862e7c3285cdc07e56d6b8973b',
'admin@xkcoding.com',
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
1,
'17300000000',
1,
'2018-12-12 14:52:27', -- create_time
'2018-12-12 14:52:27', -- update_time
'2018-12-12 14:52:27' -- last_login_time
);
INSERT INTO `wk_user` (id, username, nickname, password, email, birthday, sex, phone, status,
create_time, update_time, last_login_time)
VALUES (1072806377661009920,
'admin',
'管理员',
'$2a$10$iH/XRNn1Y5Gcyl1iG5zCD..Qq1L2YnL6xU0XmsdxmUs5SEsU/7jVe',
'admin@xkcoding.com',
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
1,
'17300000000',
1,
'2018-12-12 14:52:27', -- create_time
'2018-12-12 14:52:27', -- update_time
'2018-12-12 14:52:27' -- last_login_time
);
INSERT INTO `wk_user` (
id, username, nickname, password, email, birthday, sex, phone, status,
create_time, update_time, last_login_time
) VALUES (
1072806378780889088,
'user',
'普通用户',
'6c6bf02c8d5d3d128f34b1700cb1e32c',
'user@xkcoding.com',
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
1,
'17300001111',
1,
'2018-12-12 14:52:27', -- create_time
'2018-12-12 14:52:27', -- update_time
'2018-12-12 14:52:27' -- last_login_time
);
INSERT INTO `wk_user` (id, username, nickname, password, email, birthday, sex, phone, status,
create_time, update_time, last_login_time)
VALUES (1072806378780889088,
'user',
'普通用户',
'$2a$10$iH/XRNn1Y5Gcyl1iG5zCD..Qq1L2YnL6xU0XmsdxmUs5SEsU/7jVe',
'user@xkcoding.com',
'1994-11-28 00:00:00', -- birthday: 785433600000 → 1994-11-28
1,
'17300001111',
1,
'2018-12-12 14:52:27', -- create_time
'2018-12-12 14:52:27', -- update_time
'2018-12-12 14:52:27' -- last_login_time
);
COMMIT;

View File

@ -4,7 +4,7 @@ CREATE TABLE `wk_user`
`id` bigint(64) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
`username` VARCHAR(32) NOT NULL UNIQUE COMMENT '用户名',
`nickname` VARCHAR(32) NOT NULL UNIQUE COMMENT '昵称',
`password` VARCHAR(32) NOT NULL COMMENT '加密后的密码',
`password` VARCHAR(256) NOT NULL COMMENT '加密后的密码',
`email` VARCHAR(32) NOT NULL UNIQUE COMMENT '邮箱',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` INT(2) DEFAULT NULL COMMENT '性别,男-1,女-2',

View File

@ -1,11 +1,14 @@
package asia.yulinling.workflow.utils;
import asia.yulinling.workflow.config.JwtConfig;
import io.jsonwebtoken.Claims;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.List;
@ -24,34 +27,35 @@ class JwtUtilTest {
@BeforeEach
void setUp() {
// 构造 JwtConfig 模拟配置
JwtConfig config = new JwtConfig();
config.setKey("your-256-bit-secret-key-which-needs-to-be-long-enough"); // 至少256位bit
config.setTtl(3600000L); // 1小时
config.setRemember(604800000L); // 7天
jwtUtil = new JwtUtil(config);
jwtUtil = new JwtUtil();
}
@Test
void testGenerateTokenAndParseToken() {
Long userId = 123L;
String username = "test_user";
List<String> roles = List.of("USER", "ADMIN");
List<SimpleGrantedAuthority> authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
String token = jwtUtil.generateToken(false, userId, username, roles, authorities);
UserDetails user = User.withUsername("test")
.password("test")
.roles("USER")
.build();
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
String token = jwtUtil.generateToken(authentication, false);
assertNotNull(token);
Claims claims = jwtUtil.parseToken(token);
assertEquals(userId.toString(), claims.getId());
assertEquals(username, claims.getSubject());
assertEquals(roles, claims.get("roles", List.class));
assertEquals("test", claims.getSubject());
}
@Test
void testValidateToken() {
String token = jwtUtil.generateToken(false, 1L, "valid_user", List.of("USER"), List.of());
UserDetails user = User.withUsername("test")
.password("test")
.roles("USER")
.build();
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
String token = jwtUtil.generateToken(authentication, false);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String rawPassword = "admin";
String encodedPassword = encoder.encode(rawPassword);
System.out.println(encodedPassword);
assertTrue(jwtUtil.validateToken(token));
}
@ -75,7 +79,6 @@ class JwtUtilTest {
Claims claims = jwtUtil.parseToken(token);
assertEquals("admin", claims.getSubject());
assertEquals("100", claims.getId());
}
// Mock UserPrincipal 用于测试