天天看点

springcloud全局过滤_Spring Cloud Gateway之全局过滤器在工作中的使用场景

一、使用注意事项

1、全局过滤器作用于所有的路由,不需要单独配置。

2、通过@Order来指定执行的顺序,数字越小,优先级越高。

二、默认全局拦截器的整体架构

springcloud全局过滤_Spring Cloud Gateway之全局过滤器在工作中的使用场景

1、校验token

@Slf4j

public class AuthenFilter implements GlobalFilter, Ordered {

@Resource

private IFeignClient feignClient;

private static final String GATEWAY_ROUTE_BEAN = "org.springframework.cloud.gateway.support" +

".ServerWebExchangeUtils.gatewayRoute";

private static final String BEAR_HEAD = "bear";

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

ServerHttpRequest request = exchange.getRequest();

String requestUrl = request.getPath().pathWithinApplication().value();

//判断过滤器是否执行

if (!RequestUtils.isFilter(requestUrl)) {

//该请求转发,因为访问/leap,需要展示登录页

if (requestUrl.equals("/leap/") || requestUrl.equals("/leap")) {

ServerHttpRequest authErrorReq = request.mutate()

.path("/index.html")

.build();

ServerWebExchange indexExchange = exchange.mutate().request(authErrorReq).build();

return chain.filter(indexExchange);

}

ResEntity res;

ServerHttpResponse response = exchange.getResponse();

Map cookiesInfo = getCookiesInfo(request);

String account = cookiesInfo.get("account");

String token = cookiesInfo.get("token");

//校验token

res = feignClient.verifyToken(token);

log.info("校验token:{}", res.getMsg());

//如果token失效清除cookies ,让用户解锁或者重新登录

if (200 == res.getHttpStatus()) {

response.addCookie(ResponseCookie.from("token", token).path("/").build());

response.addCookie(ResponseCookie.from("userAccount", account).path("/").build());

} else {

log.error("网关过滤器AuthenFilter:{}", res.getMsg());

//token失效,通过cookies失效告知前端,重新解锁

response.addCookie(ResponseCookie.from("token", token).path("/").maxAge(Duration.ofSeconds(0L)).build());

response.addCookie(ResponseCookie.from("userAccount", account).path("/").maxAge(Duration.ofSeconds(0L)).build());

ServerHttpRequest authErrorReq = request.mutate()

.path("/index.html")

.build();

ServerWebExchange indexExchange = exchange.mutate().request(authErrorReq).build();

return chain.filter(indexExchange);

}

final ResEntity resEntity = feignClient.findUserByAccount(account);

//判断用户是否存在

if (200 != resEntity.getHttpStatus() || null == resEntity.getData()) {

throw new BusinessException(ExceptionEnum.AUTH_USER_NOT_FOUND, account);

}

//设置请求头信息

exchange = setHeader(exchange, resEntity);

}

return chain.filter(exchange);

}

private Map getCookiesInfo(ServerHttpRequest request) {

Map map = new HashMap<>();

Set>> cookies = request.getCookies().entrySet();

for (Map.Entry> entry : cookies) {

if ("userAccount".equals(entry.getKey())) {

map.put("account", entry.getValue().get(0).getValue());

}

if ("token".equals(entry.getKey())) {

map.put("token", entry.getValue().get(0).getValue());

}

}

return map;

}

private ServerWebExchange setHeader(ServerWebExchange exchange, ResEntity resEntity) {

final HashMap claims = Maps.newHashMap();

claims.put("jwt", UUID.randomUUID().toString().replaceAll("-", ""));

ServerHttpRequest userInfo = null;

try {

String user = URLEncoder.encode(JSON.toJSONString(resEntity.getData()), "UTF-8");

userInfo = exchange.getRequest().mutate()

.header(BEAR_HEAD, JwtHelper.genToken(claims))

.header("userInfo", user)

.build();

exchange = exchange.mutate().request(userInfo).build();

//feign拦截器的线程局部变量

FeignRequestInterceptor.setContext(user);

} catch (UnsupportedEncodingException e) {

throw new BusinessException(ExceptionEnum.COMMON_ENCODE_EXCEPTION, e, "网关拦截器");

}

return exchange;

}

@Override

public int getOrder() {

return 4;

}

}

@Slf4j

@Configuration

public class FeignRequestInterceptor implements RequestInterceptor {

private static final String BEAR_HEAD = "bear";

private static final String USER_INFO_HEAD = "hd-user";

private static final ThreadLocal USER_INFO = new ThreadLocal<>();

public static void setContext(String userInfo) {

USER_INFO.set(userInfo);

}

public static void clean() {

USER_INFO.remove();

}

@Override

public void apply(RequestTemplate requestTemplate) {

final HashMap claims = Maps.newHashMap();

claims.put("jwt", UUID.randomUUID().toString().replaceAll("-", ""));

requestTemplate.header(BEAR_HEAD, JwtHelper.genToken(claims));

if (null != USER_INFO.get()) {

requestTemplate.header(USER_INFO_HEAD, USER_INFO.get());

}

}

}

2、更改负载均衡后的url

@Slf4j

public class VersionControlFilter implements GlobalFilter, Ordered {

private static final int VERSION_CONTROL_FILTER_ORDER = 101001;

private static final String HTTP_PREFIX = "http://";

private static final String SLASH = "/";

private static final String STAR = "*";

private static final String COLON = ":";

private final RedisUtil redisUtil;

private final ValueAnnotationUtils valueAnnotationUtils;

public VersionControlFilter(RedisUtil redisUtil, ValueAnnotationUtils valueAnnotationUtils) {

this.redisUtil = redisUtil;

this.valueAnnotationUtils = valueAnnotationUtils;

}

@Override

public int getOrder() {

return VERSION_CONTROL_FILTER_ORDER;

}

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

ServerHttpRequest request = exchange.getRequest();

//获取远程ip地址

InetSocketAddress inetSocketAddress = request.getRemoteAddress();

if (null == inetSocketAddress) {

return chain.filter(exchange);

}

String clientIp = inetSocketAddress.getAddress().getHostAddress();

//获取path

URI uri = request.getURI();

String path = uri.getPath();

//只有非白名单路径才版本控住

String requestPath = RequestUtils.getCurrentRequest(request);

if (!RequestUtils.isFilter(requestPath)) {

//判断redis中是否存在key

boolean hasKey =

redisUtil.exists(valueAnnotationUtils.getVersionControl() + valueAnnotationUtils.getActiveEnv());

if (!hasKey) {

redisUtil.set(valueAnnotationUtils.getVersionControl() + valueAnnotationUtils.getActiveEnv(),

JSON.toJSONString(new HashMap<>()));

}

//先取出原本的key

Map preMap =

JSON.parseObject(redisUtil.get(valueAnnotationUtils.getVersionControl() + valueAnnotationUtils.getActiveEnv()),

HashMap.class);

//正常url 例如 /platform/user/me

String clientAddress = clientIp + path;

String serviceIp = preMap.get(clientAddress);

//非正常,匹配正则表达式 例如 /platform/user

private String getRegx(String clientIp, String path, Map map) {

String[] paths = path.split(SLASH);

if (1 > paths.length) {

log.error(" VersionControlFilter 请求路径:{}", path);

throw new BusinessException(" VersionControlFilter 请求路径不合法");

}

for (int i = 0; i < paths.length; i++) {

StringBuilder clientAddress = new StringBuilder(clientIp);

String item = paths[i];

if (StringUtils.isBlank(item)) {

continue;

}

for (int j = 0; j <= i; j++) {

if (StringUtils.isBlank(paths[j])) {

continue;

}

if (j == paths.length - 1) {

clientAddress.append(SLASH + STAR);

} else {

clientAddress.append(SLASH).append(paths[j]);

}

}

if (i != paths.length - 1) {

clientAddress.append(SLASH + STAR + STAR);

}

String serverIp = map.get(clientAddress.toString());

if (StringUtils.isNotBlank(serverIp)) {

return serverIp;

}

}

return null;

}

}

注意点:如果开启熔断,要注意熔断的线程隔离级别,否则Feign的请求拦截器在头中放入的数据,下游无法拿到。