当前位置: 首页 > news >正文

Spring Cloud OAuth 实现微服务内部Token传递的源码解析

为什么80%的码农都做不了架构师?>>>   hot3.png

背景分析

  • 1.客户端携带认证中心发放的token,请求资源服务器A(Spring Security OAuth 发放Token 源码解析)

  • 2.客户端携带令牌直接访问资源服务器,资源服务器通过对token 的校验 (Spring Cloud OAuth2 资源服务器CheckToken 源码解析 ) 判断用户的合法性,并保存到上下文中

  • 3.A服务接口接收到请求,需要通过Feign或者其他RPC框架调用B服务来组装返回数据

本文主要来探讨第三部 A --> B ,token 自定维护的源码实现

如何实现token 传递

配置OAuth2FeignRequestInterceptor 即可

  • 此类是Feign 的拦截器实现

@Bean
@ConditionalOnProperty("security.oauth2.client.client-id")
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
														OAuth2ProtectedResourceDetails resource,) {
	return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);
}

源码解析

  • 获取上下文中的token ,组装到请求头
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
	// 给请求增加 token
	@Override
	public void apply(RequestTemplate template) {
		template.header(header, extract(tokenType));
	}
	
	protected String extract(String tokenType) {
		OAuth2AccessToken accessToken = getToken();
		return String.format("%s %s", tokenType, accessToken.getValue());
	}

	// 从spring security 上下文中获取token
	public OAuth2AccessToken getToken() {

		OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
		if (accessToken == null || accessToken.isExpired()) {
			try {
				accessToken = acquireAccessToken();
			}
		}
		return accessToken;
	}

}
  • 再来看AccessTokenContextRelay, 上下文token 中转器.非常简单从上下文获取认证信息得到把 token 放到上下文
public class AccessTokenContextRelay {

	private OAuth2ClientContext context;

	public AccessTokenContextRelay(OAuth2ClientContext context) {
		this.context = context;
	}
    
	public boolean copyToken() {
		if (context.getAccessToken() == null) {
			Authentication authentication = SecurityContextHolder.getContext()
					.getAuthentication();
			if (authentication != null) {
				Object details = authentication.getDetails();
				if (details instanceof OAuth2AuthenticationDetails) {
					OAuth2AuthenticationDetails holder = (OAuth2AuthenticationDetails) details;
					String token = holder.getTokenValue();
					DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(
							token);
					String tokenType = holder.getTokenType();
					if (tokenType != null) {
						accessToken.setTokenType(tokenType);
					}
					context.setAccessToken(accessToken);
					return true;
				}
			}
		}
		return false;
	}

}
  • 什么时候执行中转,oauth2 资源服务器非常简单暴力,加了个拦截器给转发。

源码非常简单

谈谈spring security oauth 实现的问题

  1. 当请求上下文没有Token,如果调用feign 会直接,这个OAuth2FeignRequestInterceptor 肯定会报错,因为上下文copy 失败
  2. 如果设置线程隔离,这里也会报错。导致安全上下问题传递不到子线程中。
  3. 强制使用拦截器去处理 token 转发到这里上下文,使用的业务场景只有这里,影响性能高

这三个问题,大家在使用的过程中一定会遇到

自定义OAuth2FeignRequestInterceptor

  • 通过外部条件是否执行token中转
public void apply(RequestTemplate template) {
	Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);
	if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
		return;
	}

	accessTokenContextRelay.copyToken();
	if (oAuth2ClientContext != null
		&& oAuth2ClientContext.getAccessToken() != null) {
		super.apply(template);
	}
}
  • 手动调用accessTokenContextRelay的copy,当然需要覆盖原生oauth 客户端的配置

总结

  • 以上源码参考个人项目 基于Spring Cloud、OAuth2.0开发基于Vue前后分离的开发平台
  • QQ: 2270033969 一起来聊聊你们是咋用 spring cloud 的吧。

欢迎关注我们获得更多的好玩JavaEE 实践

转载于:https://my.oschina.net/giegie/blog/3037888

相关文章:

  • Swift实现菜单的多选
  • 预防缓存击穿-布隆过滤器
  • Windows下PyQt4的安装
  • jsplumb 使用总结
  • PHP判断变量是否存在及函数isset() 、empty()与is_null的区别
  • [mysql]错误解决之Failed to start MySQL Server
  • CSS3 calc的用法详解
  • MySQL主从复制虽好,能完美解决数据库单点问题吗?
  • 声明25个长度的数组,通过键盘录入学生成绩,并把每个元素赋值为学生的分数成绩,输出结束后遍历输出。...
  • 妈妈走开一会儿
  • 需求的重要性续集
  • 《人月神话》2
  • 7年间减少4000万劳动力,中国企业该何去何从?
  • php图片转base64并保存为文本
  • B 站后端源码被恶意“开源” 6 小时,如何保证后台的安全!
  • (三)从jvm层面了解线程的启动和停止
  • 【刷算法】求1+2+3+...+n
  • 2017-09-12 前端日报
  • es6
  • JAVA多线程机制解析-volatilesynchronized
  • Laravel Mix运行时关于es2015报错解决方案
  • leetcode98. Validate Binary Search Tree
  • Redis 中的布隆过滤器
  • SOFAMosn配置模型
  • springMvc学习笔记(2)
  • vue 配置sass、scss全局变量
  • 代理模式
  • 工程优化暨babel升级小记
  • 少走弯路,给Java 1~5 年程序员的建议
  • 推荐一个React的管理后台框架
  • 应用生命周期终极 DevOps 工具包
  • 积累各种好的链接
  • ​Spring Boot 分片上传文件
  • # .NET Framework中使用命名管道进行进程间通信
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • #数学建模# 线性规划问题的Matlab求解
  • (10)STL算法之搜索(二) 二分查找
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (Git) gitignore基础使用
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (编译到47%失败)to be deleted
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (蓝桥杯每日一题)love
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • .chm格式文件如何阅读
  • .Net core 6.0 升8.0
  • .NET Core 项目指定SDK版本
  • .Net Core与存储过程(一)
  • .Net MVC + EF搭建学生管理系统
  • .NET 材料检测系统崩溃分析
  • .Net 垃圾回收机制原理(二)
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @Query中countQuery的介绍