Ver Fonte

增加jwt的demo

david há 2 anos atrás
pai
commit
b901cd9b02

+ 10 - 0
pom.xml

@@ -49,11 +49,21 @@
 			<artifactId>kaptcha</artifactId>
 			<artifactId>kaptcha</artifactId>
 			<version>2.3.2</version>
 			<version>2.3.2</version>
 		</dependency>
 		</dependency>
+		<!-- 引入JWT依赖 -->
+		<dependency>
+			<groupId>com.auth0</groupId>
+			<artifactId>java-jwt</artifactId>
+			<version>3.4.0</version>
+		</dependency>
 		<dependency>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-test</artifactId>
 			<artifactId>spring-boot-starter-test</artifactId>
 			<scope>test</scope>
 			<scope>test</scope>
 		</dependency>
 		</dependency>
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+		</dependency>
 	</dependencies>
 	</dependencies>
 
 
 	<build>
 	<build>

+ 27 - 0
src/main/java/com/roma/romaapi/config/InterceptorConfig.java

@@ -0,0 +1,27 @@
+package com.roma.romaapi.config;
+
+import com.roma.romaapi.interceptor.JWTInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 拦截器的配置文件
+ */
+
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+
+    @Autowired
+    private JWTInterceptor jwtInterceptor;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(jwtInterceptor)
+                //拦截的路径
+                .addPathPatterns("/**")
+                //排除登录接口
+                .excludePathPatterns("/login/verifyCode","/api/login");
+    }
+}

+ 82 - 0
src/main/java/com/roma/romaapi/interceptor/JWTInterceptor.java

@@ -0,0 +1,82 @@
+package com.roma.romaapi.interceptor;
+
+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.roma.romaapi.utils.JWTUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+/***
+ 这个拦截器就是调用了工具类中验证Token的部分 对前端用户传回的Token是否合法 以及是否保存了UID等信息进行验证
+ 验证通过就放行
+ 验证失败就抛出异常 由统一错误处理类承接 并最终给前端返回错误信息
+ ***/
+@Slf4j
+@Component
+public class JWTInterceptor implements HandlerInterceptor {
+
+    @Autowired
+    private JWTUtil jwtUtil;
+
+//    @Autowired
+//    private RedisUtil redisUtil;
+    @Autowired
+    StringRedisTemplate stringRedisTemplate;
+
+    @Override
+    public boolean preHandle(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, Object handler) throws Exception {
+        // 从Header中获得Token 和 uid 这两个是要与前端同步的
+
+        String token = request.getHeader("token");
+        String uid = request.getHeader("uid");
+
+        if(token==null || token.equals("")){
+            throw new Exception("Header 未装载 token");
+        }
+
+        if(uid==null ||  uid.equals("")){
+            throw new Exception("Header 未装载 uid");
+        }
+
+        try {
+
+            // 得到签名实体
+            DecodedJWT verify = jwtUtil.verify(token, Long.valueOf(uid));
+
+            // 得到签名中的登录时间
+            String loginTimeFromToken = verify.getClaim("userLoginTime").asString();
+
+            log.info("token:" + loginTimeFromToken);
+
+            // 从Redis中获取用户的信息
+//            User user = redisUtil.getObject(RedisPrefix.USER + uid, User.class);
+
+//            String loginTimeFromRedis = BaseUtil.localDateTime2String(user.getuLoginTime());
+
+//            log.info("redis:" + loginTimeFromRedis);
+
+//            if (!loginTimeFromRedis.equals(loginTimeFromToken)){
+//                throw new Exception("用户Token已更新");
+//            }
+
+        } catch (SignatureVerificationException e) {
+
+            throw new Exception("无效Token签名");
+        } catch (TokenExpiredException e) {
+
+            throw new Exception("token过期");
+        } catch (AlgorithmMismatchException e) {
+
+            throw new Exception("token算法不一致");
+        } catch (Exception e) {
+            throw new Exception("token无效:" + e.getMessage());
+        }
+
+        return true;
+    }
+}

+ 88 - 0
src/main/java/com/roma/romaapi/utils/JWTUtil.java

@@ -0,0 +1,88 @@
+package com.roma.romaapi.utils;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTCreator;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+
+/***
+ * Author: YL.Lou
+ * Class: JWTUtil
+ * Project: washes_base_backstage
+ * Introduce: JWT的验证工具类 被生成Token的方法Service调用 也会被 拦截器调用 验签
+ * DateTime: 2022-06-29 19:23
+ ***/
+
+@Slf4j
+@Component
+public class JWTUtil {
+    // 为Redis存储准备的Key前缀 后边跟的是用户的 ID  例如:JWT_SIGN_1 查询到的是 MD5 之后的 用户密码 信息
+    public static final String SIGN = "JWT_SIGN_";
+
+    // 加盐
+    private static final String SECRET = "lou123321!!!";
+
+    @Autowired
+    StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 获取token
+     * @return token
+     */
+    public String getToken(Map userMapInfo) {
+
+        Calendar instance = Calendar.getInstance();
+
+        //默认令牌过期时间30天
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+        instance.add(Calendar.DATE, 7);
+
+        JWTCreator.Builder builder = JWT.create();
+
+        builder.withClaim("userId", String.valueOf(userMapInfo.get("id")))
+                .withClaim("userLoginTime", String.valueOf(userMapInfo.get("loginTime")))
+                .withClaim("userName", String.valueOf(userMapInfo.get("name")))
+                .withClaim("expTime", simpleDateFormat.format(new Date(instance.getTime().getTime())));
+
+        // 将 用户ID + 用户密码 用MD5 混淆 再加盐 获取的字符串 用来生成签名
+        return builder.withExpiresAt(instance.getTime())
+                .sign(Algorithm.HMAC256(String.valueOf(userMapInfo.get("id")) + SECRET));
+    }
+
+    /**
+     * 验证token合法性 成功返回token
+     */
+    public DecodedJWT verify(String token, Long uId) throws Exception {
+
+        // 从Redis中获取用户ID + 密码 并被MD5 混淆后的字符串
+        String strSign = stringRedisTemplate.opsForValue().get(SIGN + uId);
+
+        if(null == strSign){
+            throw new Exception("Original Token 无效或已过期");
+        }
+
+        if(token==null || token.equals("")){
+            throw new Exception("token不能为空");
+        }
+
+        JWTVerifier build = JWT.require(Algorithm.HMAC256(strSign + SECRET)).build();
+
+        return build.verify(token);
+    }
+
+   /* public static void main(String[] args) {
+        DecodedJWT verify = verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTcxMDg1MDAsInVzZXJuYW1lIjoiYWRtaW4ifQ.geBEtpluViRUg66_P7ZisN3I_d4e32Wms8mFoBYM5f0");
+        System.out.println(verify.getClaim("password").asString());
+    }*/
+}