SpringCloud (2) 网关

发布时间:2026/7/27 18:07:15
SpringCloud (2) 网关 1 创建网关微服务-gateway1.1 网关作用①作为调用入口:随着微服务越来越多,前端调用地址会越来越复杂,有了网关后,前端的所有请求都走网关,由网关去做请求路由转发②身份校验:由于所有请求都走网关,一些公共的校验就可以在网关层做,比如用户登录校验1.2 引入依赖(网关模块不用openFeign依赖,因为不需要调用其他微服务,它只负责路由转发,当然加上此依赖也无伤大雅)!--SpringMVC的web开发的场景启动器与gateway冲突,因此设置继承common模块的依赖的排除该依赖-- dependency groupIdxyz.aboluo.common/groupId artifactIdcommon/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /exclusion /exclusions /dependency !--网关-- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency1.3 配置路由规则(application.yaml)spring: cloud: routes: - id: route-engineering #路由规则id,保证唯一 uri: lb://engineering #路由目标微服务名称,lb代表负载均衡 predicates: #路由断言(规则) - Path/engineering/guardNet/** #Path以请求路径(忽略请求路径前的IP和端口)断言,符合要求的请求路径被路由到uri对应的微服务去做请求 - Path/engineering/alWindow/** #Path以请求路径(忽略请求路径前的IP和端口)断言,符合要求的请求路径被路由到uri对应的微服务去做请求 - id: route-minio #路由规则id,保证唯一 uri: lb://minio #路由目标微服务名称,lb代表负载均衡 predicates: #路由断言(规则) - Path/minio/** #Path以请求路径(忽略请求路径前的IP和端口)断言,符合要求的请求路径被路由到uri对应的微服务去做请求filters:[StripPrefix1],// 去掉第一层/engineering1.4 增加application.properties文件,去掉数据源配置#禁用SpringBoot的自动数据源配置因为gateway无需访问数据库如果不开启这个配置就需要在application.yaml中配置数据源 spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration2 身份校验2.1 网关请求处理流程2.2 自定义过滤器自定义一个过滤器,让它在NettyRoutingFilter之前执行,将登录校验的内容放在自定义过滤器的PRE中,以此保证在NettyRoutingFilter将请求转发到微服务之前执行完登录校验2.2.1 两种网关过滤器GatewayFilter(约30多种):路由过滤器,配置在哪个路由下就在此路由下生效(也可以配置在default-filters下,让所有路由生效) //自定义比较麻烦,不建议使用GlobalFilter:全局过滤器,作用范围是所有路由,声明后自动生效2.2.2 代码示例Component public class MyGlobalFilter implements GlobalFilter, Ordered { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1.获取请求 ServerHttpRequest request exchange.getRequest(); // 2.拦截处理 System.out.println(打印请求头信息: request.getHeaders()); if (false) { // 如果程序不符合要求 ServerHttpResponse response exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); // 设置response的状态码401 return response.setComplete(); // Complete表示完结,不会再往下执行,返回值是Mono } // 3.放行 return chain.filter(exchange); } Override public int getOrder() { // 过滤器执行顺序(值越小,优先级越高(NettyRoutingFilter的ordered优先级是21亿,总能保证在最后执行)) return 0; } }3 路由转发3.1 用户信息从网关传递给微服务在自定义过滤器中过滤后得到的用户信息,需要从geteway模块传递到业务微服务模块,而网关和微服务之间是通过http协议进行数据传输的,因此将用户信息放在请求头中比较合适// 如果不做任何处理,网关会将请求头信息从gateway带到微服务的请求头要在gateway模块的自定义实现GlobalFilter接口的filter类中修改请求头,需要用到ServerWebExchange提供的API(mutate()),判断loginCode的有效性// 2.拦截处理(做用户校验,校验结果保存在请求头中) ServerWebExchange newExchange null; ListString loginCodes request.getHeaders().get(loginCode); if (CollectionUtils.isEmpty(loginCodes)) { // loginCode无效(下游微服务需要做登录校验的,拿到true才行;不需要做登录校验的,true/false都放行) newExchange exchange.mutate() //mutate表示对下游(请求,响应...)做更改 .request(builder - builder.header(loginCode-verification, false)) .build(); } else { String loginCode loginCodes.get(0); // 用获取的loginCode去redis查询是否有效 String redisString Common.getRedisString(login-code▲ loginCode); if (StringUtils.isNotBlank(redisString)) { // loginCode生效(下游微服务需要做登录校验的,拿到true才行;不需要做登录校验的,true/false都放行) newExchange exchange.mutate() //mutate表示对下游(请求,响应...)做更改 .request(builder - builder.header(loginCode-verification, true)) .build(); } else { // loginCode无效(下游微服务需要做登录校验的,拿到true才行;不需要做登录校验的,true/false都放行) newExchange exchange.mutate() //mutate表示对下游(请求,响应...)做更改 .request(builder - builder.header(loginCode-verification, false)) .build(); } } // 3.放行 return chain.filter(newExchange);3.2 用户信息在微服务之间的传递-OpenFeignOpenFeign提供了一个拦截器接口,所有OpenFeign发起的请求都会先(在上一个微服务最后,即下一个微服务的AOP之前执行)调用RequestInterceptor(gateway微服务不会触发)拦截器处理请求// 如果不做任何处理,OpenFeign不会将一个微服务的请求头的信息带到另一个微服务的请求头3.2.1 注册RequestInterceptor在common模块(其它微服务模块依赖common模块),新建DefaultFeignConfig类,将RequestInterceptor注册进SpringIOCpublic class DefaultFeignConfig { Bean public RequestInterceptor getRequestInterceptor() { return new RequestInterceptor() { Override public void apply(RequestTemplate requestTemplate) { // 当用OpenFeign调用下个微服务之前,将从拦截的请求头loginCode-verification数据封装到调用下个微服务的请求头中 // 虽然RequestContextHolder.getRequestAttributes()底层使用的是ThreadLocal技术,即无法跨微服务传递数据,但此拦截器是在此微服务中运行,即数据还未出此微服务之前就被处理了 ServletRequestAttributes attrs (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attrs ! null) { String aaa attrs.getRequest().getHeader(loginCode-verification); if (StringUtils.isNotBlank(aaa)) { requestTemplate.header(loginCode-verification, aaa); } } } }; } }3.2.2 让RequestInterceptor生效在对应微服务模块的启动类MainApplication加上EnableFeignClients注解,让RequestInterceptor生效EnableFeignClients(basePackages xxx.xxx.xxx, defaultConfiguration DefaultFeignConfig.class)3.3 微服务AOP拦截Controller请求由于gateway(GlobalFilter)微服务和OpenFeign(RequestInterceptor)微服务都提前将loginCode的校验结果封装在了请求头loginCode-verification中,因此在被请求的微服务的对Controller层进行拦截的AOP中做的操作逻辑相同微服务里面再通过AOP,拦截对Controller层的请求,根据Controller方法是否有loginCode参数// 获取请求头 ServletRequestAttributes attrs (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attrs ! null) { HttpServletRequest request attrs.getRequest(); String aaa request.getHeader(loginCode-verification); if(StringUtils.isNotBlank(aaa)){ //校验loginCode是否有效 } }