Browse Source

jwt集成完成

lcl 1 year ago
parent
commit
f81f2d05bd

+ 22 - 0
pom.xml

@@ -86,6 +86,28 @@
 			<artifactId>fastjson</artifactId>
 			<version>2.0.34</version>
 		</dependency>
+		<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt-api</artifactId>
+			<version>0.11.5</version>
+		</dependency>
+
+		<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt-impl</artifactId>
+			<version>0.11.5</version>
+			<scope>runtime</scope>
+		</dependency>
+
+		<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt-jackson</artifactId>
+			<version>0.11.5</version>
+			<scope>runtime</scope>
+		</dependency>
 	</dependencies>
 
 	<build>

BIN
src/main/java/com/.DS_Store


BIN
src/main/java/com/kxs/adminap/.DS_Store


+ 37 - 0
src/main/java/com/kxs/adminap/config/FilterConfig.java

@@ -0,0 +1,37 @@
+package com.kxs.adminap.config;
+
+ 
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.kxs.adminap.filter.JWTFilter;
+ 
+/**
+ * 过滤器配置
+ *
+ * @author kou
+ */
+@Configuration
+public class FilterConfig {
+ 
+    @Autowired
+    private JWTFilter jwtFilter;
+ 
+    @Bean
+    public FilterRegistrationBean registrationBean() {
+ 
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        // 设置过滤器
+        registration.setFilter(jwtFilter);
+        // 设置拦截路径
+        registration.addUrlPatterns("/api/*");
+        // 设置过滤器名称
+        registration.setName("JWTFilter");
+        // 设置过滤器执行顺序
+        registration.setOrder(1);
+ 
+        return registration;
+    }
+}

+ 10 - 7
src/main/java/com/kxs/adminap/controller/TestController.java

@@ -17,6 +17,7 @@ import com.kxs.adminap.config.Base;
 // import com.kxs.adminap.enity.TestTable;
 import com.kxs.adminap.service.DbConn;
 import com.kxs.adminap.util.DesUtil;
+import com.kxs.adminap.util.JwtUtil;
 import com.kxs.adminap.util.RedisUtils;
 import com.kxs.adminap.util.Util;
 
@@ -53,13 +54,15 @@ public class TestController {
 		// TestTable d = testTablerepository.findById(2).get();
 		// redisUtils.addList("test:list", d);
 
-		String json = "{\"MerchantId\":12}";
-		try {
-			json = DesUtil.encrypt(json);
-			json = DesUtil.decrypt(json);
-		} catch (Exception e) {
-			e.printStackTrace();
-		}
+		// String json = "{\"MerchantId\":12}";
+		// try {
+		// 	json = DesUtil.encrypt(json);
+		// 	json = DesUtil.decrypt(json);
+		// } catch (Exception e) {
+		// 	e.printStackTrace();
+		// }
+
+		// result = new JwtUtil().generateToken("test");
 
 		return result;
 	}

+ 140 - 0
src/main/java/com/kxs/adminap/filter/JWTFilter.java

@@ -0,0 +1,140 @@
+package com.kxs.adminap.filter;
+
+ 
+import io.jsonwebtoken.Claims;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import com.kxs.adminap.util.JwtUtil;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+ 
+/**
+ * 如果请求中(请求头或者Cookie)中存在JWT,则:
+ * 1、解析JWT并查找对应的用户信息,然后加入request attribute中
+ * 2、更新Cookie时间、更新JWT失效时间放入Header
+ * <p>
+ * OncePerRequestFilter 一次请求只进入一次filter
+ */
+@Component
+public class JWTFilter extends OncePerRequestFilter {
+ 
+    public static final String SECURITY_USER = "SECURITY_USER";
+ 
+    // 设置不需要校验的路径
+    private static final String[] NOT_CHECK_URL = {"/login"};
+ 
+    @Autowired
+    private JwtUtil jwtUtil;
+ 
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ 
+        // 判断是否需要对token处理
+        if (!isNotCheck(request.getRequestURI())) {
+            // 获取token
+            String token = getToken(request);
+ 
+            if (token.isBlank()) {
+                request.setAttribute("exceptionCode", 10001);
+                request.setAttribute("exceptionMessage", "请求无效,原因:" + jwtUtil.getHeader() + " 为空!");
+                request.getRequestDispatcher("/exception/authentication").forward(request, response);
+                // throw new AuthenticationException(SystemParameters.TOKEN_IS_NULL.getIndex(),  "请求无效,原因:" + jwtUtil.getHeader() + " 为空!");
+                return;
+            }
+ 
+            Claims claims = jwtUtil.parseToken(token);
+ 
+            // 判断签名信息
+            if (null != claims && !claims.isEmpty() && !jwtUtil.isExpired(claims.getExpiration())) {
+                // 获取签名用户信息
+                String userId = claims.getSubject();
+                //获取相应的用户信息,可以在过滤器中先行获取,也可以先保存用户ID,在需要时进行获取
+                // User user = usersService.findById(Long.valueOf(userId));
+ 
+                request.setAttribute(SECURITY_USER, userId);
+                Cookie jwtCookie = new Cookie(jwtUtil.getHeader(), URLEncoder.encode(token, "UTF-8"));
+                jwtCookie.setHttpOnly(true);
+                jwtCookie.setPath("/");
+                response.addCookie(jwtCookie);
+ 
+                response.addHeader(jwtUtil.getHeader(), token);
+            } else {
+                request.setAttribute("exceptionCode", 10001);
+                request.setAttribute("exceptionMessage", jwtUtil.getHeader() + " 无效,请重新登录!");
+                request.getRequestDispatcher("/exception/authentication").forward(request, response);
+                // throw new AuthenticationException(SystemParameters.AUTHENTICATION_FAILED.getIndex(), jwtUtil.getHeader() + " 无效,请重新登录!");
+                return;
+            }
+        }
+ 
+        filterChain.doFilter(request, response);
+    }
+ 
+    /**
+     * 获取token
+     *
+     * @param request
+     * @return 返回token
+     */
+    private String getToken(HttpServletRequest request) {
+ 
+        //先从header中获取token
+        String token = request.getHeader(jwtUtil.getHeader());
+ 
+        //再从cookie中获取
+        if (token.isBlank()) {
+            try {
+                Cookie[] cookies = request.getCookies();
+                if (cookies != null) {
+                    for (Cookie cookie : cookies) {
+                        if (jwtUtil.getHeader().equals(cookie.getName())) {
+                            token = URLDecoder.decode(cookie.getValue(), "UTF-8");
+                        }
+                    }
+                }
+            } catch (Exception e) {
+            }
+        }
+ 
+        return token;
+    }
+ 
+    /**
+     * 根据url判断是否需要校验,false需要校验
+     *
+     * @param url
+     * @return 是否需要校验
+     */
+    private boolean isNotCheck(String url) {
+ 
+        // 处理路径以"/" 结尾的"/"
+        url = url.endsWith("/") ? url.substring(0, url.lastIndexOf("/")) : url;
+ 
+        for (String path : NOT_CHECK_URL) {
+            // 判断是否以 "/**" 结尾
+            if (path.endsWith("/**")) {
+                return url.startsWith(path.substring(0, path.lastIndexOf("/") + 1))
+                        || url.equals(path.substring(0, path.lastIndexOf("/")));
+            }
+ 
+            // 判断url == path
+            if (url.equals(path)) {
+                return true;
+            }
+        }
+ 
+        return false;
+    }
+ 
+}

+ 122 - 0
src/main/java/com/kxs/adminap/util/JwtUtil.java

@@ -0,0 +1,122 @@
+package com.kxs.adminap.util;
+
+ 
+import io.jsonwebtoken.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+ 
+import javax.crypto.spec.SecretKeySpec;
+import java.security.Key;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+ 
+/**
+ * JWT 工具类
+ * <p>
+ * jwt含有三部分:头部(header)、载荷(payload)、签证(signature)
+ * (1)头部一般有两部分信息:声明类型、声明加密的算法(通常使用HMAC SHA256)
+ * (2)载荷该部分一般存放一些有效的信息。jwt的标准定义包含五个字段:
+ * - iss:该JWT的签发者
+ * - sub: 该JWT所面向的用户
+ * - aud: 接收该JWT的一方
+ * - exp(expires): 什么时候过期,这里是一个Unix时间戳
+ * - iat(issued at): 在什么时候签发的
+ * (3)签证(signature) JWT最后一个部分。该部分是使用了HS256加密后的数据;包含三个部分
+ *
+ * @author kou
+ */
+@ConfigurationProperties(prefix = "custom.jwt")
+@Component
+public class JwtUtil {
+ 
+    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
+ 
+    // 秘钥
+    private String secret = "aHR0cHM6Ly9teS5vc2NoaW5hLm5ldC91LzM2ODE4Njg=";
+ 
+    // 有效时间
+    private Long expire = 5184000L;
+ 
+    // 用户凭证
+    private String header = "token";
+ 
+    // 签发者
+    private String issuer = "kxs";
+
+    public String getHeader() {
+        return this.header;
+    }
+ 
+    /**
+     * 生成token签名
+     *
+     * @param subject
+     * @return
+     */
+    public String generateToken(String subject) {
+ 
+        final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+ 
+        Date now = new Date();
+        // 过期时间
+        Date expireDate = new Date(now.getTime() + expire * 1000);
+ 
+        //Create the Signature SecretKey
+        final byte[] apiKeySecretBytes = Base64.getEncoder().encode(secret.getBytes());
+        final Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
+ 
+        final Map<String, Object> headerMap = new HashMap<>();
+        headerMap.put("alg", "HS256");
+        headerMap.put("typ", "JWT");
+ 
+        //add JWT Parameters
+        final JwtBuilder builder = Jwts.builder()
+                .setHeaderParams(headerMap)
+                .setSubject(subject)
+                .setIssuedAt(now)
+                .setExpiration(expireDate)
+                .setIssuer(issuer)
+                .signWith(signatureAlgorithm, signingKey);
+ 
+        logger.info("JWT[" + builder.compact() + "]");
+        return builder.compact();
+ 
+    }
+ 
+    /**
+     * 解析token
+     *
+     * @param token token
+     * @return
+     */
+    public Claims parseToken(String token) {
+ 
+        Claims claims = null;
+        try {
+            final byte[] apiKeySecretBytes = Base64.getEncoder().encode(secret.getBytes());
+            claims = Jwts.parser().setSigningKey(apiKeySecretBytes).parseClaimsJws(token).getBody();
+            logger.info("Parse JWT token by: ID: {}, Subject: {}, Issuer: {}, Expiration: {}", claims.getId(), claims.getSubject(), claims.getIssuer(), claims.getExpiration());
+        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException
+                | IllegalArgumentException e) {
+            logger.info("Parse JWT errror " + e.getMessage());
+            return null;
+        }
+        return claims;
+    }
+ 
+    /**
+     * 判断token是否过期
+     *
+     * @param expiration
+     * @return
+     */
+    public boolean isExpired(Date expiration) {
+ 
+        return expiration.before(new Date());
+    }
+ 
+}