From b8d0d44e92b41a653553724e4735661086fcb40f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=82=B1=E9=A3=9E=E4=BA=91?= <1941783199@qq.com>
Date: Fri, 25 Sep 2020 17:14:25 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=BD=91=E5=85=B3=E8=BF=87?=
=?UTF-8?q?=E6=BB=A4=E8=AF=B7=E6=B1=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/compiler.xml | 12 +-
.idea/dqFinancial.iml | 2 +
.idea/encodings.xml | 7 +
.idea/jarRepositories.xml | 20 ++
dq-govern-gateway/pom.xml | 20 ++
.../gateway/SpringContextHolder.java | 64 ++++
.../gateway/config/ApiGlobalFilter.java | 114 +++++++
.../financial/gateway/util/JwtUtil.java | 70 ++++
.../financial/gateway/util/OdcException.java | 24 ++
.../financial/gateway/util/OdcProperties.java | 50 +++
.../financial/gateway/util/RedisUtil.java | 298 ++++++++++++++++++
.../gateway/util/ResultCodeEnum.java | 62 ++++
.../src/main/resources/jwt.properties | 1 +
13 files changed, 738 insertions(+), 6 deletions(-)
create mode 100644 .idea/dqFinancial.iml
create mode 100644 .idea/jarRepositories.xml
create mode 100644 dq-govern-gateway/src/main/java/com/daqing/financial/gateway/SpringContextHolder.java
create mode 100644 dq-govern-gateway/src/main/java/com/daqing/financial/gateway/config/ApiGlobalFilter.java
create mode 100644 dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/JwtUtil.java
create mode 100644 dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcException.java
create mode 100644 dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcProperties.java
create mode 100644 dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/RedisUtil.java
create mode 100644 dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/ResultCodeEnum.java
create mode 100644 dq-govern-gateway/src/main/resources/jwt.properties
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index e24ccc08..00c72ccf 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -6,17 +6,17 @@
-
-
+
+
-
-
+
-
-
+
+
+
diff --git a/.idea/dqFinancial.iml b/.idea/dqFinancial.iml
new file mode 100644
index 00000000..78b2cc53
--- /dev/null
+++ b/.idea/dqFinancial.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 30d52777..f46cbaa0 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -3,11 +3,18 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 00000000..712ab9d9
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dq-govern-gateway/pom.xml b/dq-govern-gateway/pom.xml
index 70da4975..8eda2099 100644
--- a/dq-govern-gateway/pom.xml
+++ b/dq-govern-gateway/pom.xml
@@ -30,7 +30,27 @@
org.springframework.cloud
spring-cloud-starter-gateway
+
+ commons-lang
+ commons-lang
+ 2.6
+
+
+ org.apache.commons
+ commons-lang3
+ 3.8.1
+
+
+
+ com.auth0
+ java-jwt
+ 3.3.0
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
org.springframework.boot
spring-boot-starter-test
diff --git a/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/SpringContextHolder.java b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/SpringContextHolder.java
new file mode 100644
index 00000000..cd8a20a0
--- /dev/null
+++ b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/SpringContextHolder.java
@@ -0,0 +1,64 @@
+package com.daqing.financial.gateway;
+
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+@Component
+@Lazy(false)
+public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
+
+ private static ApplicationContext applicationContext = null;
+
+
+ /**
+ * 取得存储在静态变量中的ApplicationContext.
+ */
+ public static ApplicationContext getApplicationContext() {
+ return applicationContext;
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ @SuppressWarnings("unchecked")
+ public static T getBean(String name) {
+ return (T) applicationContext.getBean(name);
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ public static T getBean(Class requiredType) {
+ return applicationContext.getBean(requiredType);
+ }
+
+ public static T getBean(String name, Class clazz) {
+ return getApplicationContext().getBean(name, clazz);
+ }
+
+ /**
+ * 清除SpringContextHolder中的ApplicationContext为Null.
+ */
+ public static void clearHolder() {
+ applicationContext = null;
+ }
+
+ /**
+ * 实现ApplicationContextAware接口, 注入Context到静态变量中.
+ */
+ @Override
+ public void setApplicationContext(ApplicationContext appContext) {
+ applicationContext = appContext;
+ }
+
+ /**
+ * 实现DisposableBean接口, 在Context关闭时清理静态变量.
+ */
+ @Override
+ public void destroy() {
+ SpringContextHolder.clearHolder();
+ }
+}
\ No newline at end of file
diff --git a/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/config/ApiGlobalFilter.java b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/config/ApiGlobalFilter.java
new file mode 100644
index 00000000..ed0a648f
--- /dev/null
+++ b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/config/ApiGlobalFilter.java
@@ -0,0 +1,114 @@
+package com.daqing.financial.gateway.config;
+
+import com.alibaba.fastjson.JSONObject;
+import com.daqing.financial.gateway.util.JwtUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
+import java.util.List;
+
+@Component
+@PropertySource(value = "classpath:jwt.properties")
+public class ApiGlobalFilter implements GlobalFilter, Ordered {
+
+ /**
+ * 不进行token校验的请求地址
+ */
+ @Value("#{'${jwt.ignoreUrlList}'.split(',')}")
+ public List ignoreUrl;
+
+ /**
+ * 拦截所有的请求头
+ * @param exchange
+ * @param chain
+ * @return
+ */
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ String requestUrl = exchange.getRequest().getPath().toString();
+ boolean status = CollectionUtils.contains((Enumeration>) ignoreUrl, requestUrl);
+ if (!status){
+ String token = exchange.getRequest().getHeaders().getFirst("token");
+ //type用于区分不同的端,在做校验token时需要
+ String type= exchange.getRequest().getHeaders().getFirst("type");
+ ServerHttpResponse response = exchange.getResponse();
+ //没有数据
+ if (StringUtils.isBlank(token) || StringUtils.isBlank(type)) {
+ JSONObject message = new JSONObject();
+ message.put("code", "");
+ message.put("message", "鉴权失败,无token或类型");
+ byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);
+ DataBuffer buffer = response.bufferFactory().wrap(bits);
+ response.setStatusCode(HttpStatus.UNAUTHORIZED);
+ response.getHeaders().add("Content-Type", "text/json;charset=UTF-8");
+ return response.writeWith(Mono.just(buffer));
+ //有数据
+ }else {
+ String prefix = this.getPrefix(type);
+ //校验token
+ Long userId = verifyJWT(token ,prefix);
+ if (userId == null){
+ JSONObject message = new JSONObject();
+ message.put("message", "token错误");
+ message.put("code", "");
+ byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);
+ DataBuffer buffer = response.bufferFactory().wrap(bits);
+ response.setStatusCode(HttpStatus.UNAUTHORIZED);
+ response.getHeaders().add("Content-Type", "text/json;charset=UTF-8");
+ return response.writeWith(Mono.just(buffer));
+ }
+ //将现在的request,添加当前身份
+ ServerHttpRequest mutableReq = exchange.getRequest().mutate().header("Authorization-UserId", String.valueOf(userId)).build();
+ ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
+ return chain.filter(mutableExchange);
+ }
+ }
+ return chain.filter(exchange);
+ }
+
+ /**
+ * JWT验证
+ * @param token
+ * @return userPhone
+ */
+ private Long verifyJWT(String token, String prefix){
+ return JwtUtil.verifyToken(token);
+ }
+
+ /**
+ * 根据type获取前缀
+ * @param type
+ * @return
+ */
+ private String getPrefix(String type){
+ String prefix = null;
+ if ("1".equals(type)){
+ prefix = "OPERATE";
+ }else if ("2".equals(type)){
+ prefix = "USER";
+ }else if ("3".equals(type)){
+ prefix = "WX";
+ }
+ return prefix;
+ }
+
+ @Override
+ public int getOrder() {
+ return -200;
+ }
+}
+
diff --git a/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/JwtUtil.java b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/JwtUtil.java
new file mode 100644
index 00000000..6e495502
--- /dev/null
+++ b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/JwtUtil.java
@@ -0,0 +1,70 @@
+package com.daqing.financial.gateway.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+
+import com.daqing.financial.gateway.SpringContextHolder;
+
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * @author zcw
+ * @version 1.0
+ * @date 2019/11/23 11:06
+ * @description jwt工具类
+ */
+public class JwtUtil {
+
+ private final static Algorithm algorithm = SpringContextHolder.getBean("algorithm", Algorithm.class);
+
+ private final static OdcProperties properties = SpringContextHolder.getBean("odcProperties", OdcProperties.class);
+
+ /**
+ * 创建token
+ *
+ * @param userId;
+ * @param timeout; 单位是秒
+ */
+ public static String createJwtToken(Long userId, long timeout) {
+ return JWT.create()
+ .withClaim("member", userId)
+ .withExpiresAt(new Date(System.currentTimeMillis() + timeout * 1000))
+ .sign(algorithm);
+ }
+
+ /**
+ * token正确且有效,则返回userId
+ */
+ public static Long verifyToken(String token) {
+ try {
+ String noBearerToken = token.replaceFirst("Bearer ", "");
+ Long userId = JWT.require(algorithm)
+ .build()
+ .verify(noBearerToken)
+ .getClaim("member")
+ .asLong();
+ if (RedisUtil.get(getRedisKey(userId, noBearerToken)) != null) {
+ return userId;
+ }
+ } catch (Exception e) {
+ throw new OdcException(ResultCodeEnum.UN_AUTHORIZATION);
+ }
+ throw new OdcException(ResultCodeEnum.UN_AUTHORIZATION);
+ }
+
+ public static String getRedisKey(Long userId, String token) {
+ return String.format(properties.getConfig().getTokenRedisKeyFormat(), userId, token);
+ }
+
+ public static void putTokenToRedis(Long userId, String token) {
+ RedisUtil.setEx(getRedisKey(userId, token), "nothing", properties.getConfig().getTokenExpireSeconds());
+ }
+
+ public static void removeTokenByUserId(Long userId) {
+ Set tokenSet = RedisUtil.keys(getRedisKey(userId, "*"));
+ for (String key : tokenSet) {
+ RedisUtil.del(key);
+ }
+ }
+}
diff --git a/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcException.java b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcException.java
new file mode 100644
index 00000000..96b11e1d
--- /dev/null
+++ b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcException.java
@@ -0,0 +1,24 @@
+package com.daqing.financial.gateway.util;
+
+import lombok.Data;
+
+@Data
+public class OdcException extends RuntimeException {
+
+ private int code;
+
+ private ResultCodeEnum resultCodeEnum;
+
+ public OdcException(ResultCodeEnum codeEnum) {
+ super(codeEnum.getRemark());
+ code = codeEnum.getCode();
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public ResultCodeEnum getResultCodeEnum() {
+ return resultCodeEnum;
+ }
+}
\ No newline at end of file
diff --git a/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcProperties.java b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcProperties.java
new file mode 100644
index 00000000..2d8890f9
--- /dev/null
+++ b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcProperties.java
@@ -0,0 +1,50 @@
+package com.daqing.financial.gateway.util;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "com.odcchina", ignoreUnknownFields = true)
+public class OdcProperties {
+
+ private Oss oss = new Oss();
+
+ private Async async = new Async();
+
+ private Config config = new Config();
+
+ @Data
+ public static class Oss {
+ private String endpoint;
+ private String accessKeyId;
+ private String accessKeySecret;
+ private String bucketName;
+ private String prefixUrl;
+ }
+
+ @Data
+ public static class Async {
+ private int corePoolSize = 10;
+ private int maxPoolSize = 40;
+ private int queueCapacity = 20;
+ private int keepAliveSeconds = 30;
+ }
+
+ @Data
+ public static class Config {
+
+ private String tokenHeader = "Authorization";
+
+ //登录token,redis key格式
+ private String tokenRedisKeyFormat = "dcm:token:%d:%s";
+
+ //token有效期,10年
+ private long tokenExpireSeconds = 315360000L;
+
+ //限制只能登录一个
+ private boolean limitLoginOneOnly = true;
+
+ }
+}
diff --git a/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/RedisUtil.java b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/RedisUtil.java
new file mode 100644
index 00000000..d8fd451f
--- /dev/null
+++ b/dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/RedisUtil.java
@@ -0,0 +1,298 @@
+package com.daqing.financial.gateway.util;
+
+
+import com.daqing.financial.gateway.SpringContextHolder;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.ListOperations;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis工具类
+ */
+public class RedisUtil {
+
+ private final static StringRedisTemplate stringRedisTemplate = SpringContextHolder.getBean("stringRedisTemplate");
+
+ /**
+ * 匹配key
+ */
+ public static Set keys(String pattern) {
+ return stringRedisTemplate.keys(pattern);
+ }
+
+ /**
+ * 删除一个key
+ */
+ public static void del(String key) {
+ stringRedisTemplate.delete(key);
+ }
+
+ /**
+ * 批量删除key
+ */
+ public static void delByPattern(String pattern) {
+ Set keySet = keys(pattern);
+ stringRedisTemplate.delete(keySet);
+ }
+
+ /**
+ * 设置过期时间,单位为秒
+ */
+ public static boolean expire(String key, long seconds) {
+ return stringRedisTemplate.expire(key, seconds, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 获取自动过期时间
+ */
+ public static long ttl(String key) {
+ return stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 移除过期时间
+ */
+ public static boolean persist(String key) {
+ return stringRedisTemplate.persist(key);
+ }
+
+ /////// String 操作
+
+ /**
+ * 给key赋值
+ */
+ public static void set(String key, String value) {
+ ValueOperations op = stringRedisTemplate.opsForValue();
+ op.set(key, value);
+ }
+
+ /**
+ * 给key赋值,并设置过期时间,单位为秒
+ */
+ public static void setEx(String key, String value, long seconds) {
+ set(key, value);
+ expire(key, seconds);
+ }
+
+ /**
+ * 将key的值加num
+ */
+ public static void incrBy(String key, long num) {
+ ValueOperations op = stringRedisTemplate.opsForValue();
+ op.increment(key, num);
+ }
+
+ /**
+ * 获取key的值
+ */
+ public static String get(String key) {
+ ValueOperations op = stringRedisTemplate.opsForValue();
+ return op.get(key);
+ }
+
+ /////// list操作
+
+ /**
+ * 插入到表头
+ */
+ public static void lPush(String key, String... values) {
+ ListOperations listOp = stringRedisTemplate.opsForList();
+ listOp.leftPushAll(key, values);
+ }
+
+ /**
+ * 移除第一个
+ */
+ public static String rPop(String key) {
+ ListOperations listOp = stringRedisTemplate.opsForList();
+ return listOp.rightPop(key);
+ }
+
+ /**
+ * 获取list所有
+ *
+ * @param key
+ * @param start
+ * @param end
+ * @return
+ */
+ public static List lRange(String key, int start, int end) {
+ ListOperations opsForList = stringRedisTemplate.opsForList();
+ return opsForList.range(key, start, end);
+ }
+
+ /////// hash
+
+ /*
+ * public static void hset(String key,String hashKey,String value){
+ * HashOperations opsForHash =
+ * stringRedisTemplate.opsForHash(); opsForHash.put(key, hashKey, value); }
+ */
+ /////// set
+ /////// sorted set
+
+ /**
+ * 存放list
+ * @param key
+ * @param list
+ */
+ public static void setList(String key, List list){
+ ListOperations opsForList = stringRedisTemplate.opsForList();
+ opsForList.leftPushAll(key, list);
+ }
+
+ /**
+ * HashGet
+ * @param key 键 不能为null
+ * @param item 项 不能为null
+ * @return 值
+ */
+ public static Object hGet(String key,String item){
+ HashOperations mapOp = stringRedisTemplate.opsForHash();
+ return mapOp.get(key, item);
+ }
+
+ /**
+ * 获取hashKey对应的所有键值
+ * @param key 键
+ * @return 对应的多个键值
+ */
+ public static Map