分布式认证中心第五集 路由统一鉴权

发布时间:2026/6/30 23:41:13
分布式认证中心第五集 路由统一鉴权 上集回顾login和oauth2两个项目还在各自为政今天我们要把它们统统收编到Nacos麾下再派一个Gateway当门卫大爷一、Nacos微服务的物业公司1. 给两个项目办入住首先login和oauth2这俩住户要搬进Nacos小区得先交物业费——引入依赖!--注册中心--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactIdversion2.2.0.RELEASE/version/dependency!--配置中心--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactIdversion2.2.0.RELEASE/version/dependency2. 给每个住户发门禁卡以login服务为例得告诉它你住哪个小区Nacos地址、几号楼几单元namespace和groupspring:application:name:login cloud:nacos:server-addr:127.0.0.1:8848discovery:namespace:${spring.profiles.active}group:shop config:namespace:${spring.profiles.active}group:shop file-extension:yaml refresh-enabled:trueprofiles:active:shop配置完直接把原来的 application.yml 内容搬到Nacos的 login-shop.yaml 文件里然后删掉本地的 application.yml——这叫把家当都存到物业保险柜。3.启动两个项目后去Nacos的服务列表看看如果出现下面这俩货恭喜注册中心搞定了再瞅瞅启动日志如果出现Locatedproperty source:CompositePropertySource{nameNACOS,propertySources[NacosPropertySource{namelogin-shop.yaml,shop},NacosPropertySource{namelogin.yaml,shop},NacosPropertySource{namelogin,shop}]}说明配置中心也OK了如果找不到这行日志直接看启动端口是不是Nacos里配的那个——简单粗暴二、Gateway微服务的门卫大爷1.给门卫配装备新建一个gateway服务它要站在所有请求的前面当门神。先给它配齐家伙什dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-oauth2-resource-server/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-validation/artifactId/dependency!--注册中心--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactIdversion2.2.0.RELEASE/version/dependency!--配置中心--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactIdversion2.2.0.RELEASE/version/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactIdversion2.2.0.RELEASE/version/dependency2. 门卫的放行规则SecurityConfig门卫得知道哪些人能直接进哪些人要检查ConfigurationEnableWebFluxSecuritypublicclassSecurityConfig{BeanpublicSecurityWebFilterChainspringSecurityFilterChain(ServerHttpSecurityhttp){http.authorizeExchange(exchanges-exchanges.pathMatchers(HttpMethod.OPTIONS,/**).permitAll().pathMatchers(/api/login/**,/oauth/**,/login,/login.html).permitAll().anyExchange().authenticated()).oauth2ResourceServer(oauth2-oauth2.jwt()).csrf(ServerHttpSecurity.CsrfSpec::disable);returnhttp.build();}BeanpublicCorsWebFiltercorsWebFilter(){CorsConfigurationconfignewCorsConfiguration();config.addAllowedOrigin(http://localhost:8080);config.addAllowedOrigin(null);config.addAllowedMethod(*);config.addAllowedHeader(*);config.setAllowCredentials(true);config.setMaxAge(3600L);UrlBasedCorsConfigurationSourcesourcenewUrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**,config);returnnewCorsWebFilter(source);}3. 门卫的验钞机JwtConfigJWT token长啥样门卫得有个验钞机来识别ConfigurationpublicclassJwtConfig{privateStringtokenKeyUrlhttp://localhost:8129/oauth/token_key;BeanpublicReactiveJwtDecoderreactiveJwtDecoder(){try{Stringjsonnewjava.util.Scanner(newjava.net.URL(tokenKeyUrl).openStream()).useDelimiter(\\A).next();Stringpemjson.split(\value\:\)[1].split(\)[0];Stringbase64pem.replace(-----BEGIN PUBLIC KEY-----,).replace(-----END PUBLIC KEY-----,).replaceAll(\\\\n,).replaceAll(\\s,);RSAPublicKeypublicKey(RSAPublicKey)KeyFactory.getInstance(RSA).generatePublic(newX509EncodedKeySpec(Base64.getDecoder().decode(base64)));returnNimbusReactiveJwtDecoder.withPublicKey(publicKey).build();}catch(Exceptione){thrownewRuntimeException(获取JWT公钥失败,e);}}}4. 门卫给访客贴标签UserInfoForwardFilter门卫验完JWT后会把用户信息从token里抽出来写在便利贴上贴到请求头上传给后面的服务ComponentpublicclassUserInfoForwardFilterimplementsGlobalFilter,Ordered{OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){returnReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).map(auth-{Jwtjwt(Jwt)auth.getPrincipal();Stringusernamejwt.getClaimAsString(user_name);if(usernamenull){usernamejwt.getSubject();}ServerHttpRequestmutatedRequestexchange.getRequest().mutate().header(X-Username,username).build();returnexchange.mutate().request(mutatedRequest).build();}).defaultIfEmpty(exchange).flatMap(chain::filter);}OverridepublicintgetOrder(){returnOrdered.HIGHEST_PRECEDENCE1;}}5. 门卫的巡逻路线图路由配置告诉门卫哪条路往哪走server:port:8100spring:cloud:gateway:discovery:locator:enabled:falselower-case-service-id:trueroutes:-id:login uri:lb://login predicates:-Path/api/login/** - id: oauth2 uri: lb://oauth2 predicates: - Path/oauth/**,/login,/login.html logging: level: org.springframework.cloud.gateway: DEBUG reactor.netty.http.client: DEBUG三、改造瘦身后的login服务既然Gateway当了门卫login服务就可以躺平了1 卸掉重装备login服务中跟JWT、OAuth2、Security相关的依赖和配置统统删除——让专业的人Gateway干专业的事2 轻轻松松拿用户信息以前login服务要自己解析token现在直接看请求头就行了GetMapping(/userinfo)publicMapString,ObjectgetUserInfo(RequestHeader(valueX-Username,requiredfalse)Stringusername){MapString,ObjectuserInfonewHashMap();if(username!null){userInfo.put(username,username);}else{thrownewRuntimeException(未认证缺少用户信息);}returnuserInfo;}四、验收时刻来 现在测试一下 密码模式和授权码模式看Network里所有请求都指向 8100 端口——Gateway门卫大爷完美接客大功告成 Gateway服务搭建完毕微服务门卫化改造圆满完成