关键词:
导读:上篇文章我们已经抽取出了单独的认证服务,本章主要内容是让SpringCloud Gateway 集成Oauth2。
概念部分
在网关集成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网关访问后端服务
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-gateway
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://xx.0.xx.xx:3306/oauth2_config?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: xxxxx
password: xxxxxxx
driver-class-name: com.mysql.jdbc.Driver
主要配置oauth2的数据库连接地址
自定义认证接口管理类
在webFlux环境下通过实现 ReactiveAuthenticationManager
接口 自定义认证接口管理,由于我们的token是存在jdbc中所以命名上就叫ReactiveJdbcAuthenticationManager
@Slf4j
public class ReactiveJdbcAuthenticationManager implements ReactiveAuthenticationManager
private TokenStore tokenStore;
public JdbcAuthenticationManager(TokenStore tokenStore)
this.tokenStore = tokenStore;
@Override
public Mono<Authentication> authenticate(Authentication authentication)
return Mono.justOrEmpty(authentication)
.filter(a -> a instanceof BearerTokenAuthenticationToken)
.cast(BearerTokenAuthenticationToken.class)
.map(BearerTokenAuthenticationToken::getToken)
.flatMap((accessToken ->
log.info("accessToken is :",accessToken);
OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);
//根据access_token从数据库获取不到OAuth2AccessToken
if(oAuth2AccessToken == null)
return Mono.error(new InvalidTokenException("invalid access token,please check"));
else if(oAuth2AccessToken.isExpired())
return Mono.error(new InvalidTokenException("access token has expired,please reacquire token"));
OAuth2Authentication oAuth2Authentication =this.tokenStore.readAuthentication(accessToken);
if(oAuth2Authentication == null)
return Mono.error(new InvalidTokenException("Access Token 无效!"));
else
return Mono.just(oAuth2Authentication);
)).cast(Authentication.class);
网关层的安全配置
@Configuration
public class SecurityConfig
private static final String MAX_AGE = "18000L";
@Autowired
private DataSource dataSource;
@Autowired
private AccessManager accessManager;
/**
* 跨域配置
*/
public WebFilter corsFilter()
return (ServerWebExchange ctx, WebFilterChain chain) ->
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request))
HttpHeaders requestHeaders = request.getHeaders();
ServerHttpResponse response = ctx.getResponse();
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
HttpHeaders headers = 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);
return Mono.empty();
return chain.filter(ctx);
;
@Bean
SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception
//token管理器
ReactiveAuthenticationManager tokenAuthenticationManager = new ReactiveJdbcAuthenticationManager(new JdbcTokenStore(dataSource));
//认证过滤器
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(tokenAuthenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());
http
.httpBasic().disable()
.csrf().disable()
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.anyExchange().access(accessManager)
.and()
// 跨域过滤器
.addFilterAt(corsFilter(), SecurityWebFiltersOrder.CORS)
//oauth2认证过滤器
.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);
return http.build();
这个类是SpringCloug Gateway 与 Oauth2整合的关键,通过构建认证过滤器 AuthenticationWebFilter
完成Oauth2.0的token校验。
AuthenticationWebFilter
通过我们自定义的 ReactiveJdbcAuthenticationManager
完成token校验。
我们在这里还加入了CORS
过滤器,以及权限管理器 AccessManager
权限管理器
@Slf4j
@Component
public class AccessManager implements ReactiveAuthorizationManager<AuthorizationContext>
private Set<String> permitAll = new ConcurrentHashSet<>();
private static final AntPathMatcher antPathMatcher = new AntPathMatcher();
public AccessManager ()
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");
/**
* 实现权限验证判断
*/
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext)
ServerWebExchange exchange = authorizationContext.getExchange();
//请求资源
String requestPath = exchange.getRequest().getURI().getPath();
// 是否直接放行
if (permitAll(requestPath))
return Mono.just(new AuthorizationDecision(true));
return authenticationMono.map(auth ->
return new AuthorizationDecision(checkAuthorities(exchange, auth, requestPath));
).defaultIfEmpty(new AuthorizationDecision(false));
/**
* 校验是否属于静态资源
* @param requestPath 请求路径
* @return
*/
private boolean permitAll(String requestPath)
return permitAll.stream()
.filter(r -> antPathMatcher.match(r, requestPath)).findFirst().isPresent();
//权限校验
private boolean checkAuthorities(ServerWebExchange exchange, Authentication auth, String requestPath)
if(auth instanceof OAuth2Authentication)
OAuth2Authentication athentication = (OAuth2Authentication) auth;
String clientId = athentication.getOAuth2Request().getClientId();
log.info("clientId is ",clientId);
Object principal = auth.getPrincipal();
log.info("用户信息:",principal.toString());
return true;
主要是过滤掉静态资源,将来一些接口权限校验也可以放在这里。
测试
-
通过网关调用auth-service获取 access_token
-
在Header上添加认证访问后端服务
-
网关过滤器进行token校验
-
权限管理器校验
-
去认证服务器校验当前用户
-
返回正常结果
-
故意写错access_token,返回错误响应
-
请求头上去掉access_token,直接返回
401 Unauthorized
总结
通过以上几步我们将SpringCloud Gateway整合好了Oauth2.0,这样我们整个项目也基本完成了,后面几期再来对项目进行优化,欢迎持续关注。
SpringCloud Alibaba 系列文章
springcloudalibaba微服务实战三十四-隐私接口禁止外部访问(代码片段)
大家好,我是飘渺!在这个系列文章中曾经介绍过在SpringCloud体系下如何防止前端请求绕过网关直接到达后端微服务,今天我们要反其道而行之,介绍在SpringCloud体系中如何防止内部隐私接口被网关调用。看到这里... 查看详情
汇总
个人感觉这是全网比较齐全,写的比较好的SpringCloudalibaba系列教程了,推荐给大家!SpringCloudAlibaba微服务实战一-基础环境准备SpringCloudAlibaba微服务实战二-服务注册SpringCloudAlibaba微服务实战三-服务调用SpringCloudAlibaba微... 查看详情
汇总
个人感觉这是全网比较齐全,写的比较好的SpringCloudalibaba系列教程了,推荐给大家!SpringCloudAlibaba微服务实战一-基础环境准备SpringCloudAlibaba微服务实战二-服务注册SpringCloudAlibaba微服务实战三-服务调用SpringCloudAlibaba微... 查看详情
springcloudalibaba微服务实战教程系列
一、应用系列 Docker安装MySql完整教程、实操 使用到的mysql数据库的安装方案。 Docker安装AlibabaNacos教程 docker安装单实例或集群的Nacos的注册中心方便快速开始。 实现Nacos服... 查看详情
springcloudalibaba微服务实战二-服务注册
导读:在之前一篇文章中我们准备好了基于SpringCloudAlibaba的基础组件,本期主要内容是将所有的服务注册进Nacos,并让account-service和product-service能对外提供基础的增删改查能力。基础框架搭建在你的IDEA中建立一个多模块的项目(... 查看详情
springcloudalibaba微服务实战一-基础环境准备(代码片段)
SpringcloudAibaba现在这么火,我一直想写个基于SpringcloudAlibaba一步一步构建微服务架构的系列博客,终于下定决心从今天开始本系列文章的第一篇-基础环境准备。该系列文章内容主要基于三个微服务:用户服务AccountService,订单服... 查看详情
springcloudalibaba微服务实战二十一-jwt增强
今天内容主要是解决一位粉丝提的问题:如何在jwt中添加用户的额外信息并在资源服务器中获取这些数据。涉及的知识点有以下三个:如何在返回的jwt中添加自定义数据如何在jwt中添加用户的额外数据,比如用户id、手机号码如... 查看详情
springcloudalibaba微服务实战三十五-退出登录注销jwttoken(代码片段)
大家好,我是飘渺。有一个看我SpringCloudalibaba系列文章的粉丝私下问我:如何处理jwt失效的问题?修改密码或退出登录后需要将之前的jwttoken失效掉,不允许使用旧token登录系统。我说:很简单呀,咱们直... 查看详情
springcloudalibaba微服务实战三十七-oauth2自定义登录接口
...给大家送书了~)有不少人私下问我,为什么SpringCloudalibaba实战系列不更新了,主要是因为大部分核心功能都已经讲完了,剩下的基本是属于业务功能开发了,需要根据实际业务扩展。今天更新文章的原因... 查看详情
springcloudalibaba微服务实战三十七-oauth2自定义登录接口(代码片段)
大家好,我是飘渺。有不少人私下问我,为什么SpringCloudalibaba实战系列不更新了,主要是因为大部分核心功能都已经讲完了,剩下的基本是属于业务功能开发了,需要根据实际业务扩展。今天更新文章的原因是粉丝提了个问题:... 查看详情
springcloudalibaba微服务实战三十七-oauth2自定义登录接口(代码片段)
大家好,我是飘渺。有不少人私下问我,为什么SpringCloudalibaba实战系列不更新了,主要是因为大部分核心功能都已经讲完了,剩下的基本是属于业务功能开发了,需要根据实际业务扩展。今天更新文章的原因是粉丝提了个问题:... 查看详情
springcloudalibaba微服务实战二十二-整合dubbo
概述在SpringCloud构建的微服务系统中,大多数的开发者使用都是官方提供的Feign组件来进行内部服务通信,这种声明式的HTTP客户端使用起来非常的简洁、方便、优雅,但是有一点,在使用Feign消费服务的时候,相比较Dubbo这种RPC框... 查看详情
微服务实战:选择微服务部署策略
微服务实战(一):微服务架构的优势与不足微服务实战(二):使用APIGateway微服务实战(三):深入微服务架构的进程间通信微服务实战(四):服务发现的可行方案以及实践案例微服务实践(五):微服务的事件驱动... 查看详情
chrisrichardson微服务实战系列
微服务实战(一):微服务架构的优势与不足微服务实战(二):使用APIGateway微服务实战(三):深入微服务架构的进程间通信微服务实战(四):服务发现的可行方案以及实践案例微服务实践(五):微服务的事件驱动数据... 查看详情
3.20go微服务实战(微服务实战)---日志和监控
第20章 日志和监控20.1 日志实践20.2 指标 20.2.1 指标数据类型 20.2.2 命名约定 20.2.3 存储和查询 20.2.4 Grafana20.3 日志记录 20.3.1 具有关联ID的分布式跟踪 20.3.2 ElasticSearch、Logstash和Kibana 20.3.3 Kibana 20.4 异常 查看详情
3.20go微服务实战(微服务实战)---日志和监控
第20章 日志和监控20.1 日志实践20.2 指标 20.2.1 指标数据类型 20.2.2 命名约定 20.2.3 存储和查询 20.2.4 Grafana20.3 日志记录 20.3.1 具有关联ID的分布式跟踪 20.3.2 ElasticSearch、Logstash和Kibana 20.3.3 Kibana 20.4 异常 查看详情
3.21go微服务实战(微服务实战)---持续交付
第21章 持续交付21.1 持续交付简介 21.1.1 手动部署 21.1.2 持续交付的好处 21.1.3 持续交付面面观 21.1.4 持续交付的过程21.2 容器编排的选项和基础架构21.3 Terraform 21.3.1 提供者 21.3.2 Terraform配置入口点 21.3.3 VPC模块 21.3.... 查看详情
3.21go微服务实战(微服务实战)---持续交付
第21章 持续交付21.1 持续交付简介 21.1.1 手动部署 21.1.2 持续交付的好处 21.1.3 持续交付面面观 21.1.4 持续交付的过程21.2 容器编排的选项和基础架构21.3 Terraform 21.3.1 提供者 21.3.2 Terraform配置入口点 21.3.3 VPC模块 21.3.... 查看详情