网关路由的实现及使用

1.微服务网关 的定义

正常来说 我们 请求转发到我们的微服务的时候  是对请求链接进行拼接的,比如 我们的购物车服务的 请求地址后面有一个 /cartservice ,然后我们其他请求 过来 就是对一个网址进行拼接,我们使用本地 请求购物车服务   就是 localhost/8080 /user/cartservice ,前面的基本都一样 就是我们最后几个请求不一样 ,而网关就是根据我们 请求的网址 进行鉴权 校验 看看需要转发到哪个 微服务中去,那 网关既然帮我们进行校验了,那网关就可以取代我们的普通的单体架构当中 的过滤器 拦截器的校验了。

=而网关怎么知道我们要访问哪个微服务呢,这时候就用到路由了,路由来指定哪个网址转发到哪个微服务中去

最终的请求转发是由 nettyroutingfilter进行的,所以我们的自定义登录鉴权 +用户信息的配置的路在请求转发到微服务之前来完成,也就是nettyroutingfilter之前,所以我们需要给我们的过滤波器也设置一个优先级 我们直接看实现代码

网关全局过滤器代码实现

@RequiredArgsConstructor
@Slf4j
@Component
public class TokenGlobalFilter implements GlobalFilter, Ordered {
    private  final JwtProperties jwtProperties;
    private  final StringRedisTemplate redisTemplate;
    // 这个过滤器 请求的转发
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //我们只要是非登录请求全都要检验jwt 然后进行 用户信息的传递
       //获取request对象
        ServerHttpRequest request = exchange.getRequest();
        RequestPath requestPath = request.getPath();
        if(requestPath.toString().contains("/userLogin")){
            return  chain.filter(exchange);
        }
        //获取请求头的 token
//        String redisToken =null;
//        List<String> authorization = request.getHeaders().get("authorization");
//        if (!CollUtil.isEmpty(authorization)) {
//            redisToken = authorization.get(0);
//        }
//获取请求 头的token 然后 拿到用户id 拼接到redis中
        String first = request.getHeaders().getFirst(jwtProperties.getTokenname());
    if(first==null){
      log.info("first为空");
    return  exchange.getResponse().setComplete();
}
    log.info("请求头的详细信息为{}",request.getHeaders());
        log.info("first的token={}",first);
        Claims claims1 = JwtUntils.parseJwt(first, jwtProperties.getSecretkey());
        String loginId2 = claims1.get(MessageConstant.LOGIN_ID).toString();
        String redisToken = (String)redisTemplate.opsForValue().get(MessageConstant.TOKEN+loginId2);

        log.info("token:{}",redisToken);
        if (redisToken != null && redisToken.equals(first) ) {
            //进行jwt的解析
            try {
                Claims claims = JwtUntils.parseJwt(redisToken, jwtProperties.getSecretkey());
                //每次 访问其他资源的时候 都把token更新
                String loginId = claims.get(MessageConstant.LOGIN_ID).toString();
                log.info("网关层当前用户的id:{}", Long.valueOf(loginId));
                redisTemplate.expire(MessageConstant.TOKEN+loginId, 1000, TimeUnit.DAYS);
                //证明 token有效 传递用户信息
                ServerWebExchange loginId1 = exchange.mutate()
                        .request(b -> b.header(MessageConstant.LOGIN_ID, loginId))
                        .build();
                return chain.filter(loginId1);
            } catch (Exception e) {
                log.info("{}",e.getMessage());
                //出现异常返回一个异常响应
                ServerHttpResponse response = exchange.getResponse();
                response.setRawStatusCode(401);
                return  response.setComplete();
            }
        }
        log.info("token错误");
       return  exchange.getResponse().setComplete();
    }
//过滤器链中的优先级 数值越低 优先级就越高
    @Override
    public int getOrder() {
        return 0;
    }
}

这是登录鉴权 并且往下一级传递用户信息的代码,我们只需要解析关键部分即可,首先 我们这个网关在使用之前需要引入网关的依赖这个就不说了,然后我们自定义个过滤器类,实现

GlobalFilter, Ordered

这接口,然后重写其中的filter 与getorder方法,filter做登录校验和 信息传递,而getorder方法进行过滤器优先级的配置,我们直接返回0,

再往下看

我们解析到token之后,根据我们的登录设置jwt令牌的token 获得当时传的用户id,然后 手动设置一个请求投给 我们的请求体,然后把改请求头传给下一层过滤器,最后靠过滤器进行 用户信息的传递

微服务实例用户信息解析

好,那么现在 我们到 把请求转发到微服务了,我们的服务在接收的时候需要解析出来我们设置的请求 信息,然后把请求信息传递到整个实例中  , 我们怎么解析嘞,当然是请求进入实例的时候加一个拦截器就可以啦

@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String header = request.getHeader(MessageConstant.LOGIN_ID);
        if(header !=null){
            ThreadContext.setCurrentId(Long.valueOf(header));
            log.info("网关之后 获得的用户id{}",ThreadContext.getCurrentId());
        }
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    //移除用户
      ThreadContext.removeCurrentId();
    }
}

然后我们在微服务实例互相调用的时候需不需要知道我们的用户信息,那当然是也需要 我们只需要用feign中的一个实例就可以了

@Slf4j
public class UserFeignConfig {
    @Bean
   public RequestInterceptor requestInterceptor(){
     return  new RequestInterceptor() {
         @Override
         public void apply(RequestTemplate requestTemplate) {
             Long currentId = ThreadContext.getCurrentId();
             if(currentId == null){
                 return;
             }
             log.info("feign调用的时候传递了用户id{}",currentId);
             requestTemplate.header(MessageConstant.LOGIN_ID,currentId.toString());
         }
     };
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三氧化真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值