当前位置: 首页 > 后端技术 > Java

快速搭建网关服务,动态路由,认证,一气呵成!(含流程图)

时间:2023-04-01 21:50:47 Java

作者:HotButterbeer\链接:https://juejin.cn/post/700475...前言本文记录我如何使用Gateway搭建网关服务,实现动态路由,帮助大家学习如何快速搭建网关服务,了解路由相关的配置、认证流程和业务处理,有兴趣的一定要看文末。非常适合没有接触过网关服务的同学作为入门教程。构建服务框架SpringBoot2.1org.springframework.bootspring-boot-starter-parent2.1.0.RELEASEspring-cloud-gateway-coreorg.springframework.cloudspring-cloud-gateway-corecommon-lang3org.apache.commonscommons-lang3推荐一个开源免费的SpringBoot最全教程:https://github.com/javastacks/spring-boot-best-实践路由配置网关作为请求的统一入口。路由相当于各个业务系统的入口。通过路由规则,可以匹配到对应微服务的入口,将请求命中对应的业务系统。服务器:端口:8080spring:cloud:gateway:enabled:trueroutes:-id:demo-serveruri:http://localhost:8081谓词:-Path=/demo-server/**filters:-StripPrefix=1routes配置itemdescriptionid路由唯一id,使用服务名uri路由服务的访问地址可以调用predicatesroutingassertionfilters过滤规则解释配置现在本机部署了一个服务demo-server,地址和端口都是127.0.0.1:8081,所以路由配置uri为:http://localhost:8081使用网关服务路由到这个服务,谓词-Path=/demo-server/**,网关服务的端口为8080,启动网关服务,访问localhost:8080/demo-server,路由断言会将请求路由到demo-server直接访问demo-server的接口localhost:8081/api/test,通过网关访问地址为localhost:8080/demo-server/api/test,predicates配置断言请求到这条路由,filters-StripPrefix=1表示拦截地址中/之后的第一部分,所以demo-server拦截了。使用网关通过配置文件完成路由配置非常方便。我们只需要充分理解配置项的含义和规则即可。;但是,如果要修改这些配置,则需要重启服务。重启网关服务会导致整个系统不可用,这是不可接受的。下面介绍如何通过Nacos实现动态路由。你需要先部署一个nacos服务。可以使用docker部署或者下载源码在本地启动。具体操作请参考官方文档。Nacos配置groupId:使用网关服务名dataId:routes配置格式:json[{"id":"xxx-server","order":1,#priority"predicates":[{#routingassertion"args":{“模式”:“/xxx-server/**”},“名称”:“路径”}],“过滤器”:[{#过滤规则"args":{"parts":0#如果k8s服务内部访问容器为http://xxx-server/xxx-server,配置0},"name":"StripPrefix"#开始拦截index}],"uri":"http://localhost:8080/xxx-server"#targetaddress}]json格式的配置项对应yaml中的,需要知道如何用json写配置,对比json配置使用yaml配置{"id":"demo-server","predicates":[{"args":{"pattern":"/demo-server/**"},"name":"Path"}],"filters":[{"args":{"parts":1},"name":"StripPrefix"}],"uri":"http://localhost:8081"}spring:cloud:gateway:enabled:真实路线:-id:演示服务器uri:http://localhost:8081谓词:-Path=/demo-server/**filters:-StripPrefix=1代码实现Nacos实现动态路由。核心是通过Nacos配置进行监控。配置变更后,执行网关相关接口创建路由。@ComponentpublicclassNacosDynamicRouteServiceimplementsApplicationEventPublisherAware{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(NacosDynamicRouteService.class);@AutowiredprivateRouteDefinitionWriterrouteDefinitionWriter;私有应用事件发布者应用事件发布者;/**路由ID*/privatestaticListrouteIds=Lists.newArrayList();/***监听nacos路由配置,动态更改路由*@paramconfigInfo*/@NacosConfigListener(dataId="routes",groupId="gateway-server")publicvoidrouteConfigListener(StringconfigInfo){clearRoute();try{ListgatewayRouteDefinitions=JSON.parseArray(configInfo,RouteDefinition.class);对于(RouteDefinitionrouteDefinition:gatewayRouteDefinitions){添加路线(路线定义);}发布();LOGGER.info("动态路由发布成功");}catch(Exceptione){LOGGER.error(e.getMessage(),e);}}/***清空路径由*/privatevoidclearRoute(){for(Stringid:routeIds){routeDefinitionWriter.delete(Mono.just(id)).subscribe();}routeIds.clear();}@OverridepublicvoidsetApplicationEventPublisher(ApplicationEventPublisherapplicationEventPublisher){this.applicationEventPublisher=applicationEventPublisher;}/***添加路由**@paramdefinition*/privatevoidaddRoute(RouteDefinitiondefinition){try{routeDefinitionWriter.save(Mono.just(definition)).subscribe();routeIds.add(definition.getId());}catch(Exceptione){LOGGER.error(e.getMessage(),e);}}/***发布路由,让路由生效*/privatevoidpublish(){this.applicationEventPublisher.publishEvent(newRefreshRoutesEvent(this.routeDefinitionWriter));}}过滤器网关提供了GlobalFilter和Ordered两个接口来定义过滤器,我们自定义过滤服务器只需要实现这两个接口即可。GlobalFilterfilter()实现过滤器业务OrderedgetOrder()定义过滤器执行顺序。通常一个网关服务的过滤主要包括鉴权(登录,黑名单,免登录接口。。。)限流(ip限流等)功能,今天我们简单介绍一下鉴权过滤器的流程,实现鉴权filter,我们要实现鉴权过滤器,首先要了解登录和鉴权流程,如下图所示从图中可以看出,我们鉴权和过滤的核心是验证token是否有效,所以我们的网关服务需要和业务系统在同一个redis库中,首先给网关添加redis依赖,配置org.springframework.bootspring-boot-starter-data-redis-reactivespring:redis:host:redis-serverport:6379password:database:0代码实现1.定义过滤器AuthFilter2。获取请求对象从请求头或者参数或者cookie中获取token(支持多种方式传递token对客户端比较友好,比如有些网页下载请求会新建一个页面,传递token比较麻烦在请求头中)3.没有token,返回4014。如果有token,检查redis是否有效5.如果无效,返回401,如果有效,完成验证并释放6.重置token过期时间,添加内部请求头信息,方便业务系统权限处理@ComponentpublicclassAuthFilterimplementsGlobal过滤器,有序{@AutowiredprivateRedisTemplateredisTemplate;privatestaticfinalStringTOKEN_HEADER_KEY="auth_token";@OverridepublicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){//1.获取请求对象ServerHttpRequest=exchange.getRequest();//2.获取令牌Stringtoken=getToken(request);ServerHttpResponse响应=exchange.getResponse();if(StringUtils.isBlank(token)){//3.如果token为空则返回401响应.setStatusCode(HttpStatus.UNAUTHORIZED);返回响应.setComplete();}//4.验证令牌是否有效StringuserId=getUserIdByToken(token);if(StringUtils.isBlank(userId)){//5.如果令牌无效则返回401response.setStatusCode(HttpStatus.UNAUTHORIZED);返回响应.setComplete();}//token有效,后续业务处理//写入请求头,方便业务系统从请求头中获取用户id进行权限相关处理ServerHttpRequest.Builderbuilder=exchange.getRequest().mutate();request=builder.header("user_id",userId).build();//延长缓存过期时间——如果token一直缓存用户运行过程中会一直重置过期时间//这样可以避免用户操作过程中突然过期,影响业务运行和体验。只有当用户操作间隔大于缓存过期时间才会过期。resetTokenExpirationTime(token,userId);//完成验证返回链。过滤器(交换);}@OverridepublicintgetOrder(){//优先级越小,优先级越高return0;}/***从redis中获取用户id*当登录操作成功后,会生成一个token,redis会获取到key为auth_token:token值为userid**@paramtoken*@return*/privateStringgetUserIdByToken(Stringtoken){StringredisKey=String.join(":","auth_token",token);返回redisTemplate.opsForValue().get(redisKey);}/***重置令牌过期时间**@paramtoken*@paramuserId*/privatevoidresetTokenExpirationTime(Stringtoken,StringuserId){StringredisKey=String.join(":","auth_token",token);redisTemplate.opsForValue().set(redisKey,userId,2,TimeUnit.HOURS);}/***获取令牌**@paramrequest*@return*/privatestaticStringgetToken(ServerHttpRequestrequest){HttpHeadersheaders=request.getHeaders();//从请求头中获取令牌Stringtoken=headers.getFirst(TOKEN_HEADER_KEY);if(StringUtils.isBlank(token)){//请求头如果没有token,从url中获取tokentoken=request.getQueryParams().getFirst(TOKEN_HEADER_KEY);}if(StringUtils.isBlank(token)){//如果requestheader和url都没有token,从cookies中获取HttpCookiecookie=request.getCookies().getFirst(TOKEN_HEADER_KEY);if(cookie!=null){token=cookie.getValue();}}返回令牌;}}总结Gateway可以通过配置项实现路由功能,结合Nacos和配置监控实现动态路由,实现GlobalFilter和Ordered接口可以快速实现一个过滤器。文章还详细介绍了登录后的请求认证流程。不清楚的可以评论区看看近期热点文章推荐:1.1,000+Java面试题及答案(2022最新版)2.厉害了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!