一、网关
网关:就是网络的关口,负责请求的路由,转发,身份校验。
在SpringCloud中网关的实现:
SpringCloud Gateway
由SpringCloud官方出品
基于WebFlux响应式编程
无需调优即可获得优异性能
(1)网关路由
配置路由规则
spring:cloud:gateway:routes: - id: item # 路由规则id,自定义,唯一uri: lb://item-service # 路由目标微服务,lb代表负载均衡predicates: # 路由断言,判断请求是否符合规则,符合则路由到目标- Path=/items/** # 以请求路径做判断,以/items开头则符合- id: xxuri: lb://xx-servicepredicates:- Path=/xx/**
①快速入门:
创建一个新的模块,引入网关的依赖,编写启动类,配置路由规则。
②路由属性
网关路由对应的java类型是RouteDefinition,其中常见的属性有:
id:路由唯一标识
uri:路由目标地址
predicates:路由断言,判断请求是否符合当前路由
filters:路由过滤器,对请求或响应做特殊处理
路由断言:
路由过滤器:
(2)网关登录校验
ctrl+H 查看该类的实现类
网关过滤器有两种,分别是:
GatewayFilter:路由过滤器作用于任意指定的路由,默认不生效,要配置到路由后生效。
GlobalFilter:全局过滤器,作用范围是所有路由,声明后自动生效。
①自定义全局过滤器
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求ServerHttpRequest request = exchange.getRequest();HttpHeaders headers = request.getHeaders();System.out.println("headers"+headers);
// 放行return chain.filter(exchange);}@Overridepublic int getOrder() {
// 过滤器执行顺序,值越小,优先级越高return 0;}
}
②自定义过滤器GatewayFilter
自定义GatewayFilter不是直接实现GatewayFilter,而是实现AbstractGatewayFilterFactory
@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {@Overridepublic GatewayFilter apply(Object config) {return new OrderedGatewayFilter(new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("print any filter running");return chain.filter(exchange);}},1);}}
}
③实现登录校验
@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final AuthProperties authProperties;private final JwtTool jwtTool;private final AntPathMatcher antPathMatcher=new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取requestServerHttpRequest request = exchange.getRequest();
// 2.判断是否需要做登录拦截if (isExclude(request.getPath().toString())){
// 放行return chain.filter(exchange);}
// 3.获取tokenString token =null;List<String> headers = request.getHeaders().get("authorization");if (headers!=null&&!headers.isEmpty()){token=headers.get(0);}
// 4.校验并解析tokenLong userId=null;try{userId = jwtTool.parseToken(token);}catch(UnauthorizedException e){ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}
// TODO 5.传递用户信息System.out.println("userId"+userId);
// 6.放行return chain.filter(exchange); }private boolean isExclude(String path) {for (String pathPattern :authProperties.getExcludePaths()){if (antPathMatcher.match(pathPattern,path)){return true;}}return false;}@Overridepublic int getOrder() {return 0;}
}
④网关传递用户信息
一、在网关的登录校验过滤器中,把获取到的用户写入请求头
需求:修改gateway模块中的登录校验拦截器,在校验成功后保存用户到下游请求的请求头中。 提示:要修改转发到微服务的请求,需要用到ServerWebExchange类提供的API,示例如下:
// 5.传递用户信息String userInfo = userId.toString();ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("userinfo", userInfo)).build();
从网关那里获取用户信息,通过过滤器做拦截,获取修改请求头,定义一个通用的拦截器,
二、在hm-common中编写SpringMVC拦截器,获取登录用户
需求:由于每个微服务都可能有获取登录用户的需求,因此我们直接在hm-common模块定义拦截器,这样微服务只需要引入依赖即可生效,无需重复编写。
@Component
public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取用户登陆信息String userinfo = request.getHeader("userinfo");
// 判断是否获取了用户,如果有,存入ThreadLocal
if (StrUtil.isNotBlank(userinfo)){UserContext.setUser(Long.valueOf(userinfo));}
// 放行
return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserContext.removeUser();}
}
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig,\com.hmall.common.config.JsonConfig
⑤OpenFeign传递用户(微服务之间的相互调用)
微服务项目中的很多业务要多个微服务共同合作完成,而这个过程中也需要传递登录用户信息。
OpenFeign中提供了一个拦截器接口,所有由OpenFeign发起的请求都会先调用拦截器处理请求
@Beanpublic RequestInterceptor userInfoRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {Long userId = UserContext.getUser();if (userId!=null){RequestTemplate userinfo = template.header("userinfo", userId.toString());}}};}
(3)配置管理
①配置共享
i.添加一些通用配置到Nacos中,包括:Jdbc、MybatisPlus、日志、Swagger、OpenFeign等配置
ii.拉取共享配置
基于NacosConfig拉取共享配置代替微服务的本地配置
引入依赖
<!--nacos配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--读取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>
新建bootstrap.yaml
spring:application:name: cart-service #微服务名称profiles:active: devcloud:nacos:server-addr: 1.12.232.19:8848 #nacos地址config:file-extension: yaml #文件后缀名shared-configs:- data-id: shared-jdbc.yaml- data-id: shared-log.yaml- data-id: shared-swagger.yaml
②配置热更新
配置热更新:当修改配置文件中的配置时,微服务无需重启即可使配置生效。
前提条件:nacos中要有一个与微服务名有关的配置文件。
微服务中要以特定方式读取需要热更新的配置属性
@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {private Integer maxItems;
}
③动态路由
如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。
-
创建ConfigService,目的是连接到Nacos
-
添加配置监听器,编写配置变更的通知处理逻辑
在网关中引入依赖
<!--统一配置管理-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--加载bootstrap-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
然后在网关gateway
的resources
目录创建bootstrap.yaml
文件,内容如下
spring:application:name: gatewaycloud:nacos:server-addr: 192.168.150.101config:file-extension: yamlshared-configs:- dataId: shared-log.yaml # 共享日志配置
接着,修改gateway
的resources
目录下的application.yml
,把之前的路由移除
server:port: 8080
hm:jwt:location: classpath:hmall.jksalias: hmallpassword: hmall123tokenTTL: 30mauth:excludePaths:- /search/**- /users/login- /items/**- /hi
然后,在gateway
中定义配置监听器
接下来,我们直接在Nacos控制台添加路由,路由文件名为gateway-routes.json
,类型为json
[{"id": "item","predicates": [{"name": "Path","args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}}],"filters": [],"uri": "lb://item-service"},{"id": "cart","predicates": [{"name": "Path","args": {"_genkey_0":"/carts/**"}}],"filters": [],"uri": "lb://cart-service"},{"id": "user","predicates": [{"name": "Path","args": {"_genkey_0":"/users/**", "_genkey_1":"/addresses/**"}}],"filters": [],"uri": "lb://user-service"},{"id": "trade","predicates": [{"name": "Path","args": {"_genkey_0":"/orders/**"}}],"filters": [],"uri": "lb://trade-service"},{"id": "pay","predicates": [{"name": "Path","args": {"_genkey_0":"/pay-orders/**"}}],"filters": [],"uri": "lb://pay-service"}
]