feat: 实现刷新token逻辑
This commit is contained in:
@@ -96,5 +96,11 @@
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
<version>4.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 引入redis,存储refreshToken-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -10,4 +10,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
public interface UserClient {
|
||||
@GetMapping("/inner/get-by-username")
|
||||
Result<UserAuthRespDTO> getUserByUserName(@RequestParam("userAccount") String userAccount);
|
||||
|
||||
@GetMapping("/inner/get-by-userid")
|
||||
public Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userid);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.meowrain.aioj.backend.auth.config;
|
||||
package cn.meowrain.aioj.backend.auth.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -17,5 +17,9 @@ public class JwtPropertiesConfiguration {
|
||||
/**
|
||||
* 过期时间(单位:毫秒)
|
||||
*/
|
||||
private Long expire;
|
||||
private long accessExpire; // access token TTL
|
||||
/**
|
||||
* 刷新令牌时间
|
||||
*/
|
||||
private long refreshExpire; // refresh token TTL
|
||||
}
|
||||
@@ -5,21 +5,16 @@ import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
|
||||
import cn.meowrain.aioj.backend.auth.service.AuthService;
|
||||
import cn.meowrain.aioj.backend.framework.web.Results;
|
||||
import cn.meowrain.aioj.backend.framework.web.Result;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/v1/auth")
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
public AuthController(AuthService authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public Result<UserLoginResponseDTO> login(@RequestBody UserLoginRequestDTO userLoginRequest) {
|
||||
UserLoginResponseDTO userLoginResponse = authService.userLogin(userLoginRequest);
|
||||
@@ -27,9 +22,16 @@ public class AuthController {
|
||||
|
||||
}
|
||||
|
||||
@PostMapping("/refresh")
|
||||
public Result<UserLoginResponseDTO> refresh(@RequestParam String refreshToken) {
|
||||
return Results.success(authService.refreshToken(refreshToken));
|
||||
}
|
||||
|
||||
@PostMapping("/auth")
|
||||
public Result<String> auth(@RequestBody UserLoginRequestDTO userLoginRequest) {
|
||||
UserLoginResponseDTO userLoginResponseDTO = authService.userLogin(userLoginRequest);
|
||||
return Results.success(userLoginResponseDTO.getToken());
|
||||
return Results.success(userLoginResponseDTO.getAccessToken());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,52 +22,9 @@ public class UserLoginResponseDTO implements Serializable {
|
||||
*/
|
||||
private String unionId;
|
||||
|
||||
/**
|
||||
* 公众号openId
|
||||
*/
|
||||
private String mpOpenId;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
private String userAvatar;
|
||||
|
||||
/**
|
||||
* 用户简介
|
||||
*/
|
||||
private String userProfile;
|
||||
|
||||
/**
|
||||
* 用户角色:user/admin/ban
|
||||
*/
|
||||
private String userRole;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
|
||||
private Integer isDelete;
|
||||
|
||||
|
||||
/**
|
||||
* JWT令牌(登录成功返回)
|
||||
*/
|
||||
private String token;
|
||||
private String accessToken;
|
||||
private String refreshToken;
|
||||
private Long expire;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
@@ -11,4 +11,10 @@ public interface AuthService {
|
||||
*/
|
||||
UserLoginResponseDTO userLogin(UserLoginRequestDTO request);
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
* @param refreshToken
|
||||
* @return
|
||||
*/
|
||||
UserLoginResponseDTO refreshToken(String refreshToken);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package cn.meowrain.aioj.backend.auth.service.impl;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import cn.meowrain.aioj.backend.auth.clients.UserClient;
|
||||
import cn.meowrain.aioj.backend.auth.common.constants.RedisKeyConstants;
|
||||
import cn.meowrain.aioj.backend.auth.common.enums.ChainMarkEnums;
|
||||
import cn.meowrain.aioj.backend.auth.config.properties.JwtPropertiesConfiguration;
|
||||
import cn.meowrain.aioj.backend.auth.dto.chains.context.UserLoginRequestParamVerifyContext;
|
||||
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
|
||||
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
|
||||
@@ -15,8 +17,11 @@ import cn.meowrain.aioj.backend.framework.exception.ServiceException;
|
||||
import cn.meowrain.aioj.backend.framework.web.Result;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@@ -24,6 +29,8 @@ public class AuthServiceImpl implements AuthService {
|
||||
private final JwtUtil jwtUtil;
|
||||
private final UserLoginRequestParamVerifyContext userLoginRequestParamVerifyContext;
|
||||
private final UserClient userClient;
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
private final JwtPropertiesConfiguration jwtPropertiesConfiguration;
|
||||
|
||||
@Override
|
||||
public UserLoginResponseDTO userLogin(UserLoginRequestDTO requestParam) {
|
||||
@@ -43,16 +50,57 @@ public class AuthServiceImpl implements AuthService {
|
||||
throw new ServiceException("用户不存在或者密码错误", ErrorCode.NOT_LOGIN_ERROR);
|
||||
}
|
||||
// 生成 JWT
|
||||
String token = jwtUtil.generateToken(user);
|
||||
String accessToken = jwtUtil.generateAccessToken(user);
|
||||
String refreshToken = jwtUtil.generateRefreshToken(user.getId());
|
||||
UserLoginResponseDTO resp = new UserLoginResponseDTO();
|
||||
resp.setId(user.getId());
|
||||
resp.setUserAccount(user.getUserAccount());
|
||||
resp.setUserAvatar(user.getUserAvatar());
|
||||
resp.setUserProfile(user.getUserProfile());
|
||||
resp.setUserRole(user.getUserRole());
|
||||
resp.setCreateTime(user.getCreateTime());
|
||||
resp.setUpdateTime(user.getUpdateTime());
|
||||
resp.setToken(token);
|
||||
resp.setAccessToken(accessToken);
|
||||
resp.setRefreshToken(refreshToken);
|
||||
|
||||
// refresh token存入到REDIS里面
|
||||
stringRedisTemplate.opsForValue().set(
|
||||
String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, user.getId()),
|
||||
refreshToken,
|
||||
jwtPropertiesConfiguration.getRefreshExpire(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新access token,使用refresh token
|
||||
* @param refreshToken
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserLoginResponseDTO refreshToken(String refreshToken) {
|
||||
UserLoginResponseDTO userLoginResponseDTO = new UserLoginResponseDTO();
|
||||
if (!jwtUtil.isTokenValid(refreshToken)) {
|
||||
throw new RuntimeException("Refresh Token 已过期");
|
||||
}
|
||||
|
||||
Long userId = Long.valueOf(jwtUtil.parseClaims(refreshToken).getSubject());
|
||||
|
||||
String cacheKey = String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, userId);
|
||||
String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||
|
||||
if (cacheValue == null || !cacheValue.equals(refreshToken)) {
|
||||
throw new RuntimeException("Refresh Token 已失效");
|
||||
}
|
||||
|
||||
// 再次签发新的 Access Token
|
||||
// 此处你需要查用户,拿 userName, role
|
||||
Result<UserAuthRespDTO> userResult = userClient.getUserById(String.valueOf(userId));
|
||||
if (userResult.isFail()) {
|
||||
log.error("通过id查找用户失败:{}", userResult.getMessage());
|
||||
throw new ServiceException(ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
UserAuthRespDTO user = userResult.getData();
|
||||
String newAccessToken = jwtUtil.generateAccessToken(user);
|
||||
|
||||
//设置refresh token和access token
|
||||
userLoginResponseDTO.setRefreshToken(refreshToken);
|
||||
userLoginResponseDTO.setAccessToken(newAccessToken);
|
||||
return userLoginResponseDTO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.meowrain.aioj.backend.auth.utils;
|
||||
|
||||
import cn.meowrain.aioj.backend.auth.config.JwtPropertiesConfiguration;
|
||||
import cn.meowrain.aioj.backend.auth.config.properties.JwtPropertiesConfiguration;
|
||||
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
@@ -13,32 +13,47 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JWT工具类
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
private final JwtPropertiesConfiguration jwtConfig;
|
||||
|
||||
private SecretKey getSigningKey() {
|
||||
return Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes());
|
||||
}
|
||||
|
||||
public String generateToken(UserAuthRespDTO user) {
|
||||
/** 生成 Access Token */
|
||||
public String generateAccessToken(UserAuthRespDTO user) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("userId", user.getId());
|
||||
claims.put("userName", user.getUserName());
|
||||
claims.put("role", user.getUserRole());
|
||||
|
||||
return Jwts.builder()
|
||||
.subject(user.getUserAccount())
|
||||
.issuedAt(new Date(now))
|
||||
.expiration(new Date(now + jwtConfig.getExpire()))
|
||||
.expiration(new Date(now + jwtConfig.getAccessExpire()))
|
||||
.claims(claims)
|
||||
.signWith(getSigningKey(), Jwts.SIG.HS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
/** 生成 Refresh Token(只含 userId) */
|
||||
public String generateRefreshToken(Long userId) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
return Jwts.builder()
|
||||
.subject(String.valueOf(userId))
|
||||
.issuedAt(new Date(now))
|
||||
.expiration(new Date(now + jwtConfig.getRefreshExpire()))
|
||||
.signWith(getSigningKey(), Jwts.SIG.HS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
/** 解析 Token */
|
||||
public Claims parseClaims(String token) {
|
||||
return Jwts.parser()
|
||||
.verifyWith(getSigningKey())
|
||||
@@ -47,12 +62,12 @@ public class JwtUtil {
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
/** 校验 Token 是否过期 */
|
||||
public boolean isTokenValid(String token) {
|
||||
try {
|
||||
Claims claims = parseClaims(token);
|
||||
Date expiration = claims.getExpiration();
|
||||
return expiration != null && expiration.after(new Date());
|
||||
} catch (Exception e) {
|
||||
return claims.getExpiration().after(new Date());
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
spring:
|
||||
data:
|
||||
redis:
|
||||
host: 10.0.0.10
|
||||
port: 6379
|
||||
password: 123456
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
|
||||
@@ -34,4 +34,5 @@ mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
jwt:
|
||||
secret: "12345678901234567890123456789012" # 至少32字节!!
|
||||
expire: 86400000 # 24小时(单位:毫秒)
|
||||
access-expire: 900000 # 24小时
|
||||
refresh-expire: 604800000 # 7天
|
||||
@@ -28,5 +28,10 @@ public class UserController {
|
||||
return Results.success(userAuthDTO);
|
||||
}
|
||||
|
||||
@GetMapping("/inner/get-by-userid")
|
||||
public Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userid) {
|
||||
UserAuthRespDTO userAuthRespDTO = userService.findAuthInfoByUserId(userid);
|
||||
return Results.success(userAuthRespDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,4 +22,9 @@ public interface UserService extends IService<User> {
|
||||
* @return
|
||||
*/
|
||||
UserAuthRespDTO findAuthInfoByUserAccount(String userAccount);
|
||||
|
||||
/**
|
||||
* 根据用户id查找用户认证信息
|
||||
*/
|
||||
UserAuthRespDTO findAuthInfoByUserId(String userId);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
@Override
|
||||
public Long userRegister(UserRegisterRequestDTO request) {
|
||||
UserAuthRespDTO authInfoByUserAccount = findAuthInfoByUserAccount(request.getUserAccount());
|
||||
if(authInfoByUserAccount!=null){
|
||||
if (authInfoByUserAccount != null) {
|
||||
throw new ClientException("重复创建用户");
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
// 使用 BCrypt 加密密码
|
||||
Date now = new Date();
|
||||
String salt = BCrypt.gensalt();
|
||||
String encryptPassword = BCrypt.hashpw(request.getUserPassword(),salt);
|
||||
String encryptPassword = BCrypt.hashpw(request.getUserPassword(), salt);
|
||||
User user = new User().setUserAccount(request.getUserAccount()).setUserPassword(encryptPassword)
|
||||
.setUserRole("user").setCreateTime(now).setUpdateTime(now);
|
||||
try {
|
||||
@@ -59,10 +59,22 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
public UserAuthRespDTO findAuthInfoByUserAccount(String userAccount) {
|
||||
User one = this.lambdaQuery().eq(User::getUserAccount, userAccount).one();
|
||||
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
|
||||
if(one!=null){
|
||||
if (one != null) {
|
||||
BeanUtils.copyProperties(one, userAuthDTO);
|
||||
return userAuthDTO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthRespDTO findAuthInfoByUserId(String userId) {
|
||||
User one = this.lambdaQuery().eq(User::getId, userId).one();
|
||||
UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
|
||||
if (one != null) {
|
||||
BeanUtils.copyProperties(one, userAuthDTO);
|
||||
return userAuthDTO;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user