1.网关路由
1.1认识网关
网关:网络关口,负责请求的路由,转发,身份校验
1.2快速入门
1.2.1 引入依赖
<!--网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
1.2.2 配置路由
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 192.168.150.101:8848
gateway:
routes:
- id: item # 路由规则id,自定义,唯一
uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
- Path=/items/**,/search/** # 这里是以请求路径作为判断规则
- id: cart
uri: lb://cart-service
predicates:
- Path=/carts/**
2.网关登录校验
2.1 思路分析
2.2 登录校验
在网关中编写过滤器
@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final AuthProperties authProperties; //路径 /user/**,/login/**
private final JwtTool jwtTool;
private final AntPathMatcher antPathMatcher;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求
ServerHttpRequest request = exchange.getRequest();
//2.判断是否需要登录拦截
if (isExclude(request.getPath().toString())){
return chain.filter(exchange);
}
//3.获取token
String token=null;
List<String> headers = request.getHeaders().get("authorization");
if (headers!=null && !headers.isEmpty()){
token=headers.get(0);
}
//4.校验并解析token
Long userId=null;
try {
userId = jwtTool.parseToken(token);
} catch (Exception e) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//终止请求
return response.setComplete();
}
//5.传递用户信息
String string = userId.toString();
ServerWebExchange swe = exchange.mutate()
.request(builder -> builder.header("user-info", string))
.build();
return chain.filter(swe);
}
private boolean isExclude(String path) {
List<String> excludePaths = authProperties.getExcludePaths();
for (String excludePath : excludePaths) {
if(antPathMatcher.match(excludePath,path)){
return true;
}
}
return false;
}
@Override
public int getOrder() {
return 0;
}
}
3.网关传递用户信息
3.1在微服务中编写拦截器
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userInfo = request.getHeader("user-info");
if (StrUtil.isNotBlank(userInfo)){
UserContext.setUser(Long.valueOf(userInfo));
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserContext.removeUser();
}
}
3.2 在微服务中注册拦截器
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInfoInterceptor());
}
}
4.微服务之间传递用户信息
下单的过程中,需要调用商品服务扣减库存,调用购物车服务清理用户购物车。而清理购物车时必须知道当前登录的用户身份。但是,订单服务调用购物车时并没有传递用户信息,购物车服务无法知道当前用户是谁!
由于微服务获取用户信息是通过拦截器在请求头中读取,因此要想实现微服务之间的用户信息传递,就必须在微服务发起调用时把用户信息存入请求头。
微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。我们如何才能让每一个由OpenFeign发起的请求自动携带登录用户信息呢?
这里要借助Feign中提供的一个拦截器接口:feign.RequestInterceptor
4.1OpenFeign模块中编写拦截器
public class DefaultFeignConfig {
@Bean
public Logger.Level fullFeignLoggerLevel(){
return Logger.Level.FULL;
}
@Bean
public RequestInterceptor requestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
Long user = UserContext.getUser();
if (user!=null){
template.header("user-info", user.toString());
}
}
};
}
@Bean
public ItemClientFallbackFactory itemClientFallbackFactory(){
return new ItemClientFallbackFactory();
}
}
4.2微服务在启动类上添加
@EnableFeignClients(clients = {ItemClient.class, CartClient.class},defaultConfiguration = DefaultFeignConfig.class)
public class TradeApplication {
public static void main(String[] args) {
SpringApplication.run(TradeApplication.class,args);
}
}