首页 > 编程语言 > 详细

Spring Cloud Gateway之全局过滤器的作用

时间:2020-05-11 16:49:52      阅读:382      评论:0      收藏:0      [点我收藏+]

一、使用注意事项

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

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

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

 

 

 

技术分享图片

三、实战场景,列如,校验token、记录日志(可参考这边https://www.cnblogs.com/hyf-huangyongfei/p/12849406.html)、替换负载均衡以后的路由等等

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<Void> 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<String, String> 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);
}

/**
* 获取cookies中的数据
*
* @param request 请求对象
*/
private Map<String, String> getCookiesInfo(ServerHttpRequest request) {
Map<String, String> map = new HashMap<>();
Set<Map.Entry<String, List<HttpCookie>>> cookies = request.getCookies().entrySet();
for (Map.Entry<String, List<HttpCookie>> 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;

}

/**
* 设置头信息
* am exchange
*
* @param resEntity
* @return
* @throws UnsupportedEncodingException
*/
private ServerWebExchange setHeader(ServerWebExchange exchange, ResEntity resEntity) {
final HashMap<String, String> 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;
}

/**
* 过滤器的优先级
*
* @return
*/
@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<String> 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<String, String> 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<Void> 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<String, String> 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/* 或者 /platform/user/**
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
if (StringUtils.isBlank(serviceIp)) {
serviceIp = getRegx(clientIp, path, preMap);
}
if (StringUtils.isBlank(serviceIp)) {
return chain.filter(exchange);
}
//负载均衡以后的路由地址 例如:http://160.5.34.210:9772/platform/user/me
int port = requestUrl.getPort();

//替换到灰度的版本中
StringBuilder forwardAddress = new StringBuilder(HTTP_PREFIX);
forwardAddress.append(serviceIp)
.append(COLON)
.append(port)
.append(path);
//追加参数
if("GET".equalsIgnoreCase(request.getMethodValue())){
forwardAddress.append("?").append(uri.getQuery());
}
log.debug("VersionControlFilter 灰度转发的地址:{}", forwardAddress.toString());
try {
requestUrl = new URI(forwardAddress.toString());
} catch (URISyntaxException e) {
log.error("VersionControlFilter URI不合法:{}", requestUrl);
}

exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
}
return chain.filter(exchange);
}

/**
* 匹配正则规则
*
* @param clientIp 客户端ip
* @param path 路径
* @param map redis中的数据
* @return 服务器地址
*/
private String getRegx(String clientIp, String path, Map<String, String> 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的请求拦截器在头中放入的数据,下游无法拿到。

 

@Autowired
private ValueAnnotationUtils valueAnnotationUtils;

@Resource
private IPlatformFunctionClient pfc;

@Resource
private IPlatformDubboService platformDubboService;

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

private static final String GATEWAY_ROUTE_BEAN = "org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayRoute";

private static final String BEAR_HEAD = "bear";

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//判断过滤器是否执行
String requestUrl = RequestUtils.getCurrentRequest(request);
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<String, String> cookiesInfo = getCookiesInfo(request);
String account = cookiesInfo.get("account");
String token = cookiesInfo.get("token");

if (valueAnnotationUtils.getActiveEnv().equals("DEV")) {
//目前制定的方案是dev环境写死用户
account = "011336";
}
if (valueAnnotationUtils.getActiveEnv().equals("UAT") || valueAnnotationUtils.getActiveEnv().equals("PRD")) {
res = platformDubboService.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);
}
} else {
response.addCookie(ResponseCookie.from("userAccount", account).path("/").build());
}
response.addCookie(ResponseCookie.from("expiredDate",
String.valueOf(DateUtils.addMinutes(new Date(), 20).getTime())).maxAge(20 * 60).path(
"/").build());


final ResEntity resEntity = pfc.findUserByAccount(account);
//判断用户是否存在
if (200 != resEntity.getHttpStatus() || null == resEntity.getData()) {
throw new BusinessException(ExceptionEnum.AUTH_USER_NOT_FOUND, account);
}
exchange = setHeader(exchange, resEntity);

Route route = exchange.getAttribute(GATEWAY_ROUTE_BEAN);
if (!("MSFA".equals(route.getId()) || "ACTIV".equals(route.getId()))) {
//判断url是否授权
final ResEntity<Boolean> isOk = pfc.hasPermission(route.getId(), requestUrl, request.getMethodValue());
if (HttpStatus.OK.value() != isOk.getHttpStatus() || !isOk.getData()) {
throw new BusinessException(ExceptionEnum.AUTH_API_UNALLOW);
}
}

}

return chain.filter(exchange);
}

/**
* 获取cookies中的数据
*
* @param request 请求对象
*/
private Map<String, String> getCookiesInfo(ServerHttpRequest request) {
Map<String, String> map = new HashMap<>();
Set<Map.Entry<String, List<HttpCookie>>> cookies = request.getCookies().entrySet();
for (Map.Entry<String, List<HttpCookie>> 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;

}

/**
* 设置头信息
* am exchange
*
* @param resEntity
* @return
* @throws UnsupportedEncodingException
*/
private ServerWebExchange setHeader(ServerWebExchange exchange, ResEntity resEntity) {
final HashMap<String, String> 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(USER_INFO_HEAD, user)
.header(BEAR_HEAD, JwtHelper.genToken(claims))
.build();
exchange = exchange.mutate().request(userInfo).build();
FeignRequestInterceptor.setContext(user);
} catch (UnsupportedEncodingException e) {
throw new BusinessException(ExceptionEnum.COMMON_ENCODE_EXCEPTION, e, "网关拦截器");
}

return exchange;
}

/**
* 过滤器的优先级
*
* @return
*/
@Override
public int getOrder() {
return 4;
}

Spring Cloud Gateway之全局过滤器的作用

原文:https://www.cnblogs.com/hyf-huangyongfei/p/12869745.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!