|
@@ -0,0 +1,161 @@
|
|
|
+package com.txz.mall.core;
|
|
|
+
|
|
|
+import cn.hutool.core.date.DateField;
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
+import io.jsonwebtoken.Claims;
|
|
|
+import io.jsonwebtoken.ExpiredJwtException;
|
|
|
+import io.jsonwebtoken.Jwts;
|
|
|
+import io.jsonwebtoken.SignatureAlgorithm;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.apache.commons.lang3.tuple.ImmutableTriple;
|
|
|
+import org.apache.commons.lang3.tuple.Triple;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.context.request.RequestContextHolder;
|
|
|
+import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.UUID;
|
|
|
+
|
|
|
+
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class AuthService {
|
|
|
+
|
|
|
+ private static final String BEARER = "Bearer ";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * jwt token 密钥,主要用于token解析,签名验证
|
|
|
+ */
|
|
|
+ @Value("${spring.security.oauth2.jwt.signingKey}")
|
|
|
+ private static String signingKey = "txz123456";
|
|
|
+
|
|
|
+ private static Claims getJwt(String jwtToken) {
|
|
|
+ if (jwtToken.startsWith(BEARER)) {
|
|
|
+ jwtToken = StringUtils.substring(jwtToken, BEARER.length());
|
|
|
+ }
|
|
|
+ Claims claims;
|
|
|
+ try {
|
|
|
+ claims = Jwts.parser()
|
|
|
+ .setSigningKey(signingKey.getBytes())
|
|
|
+ .parseClaimsJws(jwtToken).getBody();
|
|
|
+ } catch (ExpiredJwtException e) {
|
|
|
+ claims = e.getClaims();
|
|
|
+ }
|
|
|
+
|
|
|
+ return claims;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String buildJwtToken(Long userId) {
|
|
|
+ String KEY = "txz123456";
|
|
|
+ Date now = DateUtil.date();
|
|
|
+ Date exp = DateUtil.offset(now, DateField.DAY_OF_YEAR, 30);
|
|
|
+ Map<String, Object> claims = new HashMap<String, Object>();
|
|
|
+ claims.put("userId", userId);
|
|
|
+
|
|
|
+ String jwtToken = Jwts.builder()
|
|
|
+ .setClaims(claims)
|
|
|
+ .setId(UUID.randomUUID().toString())
|
|
|
+ .setIssuedAt(now)
|
|
|
+ .setSubject("sawa")
|
|
|
+ .signWith(SignatureAlgorithm.HS256, KEY.getBytes())
|
|
|
+ .setExpiration(exp).compact();
|
|
|
+
|
|
|
+ return "Bearer " + jwtToken;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String buildJwtTokenWithRole(Long userId, Integer role, Integer day) {
|
|
|
+ String KEY = "123456";
|
|
|
+ Date now = DateUtil.date();
|
|
|
+ Date exp = DateUtil.offset(now, DateField.DAY_OF_YEAR, day);
|
|
|
+ Map<String, Object> claims = new HashMap<String, Object>();
|
|
|
+ claims.put("userId", userId);
|
|
|
+ claims.put("role", role);
|
|
|
+
|
|
|
+ String jwtToken = Jwts.builder()
|
|
|
+ .setClaims(claims)
|
|
|
+ .setId(UUID.randomUUID().toString())
|
|
|
+ .setIssuedAt(now)
|
|
|
+ .setSubject("sawa")
|
|
|
+ .signWith(SignatureAlgorithm.HS256, KEY.getBytes())
|
|
|
+ .setExpiration(exp).compact();
|
|
|
+
|
|
|
+ return "Bearer " + jwtToken;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Claims getClaimsFromToken(String authentication) {
|
|
|
+ try {
|
|
|
+ final Claims claims = this.getJwt(authentication);
|
|
|
+ boolean pass = DateUtil.compare(DateUtil.date(), claims.getExpiration()) < 0;
|
|
|
+ if (pass) {
|
|
|
+ return claims;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("get userId from token error:{}", e.getMessage());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long getTokenUserId(HttpServletRequest request) {
|
|
|
+ if (request == null) {
|
|
|
+ ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
|
+ if (null != sra) {
|
|
|
+ request = sra.getRequest();
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String token = request.getHeader("token");
|
|
|
+ Claims claims = getJwt(token);
|
|
|
+ return Long.valueOf(claims.get("userId").toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证 JWT token 的有效性
|
|
|
+ *
|
|
|
+ * @param token 待验证的 token 字符串(可带或不带 "Bearer " 前缀)
|
|
|
+ * @return 验证结果对象,包含:
|
|
|
+ * - 是否有效(boolean)
|
|
|
+ * - 如果无效,错误信息(String)
|
|
|
+ * - 如果有效,解析出的 Claims 对象(Claims)
|
|
|
+ */
|
|
|
+ public Triple<Boolean, String, Claims> verifyToken(String token) {
|
|
|
+ try {
|
|
|
+ // 1. 检查token是否为空
|
|
|
+ if (StringUtils.isBlank(token)) {
|
|
|
+ return ImmutableTriple.of(false, "Token不能为空", null);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 解析token获取Claims
|
|
|
+ Claims claims = getJwt(token);
|
|
|
+
|
|
|
+ // 3. 检查token是否过期
|
|
|
+ if (DateUtil.compare(DateUtil.date(), claims.getExpiration()) >= 0) {
|
|
|
+ return ImmutableTriple.of(false, "Token已过期", claims);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 检查必要字段(根据业务需求)
|
|
|
+ if (claims.get("userId") == null) {
|
|
|
+ return ImmutableTriple.of(false, "无效Token: 缺少userId字段", claims);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 所有检查通过,返回有效
|
|
|
+ return ImmutableTriple.of(true, null, claims);
|
|
|
+ } catch (ExpiredJwtException e) {
|
|
|
+ // 专门处理过期异常,可以获取到过期的claims
|
|
|
+ return ImmutableTriple.of(false, "Token已过期", e.getClaims());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Token验证失败: {}", e.getMessage());
|
|
|
+ return ImmutableTriple.of(false, "无效Token: " + e.getMessage(), null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|