测试网关过滤请求

master
邱飞云 4 years ago
parent 66b5d4d3f2
commit b8d0d44e92
  1. 12
      .idea/compiler.xml
  2. 2
      .idea/dqFinancial.iml
  3. 7
      .idea/encodings.xml
  4. 20
      .idea/jarRepositories.xml
  5. 20
      dq-govern-gateway/pom.xml
  6. 64
      dq-govern-gateway/src/main/java/com/daqing/financial/gateway/SpringContextHolder.java
  7. 114
      dq-govern-gateway/src/main/java/com/daqing/financial/gateway/config/ApiGlobalFilter.java
  8. 70
      dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/JwtUtil.java
  9. 24
      dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcException.java
  10. 50
      dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/OdcProperties.java
  11. 298
      dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/RedisUtil.java
  12. 62
      dq-govern-gateway/src/main/java/com/daqing/financial/gateway/util/ResultCodeEnum.java
  13. 1
      dq-govern-gateway/src/main/resources/jwt.properties

@ -6,17 +6,17 @@
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="dq-financial-crms-auth" />
<module name="dq-financial-hrms" />
<module name="dq-financial-crms" />
<module name="dq-framework-utils" />
<module name="dq-financial-api" />
<module name="dq-framework-model" />
<module name="dq-financial-hrms-auth" />
<module name="dq-govern-gateway" />
<module name="dq-financial-hrms" />
<module name="dq-framework-common" />
<module name="dq-framework-utils" />
<module name="dq-financial-workflow" />
<module name="dq-financial-api" />
<module name="dq-govern-gateway" />
<module name="dq-financial-crms-auth" />
<module name="dq-financial-guarantee" />
<module name="dq-financial-hrms-auth" />
</profile>
</annotationProcessing>
</component>

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />

@ -3,11 +3,18 @@
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/dq-financial-crms" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-crms-auth" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-crms-auth/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-crms/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-guarantee" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-guarantee/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-hrms" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-hrms-auth" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-hrms-auth/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-hrms/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-workflow" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-financial-workflow/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-govern-gateway" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dq-govern-gateway/src/main/java" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

@ -30,7 +30,27 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

@ -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> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
public static <T> T getBean(String name, Class<T> 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();
}
}

@ -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<String> ignoreUrl;
/**
* 拦截所有的请求头
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> 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;
}
}

@ -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<String> tokenSet = RedisUtil.keys(getRedisKey(userId, "*"));
for (String key : tokenSet) {
RedisUtil.del(key);
}
}
}

@ -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;
}
}

@ -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;
}
}

@ -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<String> 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<String> 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<String, String> 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<String, String> op = stringRedisTemplate.opsForValue();
op.increment(key, num);
}
/**
* 获取key的值
*/
public static String get(String key) {
ValueOperations<String, String> op = stringRedisTemplate.opsForValue();
return op.get(key);
}
/////// list操作
/**
* 插入到表头
*/
public static void lPush(String key, String... values) {
ListOperations<String, String> listOp = stringRedisTemplate.opsForList();
listOp.leftPushAll(key, values);
}
/**
* 移除第一个
*/
public static String rPop(String key) {
ListOperations<String, String> listOp = stringRedisTemplate.opsForList();
return listOp.rightPop(key);
}
/**
* 获取list所有
*
* @param key
* @param start
* @param end
* @return
*/
public static List<String> lRange(String key, int start, int end) {
ListOperations<String, String> opsForList = stringRedisTemplate.opsForList();
return opsForList.range(key, start, end);
}
/////// hash
/*
* public static void hset(String key,String hashKey,String value){
* HashOperations<String,String,String> opsForHash =
* stringRedisTemplate.opsForHash(); opsForHash.put(key, hashKey, value); }
*/
/////// set
/////// sorted set
/**
* 存放list
* @param key
* @param list
*/
public static void setList(String key, List<String> list){
ListOperations<String, String> opsForList = stringRedisTemplate.opsForList();
opsForList.leftPushAll(key, list);
}
/**
* HashGet
* @param key 不能为null
* @param item 不能为null
* @return
*/
public static Object hGet(String key,String item){
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
return mapOp.get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key
* @return 对应的多个键值
*/
public static Map<Object,Object> hmGet(String key){
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
return mapOp.entries(key);
}
/**
* HashSet
* @param key
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public static boolean hmSet(String key, Map<String,Object> map){
try {
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
mapOp.putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key
* @param map 对应多个键值
* @param time 时间()
* @return true成功 false失败
*/
public static boolean hmset(String key, Map<String,Object> map, long time){
try {
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
mapOp.putAll(key, map);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key
* @param item
* @param value
* @return true 成功 false失败
*/
public static boolean hset(String key,String item,Object value) {
try {
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
mapOp.put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key
* @param item
* @param value
* @param time 时间() 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public static boolean hset(String key,String item,Object value,long time) {
try {
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
mapOp.put(key, item, value);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 不能为null
* @param item 可以使多个 不能为null
*/
public static void hdel(String key, Object... item){
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
mapOp.delete(key,item);
}
/**
* 判断hash表中是否有该项的值
* @param key 不能为null
* @param item 不能为null
* @return true 存在 false不存在
*/
public static boolean hHasKey(String key, String item){
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
return mapOp.hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key
* @param item
* @param by 要增加几(大于0)
* @return
*/
public static double hincr(String key, String item,double by){
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
return mapOp.increment(key, item, by);
}
/**
* hash递减
* @param key
* @param item
* @param by 要减少记(小于0)
* @return
*/
public static double hdecr(String key, String item,double by){
HashOperations<String, Object, Object> mapOp = stringRedisTemplate.opsForHash();
return mapOp.increment(key, item,-by);
}
}

@ -0,0 +1,62 @@
package com.daqing.financial.gateway.util;
public enum ResultCodeEnum {
SUCCESS(0, "请求成功"),
BAD_REQUEST(400, "错误请求"),
UN_AUTHORIZATION(401, "未授权"),
SERVER_EXCEPTION(500, "服务器异常"),
ACCOUNT_NOT_EXIST(10000, "账号不存在"),
ACCOUNT_NOT_BIND(10001, "账号未绑定钱包"),
ACCOUNT_NOT_ACTIVE(10002, "账号未激活"),
ACCOUNT_EXIST(10003, "账号已存在,请登录"),
COIN_ADDRESS_IS_NOT_EXIST(10004, "该币种地址不存在"),
COIN_NAME_IS_NOT_EXIST(10005, "该币种不存在"),
WALLET_BAD_PARAM(10100, "非法参数,禁止访问他人账号"),
WALLET_NOT_ENOUGH(10101, "可用余额不足"),
SUB_ACCOUNT_NOT_ALLOW_WITHDRAW(10102, "子账号不允许提币"),
USER_NAME_ALREADY_EXISTS(10103,"用户名已存在"),
USER_EMAIL_ALREADY_EXISTS(10104,"邮箱已存在"),
INVITATION_CODE_NOT_EXIST(10105,"邀请码不存在"),
DEVICE_EXIST(10106,"设备号相同"),
LOGINPASSWORD_ERROR(10107,"登录密码错误"),
ACCOUNT_DISABLED(10108,"账号已禁用"),
USER_NAME_IS_ENABLE(10109,"用户名不存在"),
USER_IS_ACTIVE(10111,"该用户已激活"),
PAY_PASSWORD_IS_ERROR(10112,"支付密码错误"),
USER_IS_ACTIVE_MAX(10113,"您的激活次数已上线"),
WITHDRAW_IS_ENABLE(10114,"该币种已关闭提币通道"),
WITHDRAW_IS_NOT_BEGIN_TIME(10115,"未到提币时间"),
WITHDRAW_IS_SUPER_END_TIME(10116,"提币时间已过"),
WITHDRAW_MIN_ERROR(10117,"不能小于最小提币金额"),
WITHDRAW_MAX_ERROR(10118,"不能大于最大提币金额"),
WITHDRAW_MAX_TODAY(10120,"当日提币金额已上限"),
TRANSFER_IS_ENABLE(10121,"该币种已关闭转账通道"),
TRANSFER_MIN_ERROR(10122,"不能小于最小转账金额"),
TRANSFER_MAX_ERROR(10123,"不能大于最大转账金额"),
TRANSFER_MAX_TODAY(10124,"当日转账金额已上限"),
USERNAME_AND_EMAIL_ERROR(10125,"用户名和邮箱不匹配"),
EMAIL_CODE_IS_ERROR(10126,"验证码错误"),
PAY_PASSWORD_IS_NULL(10127,"请先设置支付密码"),
CHAIN_IS_ERROR(10128,"链上异常请联系管理员");
private final int code;
private final String remark;
ResultCodeEnum(int code, String remark) {
this.code = code;
this.remark = remark;
}
public int getCode() {
return code;
}
public String getRemark() {
return remark;
}
}

@ -0,0 +1 @@
jwt.ignoreUrlList=/route-api/login,/route-api/refresh
Loading…
Cancel
Save