生成JWT令牌

This commit is contained in:
bunny 2025-07-16 00:05:10 +08:00
parent 4a43bf26b8
commit f803241c78
7 changed files with 548 additions and 4 deletions

View File

@ -1355,3 +1355,169 @@ public class AuthenticationEvents {
}
}
```
## 实现JWT的认证
### 生成JWT令牌
> [!TIP]
>
> 如果需要JWTUtil工具类需要到项目的`com.spring.step2.utils.JwtTokenUtil`下寻找。
在Spring的配置文件中写入下面的内容。
```yaml
jwtToken:
# 密钥
secret: aVeryLongAndSecureRandomStringWithAtLeast32Characters
# 主题
subject: SecurityBunny
# 过期事件 7天
expired: 604800
```
之后要读取配置文件中的信息。
在文件中包含三个方法分别是创建令牌解析令牌为用户名解析令牌为用户Id。
```java
@Configuration
@ConfigurationProperties(prefix = "jwt-token")
public class JwtBearTokenService {
@Value("${jwtToken.secret}")
public String secret;
@Value("${jwtToken.subject}")
public String subject;
// private final SecretKey securityKey = Keys.hmacShaKeyFor("Bunny-Auth-Server-Private-SecretKey".getBytes(StandardCharsets.UTF_8));
// JWT 的 秘钥
@Value("${jwtToken.expired}")
public Long expired;
/**
* 创建Token
*
* @param userId 用户Id
* @param username 用户名
* @return 令牌Token
*/
public String createToken(Long userId, String username,
List<String> roles, List<String> permissions) {
SecretKey key = getSecretKey();
// return JwtTokenUtil.createToken(userId, username, subject, key, expired);
return JwtTokenUtil.createToken(userId, username, roles, permissions, subject, key, expired);
}
/**
* 获取安全密钥
*
* @return {@link SecretKey}
*/
private SecretKey getSecretKey() {
byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
return new SecretKeySpec(secretBytes, "HmacSHA256");
}
/**
* 根据Token获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
SecretKey secretKey = getSecretKey();
return JwtTokenUtil.getUsername(token, secretKey);
}
/**
* 根据Token获取用户Id
*
* @param token 令牌
* @return 用户Id
*/
public Long getUserIdFromToken(String token) {
SecretKey secretKey = getSecretKey();
return JwtTokenUtil.getUserId(token, secretKey);
}
/**
* 根据Token获取用户Id
*
* @param token 令牌
* @return 用户Id
*/
public Map<String, Object> getMapByToken(String token) {
SecretKey secretKey = getSecretKey();
return JwtTokenUtil.getMapByToken(token, secretKey);
}
}
```
之后对方法进行测试。
```java
@SpringBootTest
class JwtBearTokenServiceTest {
private String token;
@Autowired
private JwtBearTokenService jwtBearTokenService;
@Autowired
private DbUserDetailService dbUserDetailService;
@Test
void createToken() {
long userId = 1944384432521744386L;
List<String> roles = dbUserDetailService.findUserRolesByUserId(userId);
List<String> permissions = dbUserDetailService.findPermissionByUserId(userId);
String token = jwtBearTokenService.createToken(userId,
"Bunny",
roles,
permissions);
this.token = token;
System.out.println(token);
}
@Test
void getUsernameFromToken() {
createToken();
String username = jwtBearTokenService.getUsernameFromToken(token);
System.out.println(username);
Long userId = jwtBearTokenService.getUserIdFromToken(token);
System.out.println(userId);
Map<String, Object> map = jwtBearTokenService.getMapByToken(token);
List<?> roles = (List<?>) map.get("roles");
System.out.println(roles);
// 安全转换 permissions
List<?> permissions = (List<?>) map.get("permissions");
System.out.println(permissions);
}
}
```
输出以下内容:
```properties
# 生成的令牌
eyJ6aXAiOiJHWklQIiwiYWxnIjoiSFMyNTYifQ.H4sIAAAAAAAA_0WMzQ6CMBCE32XPNIHSH-hNowcOepB4Mh5qWRKMFNLSRGJ8d6tiPEyyM7PzPcCHCyio0QTXTfM6WDtDAngfQWWSU16yjLMEgkdXNTErGcuLKMppJt-3-JZW9xhBP4AbbuhBnWC12VX7GBzr7QHOCYzo-s77brCf-m-VcqibZbqY-H-duojFFAUvTUtaqTVhVEpSmEIQnjV5m_NUtIbC8wV0LG6kzgAAAA.lf4TXuafIQ2X5Ec3CsKR5ZN3q9KpJkeEBiYQbmNMuoQ
# 解析的用户名
Bunny
# 解析的用户Id
1944384432521744386
# 解析的用户角色
[ADMIN, USER]
# 解析的用户权限
[permission::read, role::read]
```

View File

@ -19,9 +19,15 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
<jwt.version>0.12.6</jwt.version>
</properties>
<dependencies>
<!--jjwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -37,7 +37,7 @@ public class DbUserDetailService implements UserDetailsService {
Long userId = userEntity.getId();
// 设置用户角色
String[] roles = findUserRolesByUserId(userId);
String[] roles = findUserRolesByUserId(userId).toArray(String[]::new);
// 设置用户权限
List<String> permissionsByUserId = findPermissionByUserId(userId);
@ -66,9 +66,9 @@ public class DbUserDetailService implements UserDetailsService {
* @param userId 用户id
* @return 当前用户的角色信息
*/
public String[] findUserRolesByUserId(Long userId) {
public List<String> findUserRolesByUserId(Long userId) {
List<RoleEntity> roleList = userMapper.selectRolesByUserId(userId);
return roleList.stream().map(RoleEntity::getRoleCode).toArray(String[]::new);
return roleList.stream().map(RoleEntity::getRoleCode).toList();
}
/**

View File

@ -0,0 +1,86 @@
package com.spring.step2.security.service;
import com.spring.step2.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
@Configuration
@ConfigurationProperties(prefix = "jwt-token")
public class JwtBearTokenService {
@Value("${jwtToken.secret}")
public String secret;
@Value("${jwtToken.subject}")
public String subject;
// private final SecretKey securityKey = Keys.hmacShaKeyFor("Bunny-Auth-Server-Private-SecretKey".getBytes(StandardCharsets.UTF_8));
// JWT 秘钥
@Value("${jwtToken.expired}")
public Long expired;
/**
* 创建Token
*
* @param userId 用户Id
* @param username 用户名
* @return 令牌Token
*/
public String createToken(Long userId, String username,
List<String> roles, List<String> permissions) {
SecretKey key = getSecretKey();
// return JwtTokenUtil.createToken(userId, username, subject, key, expired);
return JwtTokenUtil.createToken(userId, username, roles, permissions, subject, key, expired);
}
/**
* 获取安全密钥
*
* @return {@link SecretKey}
*/
private SecretKey getSecretKey() {
byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
return new SecretKeySpec(secretBytes, "HmacSHA256");
}
/**
* 根据Token获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
SecretKey secretKey = getSecretKey();
return JwtTokenUtil.getUsername(token, secretKey);
}
/**
* 根据Token获取用户Id
*
* @param token 令牌
* @return 用户Id
*/
public Long getUserIdFromToken(String token) {
SecretKey secretKey = getSecretKey();
return JwtTokenUtil.getUserId(token, secretKey);
}
/**
* 根据Token获取用户Id
*
* @param token 令牌
* @return 用户Id
*/
public Map<String, Object> getMapByToken(String token) {
SecretKey secretKey = getSecretKey();
return JwtTokenUtil.getMapByToken(token, secretKey);
}
}

View File

@ -0,0 +1,223 @@
package com.spring.step2.utils;
import com.spring.step2.domain.vo.result.ResultCodeEnum;
import com.spring.step2.exception.SecurityException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.micrometer.common.lang.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Slf4j
public class JwtTokenUtil {
/**
* 使用默认主题默认秘钥自定义时间创建集合形式token
*
* @param map 集合
* @param day 过期时间
* @return token
*/
public static String createTokenWithMap(Map<String, Object> map, String subject, SecretKey key, Long day) {
return Jwts.builder()
.subject(subject)
.signWith(key)
.expiration(new Date(System.currentTimeMillis() + day))
.claims(map)
.id(UUID.randomUUID().toString())
.compressWith(Jwts.ZIP.GZIP).compact();
}
/**
* 使用自定义主题自定义时间创建集合形式token
*
* @param map 集合
* @param subject 主题
* @param time 过期时间
* @return token
*/
public static String createTokenWithMap(Map<String, Object> map, String subject, SecretKey key, Date time) {
return Jwts.builder()
.subject(subject)
.expiration(time)
.claims(map)
.id(UUID.randomUUID().toString())
.signWith(key)
.compressWith(Jwts.ZIP.GZIP).compact();
}
/**
* 创建集合形式token
*
* @param map 集合
* @param subject 主题
* @param day 过期时间
* @return token
*/
public static String createTokenWithMap(Map<String, Object> map, String subject, SecretKey key, Integer day) {
return Jwts.builder()
.subject(subject)
.expiration(new Date(System.currentTimeMillis() + day))
.claims(map)
.id(UUID.randomUUID().toString())
.signWith(key)
.compressWith(Jwts.ZIP.GZIP).compact();
}
/**
* 根据用户名和ID创建token
*
* @param userId 用户ID
* @param username 用户名
* @param day 过期时间
* @return token值
*/
public static String createToken(Long userId, String username, String subject, SecretKey key, Long day) {
return Jwts.builder()
.subject(subject)
.expiration(new Date(System.currentTimeMillis() + day))
.claim("userId", userId)
.claim("username", username)
.id(UUID.randomUUID().toString())
.signWith(key)
.compressWith(Jwts.ZIP.GZIP).compact();
}
/**
* 根据用户名和ID创建token
* 在载体中添加角色和权限
*
* @param userId 用户ID
* @param username 用户名
* @param day 过期时间
* @return token值
*/
public static String createToken(Long userId, String username,
List<String> roles, List<String> permissions,
String subject, SecretKey key, Long day) {
return Jwts.builder()
.subject(subject)
.expiration(new Date(System.currentTimeMillis() + day))
.claim("userId", userId)
.claim("username", username)
.claim("roles", roles)
.claim("permissions", permissions)
.id(UUID.randomUUID().toString())
.signWith(key)
.compressWith(Jwts.ZIP.GZIP).compact();
}
/**
* 使用token获取map集合,使用默认秘钥
*
* @param token token
* @return map集合
*/
public static Map<String, Object> getMapByToken(String token, SecretKey key) {
try {
if (!StringUtils.hasText(token)) throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
// body 值转为map
return Jwts.parser().verifyWith(key).build().parseSignedClaims(token).getPayload();
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
@Nullable
private static String getSubjectByTokenHandler(String token, SecretKey key) {
try {
if (!StringUtils.hasText(token)) throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
Claims body = claimsJws.getPayload();
return body.getSubject();
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
/**
* 根据token获取主题
*
* @param token token
* @return 主题
*/
public static String getSubjectByToken(String token, SecretKey key) {
return getSubjectByTokenHandler(token, key);
}
/**
* 根据token获取用户ID
*
* @param token token
* @return 用户ID
*/
public static Long getUserId(String token, SecretKey key) {
try {
if (!StringUtils.hasText(token)) throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
Claims claims = claimsJws.getPayload();
return Long.valueOf(String.valueOf(claims.get("userId")));
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
/**
* 根据token获取用户名
*
* @param token token
* @return 用户名
*/
public static String getUsername(String token, SecretKey key) {
try {
if (!StringUtils.hasText(token)) return "";
Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
Claims claims = claimsJws.getPayload();
return (String) claims.get("username");
} catch (Exception exception) {
throw new SecurityException(ResultCodeEnum.TOKEN_PARSING_FAILED);
}
}
/**
* 判断token是否过期
*
* @param token token
* @return 是否过期
*/
public static boolean isExpired(String token, SecretKey key) {
return isExpiredUtil(token, key);
}
/**
* 判断是否过期
*
* @param token token
* @return 是否过期
*/
private static boolean isExpiredUtil(String token, SecretKey key) {
try {
Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
Date expiration = claimsJws.getPayload().getExpiration();
return expiration != null && expiration.before(new Date());
} catch (Exception exception) {
log.error(exception.getMessage(), exception);
return true;
}
}
}

View File

@ -48,3 +48,10 @@ logging:
com.spring: debug
org.springframework.security: debug
jwtToken:
# 密钥
secret: aVeryLongAndSecureRandomStringWithAtLeast32Characters
# 主题
subject: SecurityBunny
# 过期事件 7天
expired: 604800

View File

@ -0,0 +1,56 @@
package com.spring.step2.security.config;
import com.spring.step2.security.service.DbUserDetailService;
import com.spring.step2.security.service.JwtBearTokenService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class JwtBearTokenServiceTest {
private String token;
@Autowired
private JwtBearTokenService jwtBearTokenService;
@Autowired
private DbUserDetailService dbUserDetailService;
@Test
void createToken() {
long userId = 1944384432521744386L;
List<String> roles = dbUserDetailService.findUserRolesByUserId(userId);
List<String> permissions = dbUserDetailService.findPermissionByUserId(userId);
String token = jwtBearTokenService.createToken(userId,
"Bunny",
roles,
permissions);
this.token = token;
System.out.println(token);
}
@Test
void getUsernameFromToken() {
createToken();
String username = jwtBearTokenService.getUsernameFromToken(token);
System.out.println(username);
Long userId = jwtBearTokenService.getUserIdFromToken(token);
System.out.println(userId);
Map<String, Object> map = jwtBearTokenService.getMapByToken(token);
List<?> roles = (List<?>) map.get("roles");
System.out.println(roles);
// 安全转换 permissions
List<?> permissions = (List<?>) map.get("permissions");
System.out.println(permissions);
}
}