|
|
@@ -4,7 +4,11 @@ import com.auth0.jwt.exceptions.AlgorithmMismatchException;
|
|
|
import com.auth0.jwt.exceptions.SignatureVerificationException;
|
|
|
import com.auth0.jwt.exceptions.TokenExpiredException;
|
|
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.fasterxml.jackson.databind.util.JSONPObject;
|
|
|
import com.roma.romaapi.utils.JWTUtil;
|
|
|
+import com.roma.romaapi.utils.CustomResponse;
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
@@ -12,6 +16,10 @@ import org.springframework.http.HttpMethod;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.web.servlet.HandlerInterceptor;
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.PrintWriter;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
import static com.roma.romaapi.utils.JWTUtil.REDIS_USER_EXPIRE_TIME;
|
|
|
@@ -22,6 +30,8 @@ import static com.roma.romaapi.utils.JWTUtil.SIGN;
|
|
|
验证通过就放行
|
|
|
验证失败就抛出异常 由统一错误处理类承接 并最终给前端返回错误信息
|
|
|
***/
|
|
|
+// 特殊错误码: 50000 表示token失效,需要跳转到登录页面 50000-50999 为校验错误信息
|
|
|
+// 51000 表示没有操作权限
|
|
|
@Slf4j
|
|
|
@Component
|
|
|
public class JWTInterceptor implements HandlerInterceptor {
|
|
|
@@ -37,48 +47,93 @@ public class JWTInterceptor implements HandlerInterceptor {
|
|
|
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
+ Map<String, Object> errorMap = new HashMap<>(); // 存放错误信息
|
|
|
String authorization = request.getHeader("Authorization");
|
|
|
- if(authorization==null || authorization.equals("")){
|
|
|
- throw new Exception("Header 未装载 token");
|
|
|
+ if(authorization==null || authorization.length()<8){
|
|
|
+ errorMap.put("sysErrorCode", "50001");
|
|
|
+ errorMap.put("sysErrorMessage", "Header 未装载 token");
|
|
|
+ // 格式化输出内容,并将map转为json字符串
|
|
|
+ String json = new ObjectMapper().writeValueAsString(CustomResponse.formatResponse(errorMap));
|
|
|
+ returnJson(response, json);
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
- String token = authorization.substring(7);
|
|
|
- if(token.equals("")){
|
|
|
- throw new Exception("Header 未装载 token");
|
|
|
+ String token = authorization.substring(7); // 截取token
|
|
|
+ // 从Redis中获取缓存中的token,判断是否过期
|
|
|
+ String redisTokenKey = SIGN + token;
|
|
|
+ String userId = stringRedisTemplate.opsForValue().get(redisTokenKey);
|
|
|
+ if(null == userId || userId.equals("")){
|
|
|
+ errorMap.put("sysErrorCode", "50000");
|
|
|
+ errorMap.put("sysErrorMessage", "token失效或已过期");
|
|
|
+ // 格式化输出内容,并将map转为json字符串
|
|
|
+ String json = new ObjectMapper().writeValueAsString(CustomResponse.formatResponse(errorMap));
|
|
|
+ returnJson(response, json);
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
+
|
|
|
try {
|
|
|
// 得到签名实体
|
|
|
DecodedJWT verify = jwtUtil.verify(token);
|
|
|
// 得到签名中的登录时间
|
|
|
String loginTimeFromToken = verify.getClaim("userLoginTime").asString();
|
|
|
// 续期
|
|
|
- String userId = stringRedisTemplate.opsForValue().get(SIGN + token);
|
|
|
- String redisTokenKey = SIGN + token;
|
|
|
stringRedisTemplate.opsForValue().set(redisTokenKey, userId, 60*30, TimeUnit.SECONDS);
|
|
|
} catch (SignatureVerificationException e) {
|
|
|
- System.out.println("token签名错误-----"+e.getMessage());
|
|
|
- throw new Exception("无效Token签名");
|
|
|
+ errorMap.put("sysErrorCode", "50002");
|
|
|
+ errorMap.put("sysErrorMessage", "无效Token签名");
|
|
|
+ // 格式化输出内容,并将map转为json字符串
|
|
|
+ String json = new ObjectMapper().writeValueAsString(CustomResponse.formatResponse(errorMap));
|
|
|
+ returnJson(response, json);
|
|
|
+
|
|
|
+ return false;
|
|
|
} catch (TokenExpiredException e) {
|
|
|
/*若抛出token过期异常,检查redis中的是否存在token以及请求头中的token与redis中的token是否相同
|
|
|
如果相同,说明用户仍在操作,只是请求头中的token已经过期,此时需要对token进行续期*/
|
|
|
// 从Redis中获取缓存中的token,判断是否过期
|
|
|
- String userId = stringRedisTemplate.opsForValue().get(SIGN + token);
|
|
|
- if(null == userId || userId.equals("")){
|
|
|
- throw new Exception("拦截器 Original Token 无效或已过期");
|
|
|
- } else {
|
|
|
- // 续期
|
|
|
- String redisTokenKey = SIGN + token;
|
|
|
- stringRedisTemplate.opsForValue().set(redisTokenKey, userId, 60*30, TimeUnit.SECONDS);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-// throw new Exception("token过期");
|
|
|
+ // 续期
|
|
|
+ stringRedisTemplate.opsForValue().set(redisTokenKey, userId, 60*30, TimeUnit.SECONDS);
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
} catch (AlgorithmMismatchException e) {
|
|
|
+ errorMap.put("sysErrorCode", "50003");
|
|
|
+ errorMap.put("sysErrorMessage", "token算法不一致");
|
|
|
+ // 格式化输出内容,并将map转为json字符串
|
|
|
+ String json = new ObjectMapper().writeValueAsString(CustomResponse.formatResponse(errorMap));
|
|
|
+ returnJson(response, json);
|
|
|
|
|
|
- throw new Exception("token算法不一致");
|
|
|
+ return false;
|
|
|
} catch (Exception e) {
|
|
|
- throw new Exception("token无效:" + e.getMessage());
|
|
|
+ errorMap.put("sysErrorCode", "50004");
|
|
|
+ errorMap.put("sysErrorMessage", "token无效:" + e.getMessage());
|
|
|
+ // 格式化输出内容,并将map转为json字符串
|
|
|
+ String json = new ObjectMapper().writeValueAsString(CustomResponse.formatResponse(errorMap));
|
|
|
+ returnJson(response, json);
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 返回客户端数据
|
|
|
+ */
|
|
|
+ private void returnJson(HttpServletResponse response, String result) throws Exception {
|
|
|
+ PrintWriter writer = null;
|
|
|
+ response.setCharacterEncoding("UTF-8");
|
|
|
+ response.setContentType("text/html; charset=utf-8");
|
|
|
+ try {
|
|
|
+ writer = response.getWriter();
|
|
|
+ writer.print(result);
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ } finally {
|
|
|
+ if (writer != null) {
|
|
|
+ writer.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|