最近使用springcloud gateway时,遇到了一个跨域的问题,开始的时候还觉得是个soeasy的问题,加个跨域配置不就完事了么,可是,百度找了三种跨域配置方式,怎么就是不生效,一个跨域问题搞了将近两天才解决,麻了麻了~
解决方案
yml配置文件
spring:
application:
name: gateway
cloud:
# gateway 配置
gateway:
filter:
remove-hop-by-hop:
headers:
# 以下是去掉网关默认去掉的请求响应头
- trailer
- te
- keep-alive
- transfer-encoding
- upgrade
- proxy-authenticate
- connection
- proxy-authorization
- x-application-context
# 以下是去掉服务层面定义的跨域
- access-control-allow-credentials
- access-control-allow-headers
- access-control-allow-methods
- access-control-allow-origin
- access-control-max-age
- vary
globalcors:
corsConfigurations:
'[/**]':
allowCredentials: true
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods: "*"
maxAge: 3628800
这就OK了吗?不不不
通过百度搜索,文章里有很多配置跨域的方式,上面的yml配置也好,自己加配置类也好,我亲身实验的效果就是不生效。
网关拦截器的坑
使用gateway网关,有一个重要的点就是想要去进行统一拦截,如登录拦截啊、授权认证啊等等,这些都需要在网关的拦截器里做处理
所以有了下图的自定义拦截器
拦截器里的return的值需要处理跨域,不然配置文件里的跨域配置就会不生效,前后对比如下:
代码
最后把我自定义的网关拦截器代码贴一下,没有的逻辑可以自行删除,我就直接贴所有的代码了。
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.*;
@Slf4j
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
private static final String TRACE_ID = "traceId";
private static final AntPathMatcher matcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//对请求对象request进行增强
ServerHttpRequest req = request.mutate().headers(httpHeaders -> {
//httpHeaders 封装了所有的请求头
String traceId = UUID.randomUUID().toString(true);
MDC.put(TRACE_ID, traceId);
httpHeaders.set(TRACE_ID, traceId);
}).build();
//设置增强的request到exchange对象中
exchange.mutate().request(req);
String url = request.getURI().getPath();
log.info("接收到请求:{}", url);
// 不需要拦截的接口直接放行
if (needLogin(request.getPath().toString())) {
log.info("不拦截放行");
return getVoidMono(exchange, chain);
}
// 授权验证
if (!this.auth(exchange)) {
return this.responseBody(exchange, 406, "请先登录");
}
log.info("认证成功,放行");
return getVoidMono(exchange, chain);
}
private Mono<Void> getVoidMono(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.defer(() -> {
exchange.getResponse().getHeaders().entrySet().stream()
.filter(kv -> kv.getValue() != null && kv.getValue().size() > 1)
.filter(kv -> kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS))
.forEach(kv -> kv.setValue(Collections.singletonList(kv.getValue().get(0))));
return chain.filter(exchange);
}));
}
/**
* 是否需要登录
*
* @param uri 请求URI
* @return boolean
*/
public static boolean needLogin(String uri) {
// test
List<String> uriList = new ArrayList<>();
uriList.add("/user/cloud/login");
uriList.add("/user/token");
uriList.add("/user/verify");
for (String pattern : uriList) {
if (matcher.match(pattern, uri)) {
// 不需要拦截
return true;
}
}
return false;
}
/**
* 认证拦截
*/
private boolean auth(ServerWebExchange exchange) {
String token = this.getToken(exchange.getRequest());
log.info("token:{}", token);
if (StrUtil.isBlank(token)) {
return false;
}
JSONObject userInfo = getUserInfo(token);
return !ObjectUtil.isNull(userInfo);
}
private JSONObject getUserInfo(String token) {
JSONObject jsonObject;
String tokenNew = token.substring(7);
String ss = JWT.decode(tokenNew).getPayload();
Base64.Decoder decoder = Base64.getDecoder();
jsonObject = JSON.parseObject(new String(decoder.decode(ss)));
return jsonObject;
}
/**
* 获取token
*/
public String getToken(ServerHttpRequest request) {
String token = request.getHeaders().getFirst("Authorization");
if (StrUtil.isBlank(token)) {
return request.getQueryParams().getFirst("Authorization");
}
return token;
}
/**
* 设置响应体
**/
public Mono<Void> responseBody(ServerWebExchange exchange, Integer code, String msg) {
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("code", code);
hashMap.put("msg", msg);
String message = JSON.toJSONString(hashMap);
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
return this.responseHeader(exchange).getResponse()
.writeWith(Flux.just(exchange.getResponse().bufferFactory().wrap(bytes)));
}
/**
* 设置响应体的请求头
*/
public ServerWebExchange responseHeader(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json");
return exchange.mutate().response(response).build();
}
@Override
public int getOrder() {
return 0;
}
}