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