SpringCloud Alibaba微服务实战十四 - SpringCloud Gateway集成Oauth2.0

简介: 导读:上篇文章我们已经抽取出了单独的认证服务,本章主要内容是让SpringCloud Gateway 集成Oauth2。

概念部分


1.png


在网关集成Oauth2.0后,我们的流程架构如上。主要逻辑如下:


1、客户端应用通过api网关请求认证服务器获取access_token http://localhost:8090/auth-service/oauth/token


2、认证服务器返回access_token


{
"access_token": "f938d0c1-9633-460d-acdd-f0693a6b5f4c",
"token_type": "bearer",
"refresh_token": "4baea735-3c0d-4dfd-b826-91c6772a0962",
"expires_in": 43199,
"scope": "web"}


3、客户端携带access_token通过API网关访问后端服务

2.png


4、API网关收到access_token后通过 AuthenticationWebFilter 对access_token认证


5、API网关转发后端请求,后端服务请求Oauth2认证服务器获取当前用户


在前面文章中我们搭建好了单独的Oauth2认证授权服务,基本功能框架都实现了,这次主要是来实现第四条,SpringCloud 整合 Oauth2 后如何进行access_token过滤校验。


代码示例

引入组件


<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-resource-server</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>


主要引入跟oauth2相关的jar包,这里还需要引入数据库相关的jar包,因为我们的token是存在数据库中,要想在网关层校验token的有效性必须先从数据库取出token。


bootstrap.yml 配置修改


spring:
application:
name: cloud-gatewaydatasource:
type: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql://xx.0.xx.xx:3306/oauth2_config?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=falseusername: xxxxxpassword: xxxxxxxdriver-class-name: com.mysql.jdbc.Driver


主要配置oauth2的数据库连接地址


自定义认证接口管理类


在webFlux环境下通过实现 ReactiveAuthenticationManager 接口 自定义认证接口管理,由于我们的token是存在jdbc中所以命名上就叫ReactiveJdbcAuthenticationManager


@Slf4jpublicclassReactiveJdbcAuthenticationManagerimplementsReactiveAuthenticationManager {
privateTokenStoretokenStore;
publicJdbcAuthenticationManager(TokenStoretokenStore){
this.tokenStore=tokenStore;
    }
@OverridepublicMono<Authentication>authenticate(Authenticationauthentication) {
returnMono.justOrEmpty(authentication)
                .filter(a->ainstanceofBearerTokenAuthenticationToken)
                .cast(BearerTokenAuthenticationToken.class)
                .map(BearerTokenAuthenticationToken::getToken)
                .flatMap((accessToken->{
log.info("accessToken is :{}",accessToken);
OAuth2AccessTokenoAuth2AccessToken=this.tokenStore.readAccessToken(accessToken);
//根据access_token从数据库获取不到OAuth2AccessTokenif(oAuth2AccessToken==null){
returnMono.error(newInvalidTokenException("invalid access token,please check"));
                    }elseif(oAuth2AccessToken.isExpired()){
returnMono.error(newInvalidTokenException("access token has expired,please reacquire token"));
                    }
OAuth2AuthenticationoAuth2Authentication=this.tokenStore.readAuthentication(accessToken);
if(oAuth2Authentication==null){
returnMono.error(newInvalidTokenException("Access Token 无效!"));
                    }else {
returnMono.just(oAuth2Authentication);
                    }
                })).cast(Authentication.class);
    }
}


网关层的安全配置


@ConfigurationpublicclassSecurityConfig {
privatestaticfinalStringMAX_AGE="18000L";
@AutowiredprivateDataSourcedataSource;
@AutowiredprivateAccessManageraccessManager;
/*** 跨域配置*/publicWebFiltercorsFilter() {
return (ServerWebExchangectx, WebFilterChainchain) -> {
ServerHttpRequestrequest=ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
HttpHeadersrequestHeaders=request.getHeaders();
ServerHttpResponseresponse=ctx.getResponse();
HttpMethodrequestMethod=requestHeaders.getAccessControlRequestMethod();
HttpHeadersheaders=response.getHeaders();
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
if (requestMethod!=null) {
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
                }
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
if (request.getMethod() ==HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
returnMono.empty();
                }
            }
returnchain.filter(ctx);
        };
    }
@BeanSecurityWebFilterChainwebFluxSecurityFilterChain(ServerHttpSecurityhttp) throwsException{
//token管理器ReactiveAuthenticationManagertokenAuthenticationManager=newReactiveJdbcAuthenticationManager(newJdbcTokenStore(dataSource));
//认证过滤器AuthenticationWebFilterauthenticationWebFilter=newAuthenticationWebFilter(tokenAuthenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(newServerBearerTokenAuthenticationConverter());
http                .httpBasic().disable()
                .csrf().disable()
                .authorizeExchange()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange().access(accessManager)
                .and()
// 跨域过滤器                .addFilterAt(corsFilter(), SecurityWebFiltersOrder.CORS)
//oauth2认证过滤器                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);
returnhttp.build();
    }
}


这个类是SpringCloug Gateway 与 Oauth2整合的关键,通过构建认证过滤器 AuthenticationWebFilter 完成Oauth2.0的token校验。


AuthenticationWebFilter 通过我们自定义的 ReactiveJdbcAuthenticationManager 完成token校验。

我们在这里还加入了CORS过滤器,以及权限管理器 AccessManager


权限管理器


@Slf4j@ComponentpublicclassAccessManagerimplementsReactiveAuthorizationManager<AuthorizationContext> {
privateSet<String>permitAll=newConcurrentHashSet<>();
privatestaticfinalAntPathMatcherantPathMatcher=newAntPathMatcher();
publicAccessManager (){
permitAll.add("/");
permitAll.add("/error");
permitAll.add("/favicon.ico");
permitAll.add("/**/v2/api-docs/**");
permitAll.add("/**/swagger-resources/**");
permitAll.add("/webjars/**");
permitAll.add("/doc.html");
permitAll.add("/swagger-ui.html");
permitAll.add("/**/oauth/**");
permitAll.add("/**/current/get");
    }
/*** 实现权限验证判断*/@OverridepublicMono<AuthorizationDecision>check(Mono<Authentication>authenticationMono, AuthorizationContextauthorizationContext) {
ServerWebExchangeexchange=authorizationContext.getExchange();
//请求资源StringrequestPath=exchange.getRequest().getURI().getPath();
// 是否直接放行if (permitAll(requestPath)) {
returnMono.just(newAuthorizationDecision(true));
        }
returnauthenticationMono.map(auth-> {
returnnewAuthorizationDecision(checkAuthorities(exchange, auth, requestPath));
        }).defaultIfEmpty(newAuthorizationDecision(false));
    }
/*** 校验是否属于静态资源* @param requestPath 请求路径* @return*/privatebooleanpermitAll(StringrequestPath) {
returnpermitAll.stream()
                .filter(r->antPathMatcher.match(r, requestPath)).findFirst().isPresent();
    }
//权限校验privatebooleancheckAuthorities(ServerWebExchangeexchange, Authenticationauth, StringrequestPath) {
if(authinstanceofOAuth2Authentication){
OAuth2Authenticationathentication= (OAuth2Authentication) auth;
StringclientId=athentication.getOAuth2Request().getClientId();
log.info("clientId is {}",clientId);
        }
Objectprincipal=auth.getPrincipal();
log.info("用户信息:{}",principal.toString());
returntrue;
    }
}


主要是过滤掉静态资源,将来一些接口权限校验也可以放在这里。


测试


  • 通过网关调用auth-service获取 access_token

3.png


  • 在Header上添加认证访问后端服务

4.png


  • 网关过滤器进行token校验


5.png


  • 权限管理器校验

6.png


  • 去认证服务器校验当前用户

7.png


  • 返回正常结果
  • 8.png


  • 故意写错access_token,返回错误响应

9.png


  • 请求头上去掉access_token,直接返回401 Unauthorized


10.png


总结


通过以上几步我们将SpringCloud Gateway整合好了Oauth2.0,这样我们整个项目也基本完成了,后面几期再来对项目进行优化,欢迎持续关注。

目录
相关文章
|
3月前
|
算法 Java 微服务
【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
微服务架构是What?? 微服务架构是一种架构模式,它提出将单一应用程序划分为一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。 每个服务允许在其独立的进程中,服务于服务间采用轻量级的通信机制互相协作(通常是Http协议的RESTful API或RPC协议)。 每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据上下文,选择合适的语言、工具对其进行构建
548 126
|
3月前
|
负载均衡 算法 Java
【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
1. 什么是服务治理? SpringCloud封装了Netfix开发的Eureka模块来实现服务治理 在传统pc的远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册
338 0
|
5月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1001 3
|
3月前
|
Cloud Native Serverless API
微服务架构实战指南:从单体应用到云原生的蜕变之路
🌟蒋星熠Jaxonic,代码为舟的星际旅人。深耕微服务架构,擅以DDD拆分服务、构建高可用通信与治理体系。分享从单体到云原生的实战经验,探索技术演进的无限可能。
微服务架构实战指南:从单体应用到云原生的蜕变之路
|
3月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
3月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
7月前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
487 102
|
5月前
|
负载均衡 监控 Java
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
在微服务架构中,高可用与稳定性至关重要。本文详解熔断、限流与负载均衡三大关键技术,结合API网关与Hystrix-Go实战,帮助构建健壮、弹性的微服务系统。
631 1
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
|
4月前
|
监控 安全 Java
Spring Cloud 微服务治理技术详解与实践指南
本文档全面介绍 Spring Cloud 微服务治理框架的核心组件、架构设计和实践应用。作为 Spring 生态系统中构建分布式系统的标准工具箱,Spring Cloud 提供了一套完整的微服务解决方案,涵盖服务发现、配置管理、负载均衡、熔断器等关键功能。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
286 1
|
10月前
|
NoSQL MongoDB 微服务
微服务——MongoDB实战演练——文章评论的基本增删改查
本节介绍了文章评论的基本增删改查功能实现。首先,在`cn.itcast.article.dao`包下创建数据访问接口`CommentRepository`,继承`MongoRepository`以支持MongoDB操作。接着,在`cn.itcast.article.service`包下创建业务逻辑类`CommentService`,通过注入`CommentRepository`实现保存、更新、删除及查询评论的功能。最后,新建Junit测试类`CommentServiceTest`,对保存和查询功能进行测试,并展示测试结果截图,验证功能的正确性。
248 2