【SpringCloud Gateway】SpringCloud各微服务之间用户登录信息共享的实现思路——gateway网关token校验以及向微服务发送请求携带token

这篇具有很好参考价值的文章主要介绍了【SpringCloud Gateway】SpringCloud各微服务之间用户登录信息共享的实现思路——gateway网关token校验以及向微服务发送请求携带token。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        最近在学习SpringCloud项目时,想到了一些问题,各个微服务分别部署在不同的服务上,由naocs作为注册中心实现负载均衡,彼此之间通过Feign相互调用通信,信息同步并不像单体项目那样方便,传统单体项目的登录验证方式似乎在SpringCloud中不能满足项目的需求。那么当用户完成登录后,各微服务该如何确认用户的登录状态呢?

        下面有几种实现思路:

  • 统一认证中心:建立一个单独的认证中心,例如使用Spring Security或者基于OAuth的认证服务。每个微服务都需要将用户的登录请求导向认证中心,认证中心负责验证用户身份。认证中心可以颁发访问令牌,微服务通过访问令牌进行鉴权。
  • JWT (JSON Web Tokens):使用JWT来实现身份验证和授权。认证中心颁发包含用户信息的JWT令牌,微服务在收到请求时验证JWT令牌的有效性,并提取其中的用户信息。这样,用户信息可以在不同微服务之间共享。
  • 消息队列:使用消息队列(如RabbitMQ、Kafka)来在微服务之间传递用户登录信息。当用户登录或注销时,认证中心可以发布消息,其他微服务订阅这些消息以更新用户状态。
  • 分布式缓存:使用分布式缓存(如Redis)来存储用户登录信息。当用户登录时,在认证中心将用户信息缓存到Redis中,其他微服务可以查询Redis以获取用户信息。

        这里为大家提供一种较为简单的方式:使用Redis分布式缓存储存用户登录信息。在gateway微服务中配置过滤器,在过滤器中获取到达网关的请求所携带的token信息,如果token为空或token对应的key在Redis中不存在,向用户返回401 UNAUTHORIZED的状态码;如果token验证正确,便刷新Redis中对应key的TTL,并继续向负载均衡的请求地址发送请求且携带相应的token信息。在接收请求的微服务中编写拦截器,在拦截器中获取token并通过Redis拿取对应的用户信息。

        gateway中的过滤器代码实现:

@Order(1)
@Configuration
public class GlobalFilterConfig implements GlobalFilter {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst(GET_TOKEN);
        if (token == null || token.isEmpty()) {
            return unAuthorize(exchange);
            
        }
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(LOGIN_TOKEN_KEY + token);
        if (map.isEmpty()) {
            return unAuthorize(exchange);
            
        }
        
        // 刷新TTL
        stringRedisTemplate.expire(LOGIN_TOKEN_KEY + token, 30, TimeUnit.MINUTES);
        
        //把新的 exchange放回到过滤链
        ServerHttpRequest request = exchange.getRequest().mutate().header(GATEWAY_TOKEN, token).build();
        ServerWebExchange newExchange = exchange.mutate().request(request).build();
        return chain.filter(newExchange);
        
    }
    
    // 返回未登录的自定义错误
    private Mono<Void> unAuthorize(ServerWebExchange exchange) {
        // 设置错误状态码为401
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 设置返回的信息为JSON类型
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        // 自定义错误信息
        String errorMsg = "{\"error\": \"" + "用户未登录或登录超时,请重新登录" + "\"}";
        // 将自定义错误响应写入响应体
        return exchange.getResponse()
                .writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(errorMsg.getBytes())));
    }
}

        注意需要使用@Order注解为该全局过滤器设置优先级。当过滤器的order值一致时,过滤器的执行顺序为:defaultFilter>路由过滤器>GlobalFilter,因此该过滤器的order值应当设置为较小值,以确保该全局过滤器的正确执行。(order值越小,优先级越高,执行顺序越靠前)

        微服务中拦截器的代码实现:

public class LoginHandlerInterceptor implements HandlerInterceptor {
    private StringRedisTemplate stringRedisTemplate;
    
    // 由于该类未交给spring管理,因此不能使用自动装配的方式获取RedisTemplate对象
    public LoginHandlerInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader(GATEWAY_TOKEN);
        if (token == null || token.isEmpty()) {
            return false;
        }
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(LOGIN_TOKEN_KEY + token);
        if (map.isEmpty()) {
            return false;
        }
        UserDto userDto = BeanUtil.toBean(map, UserDto.class);
        UserContext.saveUser(userDto); // 将用户信息放入线程中
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserContext.removeUser();
    }
}
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor(stringRedisTemplate))
                .addPathPatterns("/**");
    }
}

        到这里我们就完成了gateway到微服务的用户登录信息传递。接下来就需要解决微服务与微服务之间的登录信息传递问题。在这个项目中各微服务通过分Feign实现相互调用通信,那么我们只需要在调取Feign时携带token信息就好:

@FeignClient(name = "test-gateway")
public interface ExampleClient {

    @GetMapping("/api/example")
    String getExampleData(@RequestHeader("token") String token);
}

        但每次调用该Feign接口时都需要我们手动传入token值,不太优雅,因此采用下面的方式来配置Feign,每当Feign接口被调用时就会携带token信息:

public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header(GET_TOKEN, TokenContext.getToken());
    }
}
@FeignClient(name = "test-gateway", configuration = FeignRequestInterceptor.class)
public interface ExampleClient {

    @GetMapping("/api/example")
    String getExampleData(@RequestHeader("token") String token);
}

        若遇到启动时报错A bean with that name has already been defined and overriding is disabled可以看这篇文章:【SpringCloud】使用OpenFeign的spring项目启动时报错bean注册问题

        至此就完成了最基础的微服务登录信息传递。文章来源地址https://www.toymoban.com/news/detail-754806.html

到了这里,关于【SpringCloud Gateway】SpringCloud各微服务之间用户登录信息共享的实现思路——gateway网关token校验以及向微服务发送请求携带token的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springcloud的gateway之GlobalFilter获取请求信息及requestBody

    《本文参考地址》 RequestGlobalFilter.java ResponseGlobalFilter.java RequestAndResponseGlobalFilter.java

    2024年02月16日
    浏览(29)
  • uniApp微信小程序唤出授权头像昵称(微信授权登录)弹窗,及服务端用户信息解密注意事项

    头像昵称弹窗弹出条件:button授权按钮 + uni.getUserProfile API请求 1.H5部分 2.JS部分 注意事项: 不能嵌入其他API内调用,一定要在调用的方法中第一层执行(优先执行 uni.getUserProfile ) 正确做法 :必须第一步用户点击按钮,第二步调取 uni.getUserProfile API(调取 uni.getUserProfile 操作

    2024年02月11日
    浏览(53)
  • SpringCloud - GateWay服务网关

    gateway 官网:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/ 在微服务架构中,一个系统往往由多个微服务组成,而这些服务可能部署在不同机房、不同地区、不同域名下。这种情况下,客户端(例如浏览器、手机、软件工具等)想要直接请求这些服务

    2024年02月07日
    浏览(45)
  • 【SpringCloud】Gateway服务网关

    Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Gateway网关是我们服务的守门神,所有微服务的统一入口。 网关的

    2024年02月13日
    浏览(47)
  • SpringCloud:Gateway服务网关

    网关(Gateway)是将两个使用不同协议的网络段连接在一起的设备。 网关的作用就是对两个网络段中的使用不同传输协议的数据进行互相的翻译转换。 创建服务 导入依赖 编写启动类 添加配置 Route Predicate Factories :: Spring Cloud Gateway 对所有路径都生效 全局过滤器的作用也是处理

    2024年02月01日
    浏览(51)
  • 微服务网关 —— SpringCloud Gateway

    Spring Cloud Gateway 基于 Spring 5、Spring Boot 2 和 Project Reactor 等技术,是在 Spring 生态系统之上构建的 API 网关服务,Gateway 旨在提供一种简单而有效的方式来对 API 进行路由以及提供一些强大的过滤器功能,例如熔断、限流、重试等 Spring Cloud Gateway 具有如下特性: 基于 Spring Frame

    2024年02月10日
    浏览(44)
  • SpringCloud_Gateway服务网关

    Spring Cloud Gateway 用\\\"Netty + Webflux\\\"实现,不需要导入Web依赖。 Webflux 模式替换了旧的Servlet线程模型。用少量的线程处理request和response io操作,这些线程称为Loop线程,而业务交给响应式编程框架处理,响应式编程是非常灵活的,用户可以将业务中阻塞的操作提交到响应式框架的

    2024年02月02日
    浏览(45)
  • SpringCloud第三篇:GateWay服务网关

          传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。那有了网关之后

    2024年02月08日
    浏览(42)
  • day08-SpringCloud Gateway-服务网关

    没有使用网关服务时: 使用网关服务后: 官网:Spring Cloud Gateway Gateway是Spring生态系统之上构建的API网关服务,基于Spring、SpringBoot和Project Reactor等技术 Gateway旨在提供一种简单有效的方式来对API进行路由,以及提供一切强大的过滤器功能,例如:熔断、限流、重试等 鉴权 流

    2024年02月07日
    浏览(37)
  • SpringCloud基础篇-10-服务网关-Gateway

    上一代网关Zuul 官网 SpringCloudGateway官网,变化很大,以实际为准 Gateway是在Spring生态系统之上构建的API网关服务,基于Spring5SpringBoot2和ProjectReactor等技术。 Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等 SpringCl

    2024年04月11日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包