最近由于用户数逐步上升和有了长连接的需求,再加上zuul停止更新了,干脆换成gateway。
zuul的原本配置:
配置文件:
server:
port: 1234
#服务的名称
spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 100MB
application:
name: zuul
#指定注册中心地址
eureka:
client:
service-url:
defaultZone: http://eureka:8761/eureka/
instance:
prefer-ip-address: true
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 30
#自定义路由映射
zuul:
routes:
servicea:
path: /servicea/**
serviceb:
path: /serviceb/**
host:
connect-timeout-millis: 60000
socket-timeout-millis: 60000
#统一前缀
prefix: /test
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
#不需要鉴权的地址开头
filter:
allowPaths: /allow1,/allow2
跨域处理:
@Component
public class FirstFilter extends ZuulFilter {
private Logger logger = LoggerFactory.getLogger(FirstFilter.class);
@Override
public String filterType() {
/*
pre:可以在请求被路由之前调用
route:在路由请求时候被调用
post:在route和error过滤器之后被调用
error:处理请求时发生错误时被调用
* */
// 前置过滤器
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
优先级为0,数字越大,优先级越低
return 0;
}
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
//只过滤OPTIONS 请求
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
return true;
}
return false;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletResponse response = ctx.getResponse();
HttpServletRequest request = ctx.getRequest();
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "authorization,content-type,accessToken,customerCode");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Expose-Headers", "X-forwared-port, X-forwarded-host");
response.setHeader("Vary", "Origin,Access-Control-Request-Method,Access-Control-Request-Headers");
response.setHeader("Access-Control-Max-Age", "1800");
//不再路由
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(204);
return null;
}
}
跨域处理为最优先的过滤器,鉴权之类的业务过滤器优先级放在后面,差不多这就是zuul的配置。
gateway配置:
配置文件:
server:
port: 12355
#服务的名称
spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 100MB
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
#路由id,唯一即可
- id: serviceaRoute
#要转发的地址,lb代表从注册中心获取服务地址
uri: lb://servicea/**
#断言,根据路径判断要触发此条路由
predicates:
- Path=/test/servicea/**
filters:
#此处的意思是去掉转发路径中的前两段,也就是转发到servicea时会去掉/test/servicea/
- StripPrefix=2
#跨域配置
globalcors:
cors-configurations:
'[/**]':
allowCredentials: true
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods: "*"
maxAge: 3628800
#此处建议写,当配置了多条跨域时的策略选择,默认选择第三个
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE
#指定注册中心地址
eureka:
client:
service-url:
defaultZone: http://eureka:8761/eureka/
instance:
prefer-ip-address: true
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 30
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
filter:
allows:
#此处填不需要鉴权的路径
- /aaa/bbb/ccc
- /dd/fff
关于上面default-filters的配置看这里:https://github.com/spring-cloud/spring-cloud-gateway/issues/728
代码配置:
@Slf4j
@Component
public class LoginFilter implements GlobalFilter, Ordered {
@Autowired
private AllowProperties allowProperties;
@Override
public int getOrder() {
return 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
String path = exchange.getRequest().getPath().value();
// 验证token是否有效
HttpHeaders headers = exchange.getRequest().getHeaders();
String accessToken = headers.getFirst("accessToken");
//过滤不需要鉴权的
if (ignore(path)) {
log.info("ignore:{}", path);
return chain.filter(exchange);
}
//token为空
if (StrUtil.isEmpty(accessToken)) {
return emptyResponse(exchange, "need token");
}
//不为空
return chain.filter(exchange);
} catch (Exception e) {
log.info(e.getMessage());
}
return chain.filter(exchange);
}
private Mono<Void> emptyResponse(ServerWebExchange exchange, String message) {
ServerHttpResponse serverHttpResponse = exchange.getResponse();
Result<JSONObject> result = new Result<JSONObject>();
result.setCode(HttpStatus.UNAUTHORIZED.value());
result.setMessage(message);
result.setSuccess(false);
serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
serverHttpResponse.getHeaders()
.add("Content-Type", "application/json; charset=utf-8");
return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
}
/**
* 检查是否忽略url
*/
private boolean ignore(String path) {
List<String> ignoreUrl = allowProperties.getAllows();
return allowProperties.getAllows().stream()
.map(url -> url.replace("/**", ""))
.anyMatch(path::startsWith);
}
}
读取配置文件的类:
@Component
@ConfigurationProperties(prefix = "filter")
@Data
public class AllowProperties {
private List<String> allows;
}
处理web的跨域,网上讲的很多我就不写原因了:
@Configuration
@Slf4j
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
log.info("Methods:{}", JSON.toJSONString(config.getAllowedMethods()));
log.info("Origins:{}", JSON.toJSONString(config.getAllowedOrigins()));
log.info("Headers:{}", JSON.toJSONString(config.getAllowedHeaders()));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}