14 changed files with 320 additions and 46 deletions
@ -1,21 +0,0 @@ |
|||||||
package com.biutag.outeradmin.config; |
|
||||||
|
|
||||||
import com.biutag.outeradmin.entity.User; |
|
||||||
import jakarta.servlet.http.HttpServletRequest; |
|
||||||
import jakarta.servlet.http.HttpServletResponse; |
|
||||||
import jakarta.servlet.http.HttpSession; |
|
||||||
import org.springframework.web.servlet.HandlerInterceptor; |
|
||||||
|
|
||||||
|
|
||||||
public class UserLoginInterceptor implements HandlerInterceptor { |
|
||||||
@Override |
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
|
||||||
HttpSession session = request.getSession(); |
|
||||||
User user = (User) session.getAttribute("user"); |
|
||||||
if (user != null) |
|
||||||
return true; |
|
||||||
// response.sendRedirect(request.getContextPath() + "/login");
|
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,49 @@ |
|||||||
|
package com.biutag.outeradmin.controller; |
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON; |
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||||
|
import com.biutag.outeradmin.dto.TokenDTO; |
||||||
|
import com.biutag.outeradmin.entity.User; |
||||||
|
import com.biutag.outeradmin.service.UserService; |
||||||
|
import com.biutag.outeradmin.util.JwtUtil; |
||||||
|
import io.jsonwebtoken.Claims; |
||||||
|
import lombok.RequiredArgsConstructor; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
@RestController |
||||||
|
@RequiredArgsConstructor |
||||||
|
public class JwtController { |
||||||
|
private final UserService userService; |
||||||
|
|
||||||
|
|
||||||
|
@RequestMapping("/refresh-token") |
||||||
|
public TokenDTO refreshToken(@RequestBody String reToken) { |
||||||
|
TokenDTO tokenDTOReceived = JSON.parseObject(reToken, TokenDTO.class); |
||||||
|
String token = tokenDTOReceived.getRefreshToken(); |
||||||
|
log.info("refresh_token:{}", token); |
||||||
|
try { |
||||||
|
Claims claims = JwtUtil.parseJwtToken(token).getPayload(); |
||||||
|
log.info("refresh token:{}", token); |
||||||
|
log.info("claims:{}", claims); |
||||||
|
String phone = claims.get("username", String.class); |
||||||
|
TokenDTO tokenDTO = new TokenDTO(); |
||||||
|
|
||||||
|
User user = userService.getOne(new QueryWrapper<User>().eq("phone", phone)); |
||||||
|
// 如果用户存在,就生成一个新的令牌
|
||||||
|
if (user != null) { |
||||||
|
String refreshToken = JwtUtil.getNewRefreshToken(token); |
||||||
|
String accessToken = JwtUtil.getNewAccessToken(token); |
||||||
|
tokenDTO.setRefreshToken(refreshToken); |
||||||
|
tokenDTO.setAccessToken(accessToken); |
||||||
|
} |
||||||
|
log.info("refresh token success:{}", tokenDTO.toString()); |
||||||
|
return tokenDTO; |
||||||
|
} catch (Exception e) { |
||||||
|
throw new RuntimeException(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
package com.biutag.outeradmin.dto; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
import org.apache.poi.ss.formula.functions.T; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class TokenDTO { |
||||||
|
private String accessToken; |
||||||
|
private String refreshToken; |
||||||
|
private int stateCode; |
||||||
|
private List<T> list; |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
package com.biutag.outeradmin.exception; |
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||||
|
|
||||||
|
@RestControllerAdvice |
||||||
|
public class GlobalExceptionHandler { |
||||||
|
@ExceptionHandler(TokenException.class) |
||||||
|
public String handleTokenException(TokenException e, HttpServletResponse response) { |
||||||
|
response.setStatus(e.getCode()); |
||||||
|
return e.getMessage(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
package com.biutag.outeradmin.exception; |
||||||
|
|
||||||
|
public class TokenException extends RuntimeException{ |
||||||
|
private int code; |
||||||
|
|
||||||
|
public TokenException(int code, String message) { |
||||||
|
super(message); |
||||||
|
this.code = code; |
||||||
|
} |
||||||
|
|
||||||
|
public int getCode() { |
||||||
|
return code; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
package com.biutag.outeradmin.interceptor; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||||
|
import com.biutag.outeradmin.entity.User; |
||||||
|
import com.biutag.outeradmin.service.UserService; |
||||||
|
import com.biutag.outeradmin.util.JwtUtil; |
||||||
|
import io.jsonwebtoken.Claims; |
||||||
|
import io.jsonwebtoken.ExpiredJwtException; |
||||||
|
import jakarta.servlet.http.HttpServletRequest; |
||||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public class UserLoginInterceptor implements HandlerInterceptor { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private UserService userService; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
||||||
|
String token = request.getHeader("Authorization"); |
||||||
|
if (token != null && token.startsWith("Bearer ")) { |
||||||
|
token = token.substring(7); |
||||||
|
try { |
||||||
|
Claims claims = JwtUtil.parseJwtToken(token).getPayload(); |
||||||
|
String name = claims.get("username", String.class); |
||||||
|
Date date = claims.getExpiration(); |
||||||
|
if (name != null && date != null && date.after(new Date())) { |
||||||
|
log.info("用户{}已登录", name); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
if (e instanceof ExpiredJwtException) { |
||||||
|
response.setStatus(401); |
||||||
|
log.info("用户未登录或长时间未操作导致登陆失效"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
throw new RuntimeException("Token无效或过期"); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { |
||||||
|
if (ex == null) { |
||||||
|
String token = request.getHeader("Authorization"); |
||||||
|
token = token.substring(7); |
||||||
|
Claims claims = JwtUtil.parseJwtToken(token).getPayload(); |
||||||
|
String phone = claims.get("username", String.class); |
||||||
|
|
||||||
|
User user = userService.getOne(new QueryWrapper<User>().eq("phone", phone)); |
||||||
|
// 如果用户存在,就生成一个新的令牌
|
||||||
|
if (user != null) { |
||||||
|
String newToken = JwtUtil.getNewAccessToken(token); |
||||||
|
// 将新的令牌添加到响应头中
|
||||||
|
response.setHeader("Authorization", "Bearer " + newToken); |
||||||
|
log.info("用户{}的令牌已更新", phone); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,107 @@ |
|||||||
|
package com.biutag.outeradmin.util; |
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims; |
||||||
|
import io.jsonwebtoken.Jws; |
||||||
|
import io.jsonwebtoken.JwsHeader; |
||||||
|
import io.jsonwebtoken.Jwts; |
||||||
|
import io.jsonwebtoken.security.Keys; |
||||||
|
import io.jsonwebtoken.security.SecureDigestAlgorithm; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
import javax.crypto.SecretKey; |
||||||
|
import java.time.Instant; |
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
public class JwtUtil { |
||||||
|
// TODO 正式上线以后需要修改accessToken持续时间
|
||||||
|
public static final long EXPIRATION_TIME = 5; // 1h
|
||||||
|
public static final long REFRESH_TIME = 60 * 60 * 24 * 7; |
||||||
|
public static final String APP_SECRET = "5FtX1OpGWKBVyaIh3dwRl04gkAzLro6U"; |
||||||
|
public static final SecretKey KEY = Keys.hmacShaKeyFor(APP_SECRET.getBytes()); |
||||||
|
public static final SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256; |
||||||
|
private final static String JWT_ISS = "admin"; |
||||||
|
|
||||||
|
private final static String SUBJECT = "Peripherals"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建双Token |
||||||
|
* |
||||||
|
* @param phone 用户手机号 |
||||||
|
* @param captcha 验证码 |
||||||
|
* @param ifRefresh 是否refreshToken控制参数 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
|
||||||
|
public static String buildJwtToken(String phone, String captcha, boolean ifRefresh) { |
||||||
|
Date exprireDate = Date.from(Instant.now().plusSeconds(EXPIRATION_TIME)); |
||||||
|
if (ifRefresh) |
||||||
|
exprireDate = Date.from(Instant.now().plusSeconds(REFRESH_TIME)); |
||||||
|
return Jwts.builder() |
||||||
|
.header().add("typ", "JWT").add("alg", "HS256") |
||||||
|
.and() |
||||||
|
.claim("username", phone).id(captcha) |
||||||
|
.expiration(exprireDate) |
||||||
|
.issuedAt(new Date()) |
||||||
|
.subject(SUBJECT) |
||||||
|
.issuer(JWT_ISS) |
||||||
|
.signWith(KEY, ALGORITHM) |
||||||
|
.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在accessToken发送过来时刷新refreshToken |
||||||
|
* |
||||||
|
* @param Token |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getNewRefreshToken(String Token) { |
||||||
|
Jws<Claims> data = parseJwtToken(Token); |
||||||
|
String name = data.getPayload().get("username", String.class); |
||||||
|
String captcha = data.getPayload().getId(); |
||||||
|
return buildJwtToken(name, captcha, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* accessToken过期时依据未过期的refreshToken刷新accessToken |
||||||
|
* |
||||||
|
* @param refreshToken |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getNewAccessToken(String refreshToken) { |
||||||
|
Jws<Claims> data = parseJwtToken(refreshToken); |
||||||
|
String name = data.getPayload().get("username", String.class); |
||||||
|
String captcha = data.getPayload().getId(); |
||||||
|
return buildJwtToken(name, captcha, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解析Token |
||||||
|
* |
||||||
|
* @param token |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Jws<Claims> parseJwtToken(String token) { |
||||||
|
return Jwts.parser().verifyWith(KEY).build().parseSignedClaims(token); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解析Token中的header |
||||||
|
* |
||||||
|
* @param token |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static JwsHeader parseTokenHeader(String token) { |
||||||
|
return parseJwtToken(token).getHeader(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解析Token中的payload |
||||||
|
* |
||||||
|
* @param token |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Claims parsePayload(String token) { |
||||||
|
return parseJwtToken(token).getPayload(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
package com.biutag.outeradmin.util; |
||||||
|
|
||||||
|
import cn.hutool.crypto.SecureUtil; |
||||||
|
import cn.hutool.crypto.symmetric.AES; |
||||||
|
|
||||||
|
public class PhoneEncryptUtil { |
||||||
|
private static final String KEY = "D9ioYl2WCeVRfLXvg4FrqZ51UtT8Ewcz"; |
||||||
|
|
||||||
|
private static AES init() { |
||||||
|
String result = ""; |
||||||
|
byte[] key = SecureUtil.generateKey(KEY).getEncoded(); |
||||||
|
return new AES(key); |
||||||
|
} |
||||||
|
|
||||||
|
public static String encrypt(String phone) { |
||||||
|
return init().encryptHex(phone); |
||||||
|
} |
||||||
|
|
||||||
|
public static String decrypt(String phone) { |
||||||
|
return init().decryptStr(phone); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue