使用 OAuth 2 和 JWT 为微服务提供安全保障
2018-01-08 16:27
399 查看
转载:https://segmentfault.com/a/1190000009164779
Part 1 - 理论相关
作者 freewolf关键词
微服务、
Spring Cloud、
OAuth 2.0、
JWT、
Spring Security、
SSO、
UAA
写在前面
作为从业了十多年的IT行业和程序的老司机,今天如果你说你不懂微服务,都不好意思说自己的做软件的。SOA喊了多年,无人不知,但又有多少系统开发真正的SOA了呢?但是好像一夜之间所有人都投入了微服务的怀抱。作为目前最主流的“微服务框架”,Spring Cloud发展速度很快,成为了最全面的微服务解决方案。不管什么软件体系,什么框架,安全永远是不可能绕开的话题,我也把它作为我最近一段时间研究微服务的开篇。
老话题!“如何才能在微服务体系中保证安全?”,为了达成目标,这里采用一个简单而可行方式来保护
Spring Cloud中服务的安全,也就是建立统一的用户授权中心。
这里补充说一下什么是
Authentication(认证)和
Authorization(鉴权),其实很简单,认证关心你是谁,鉴权关心你能干什么。举个大家一致都再说的例子,如果你去机场乘机,你持有的护照代表你的身份,这是认证,你的机票就是你的权限,你能干什么。
学习微服务并不是一个简单的探索过程,这不得学习很多新的知识,其实不管是按照DDD(Domain Driven Design)领域驱动设计中领域模型的方式,还是将微服务拆分成更小的粒度。都会遇到很多新的问题和以前一直都没解决很好的问题。随着不断的思考,随着熟悉
Facebook/GitHub/AWS这些机构是如何保护内部资源,答案也逐渐浮出水面。
为了高效的实现这个目标,这里采用
OAuth 2和
JWT(JSON Web Tokens)技术作为解决方案,
为什么使用OAuth 2
尽管微服务在现代软件开发中还算一个新鲜事物,但是OAuth 2已经是一个广泛使用的授权技术,它让Web开发者在自己提供服务中,用一种安全的方式直访问
Google/Facebook/GitHub平台用户信息。但在我开始阐述细节之前,我将揭开聚焦到本文真正的主题:
云安全
那么在云服务中对用户访问资源的控制,我们一般都怎么做呢?然我举一些大家似乎都用过的但又不是很完美的例子。
我们可以设置边界服务器或者带认证功能的反向代理服务器,假设所有访问请求都发给它。通过认证后,转发给内部相应的服务器。一般在
Spring MVC Security开发中几乎都会这样做的。但这并不安全,最重要的是,一旦是有人从内部攻击,你的数据毫无安全性。
其他方式:我们为所有服务建立统一的权限数据库,并在每次请求前对用户进行鉴权,听起来某些方面的确有点愚蠢,但实际上这确实是一个可行的安全方案。
更好的方式: 用户通过授权服务来实现鉴权,把用户访问Session映射成一个
Token。所有远程访问资源服务器相关的API必须提供
Token。然后资源服务器访问授权服务来识别
Token,得知
Token属于哪个用户,并了解通过这个Token可以访问什么资源。
这听起来是个不错的方案,对不?但是如何保证
Token的安全传输?如何区分是用户访问还是其他服务访问?这肯定是我们关心的问题。
所以上述种种问题让我们选择使用
OAuth 2,其实访问
Facebook/Google的敏感数据和访问我们自己后端受保护数据没什么区别,并且他们已经使用这样的解决方案很多年,我们只要遵循这些方法就好了。
OAuth 2
是如何工作的
如果你了解OAuth 2相关的原理,那么在部署
OAuth 2是非常容易的。
让我们描述下这样一个场景,“
某App希望获得
Tom在
OAuth 2 在整个流程中有四种角色:
资源拥有者(Resource Owner) - 这里是Tom
资源服务器(Resource Server) - 这里是Facebook
授权服务器(Authorization Server) - 这里当然还是Facebook,因为Facebook有相关数据
客户端(Client) - 这里是某App
当
Tom试图登录
某App将他重定向到
Tom登录成功,并且许可自己的Email和个人信息被
某App获取。这两个资源被定义成一个
Scope(权限范围),一旦准许,
某App的开发者就可以申请访问权限范围中定义的这两个资源。
+--------+ +---------------+ | |--(A)- Authorization Request ->| Resource | | | | Owner | | |<-(B)-- Authorization Grant ---| | | | +---------------+ | | | | +---------------+ | |--(C)-- Authorization Grant -->| Authorization | | Client | | Server | | |<-(D)----- Access Token -------| | | | +---------------+ | | | | +---------------+ | |--(E)----- Access Token ------>| Resource | | | | Server | | |<-(F)--- Protected Resource ---| | +--------+ +---------------+
Tom允许了权限请求,再次通过重定向返回
某App,重定向返回时携带了一个
Access Token(访问令牌),接下来
某App就可以通过这个
Access Token从
Tom相关的鉴权。而且每当
Tom登录了
某App,都可以通过之前获得的
Access Token,直接获取相关授权资源。
到目前为止,我们如何直接将以上内容用于实际的例子中?
OAuth 2十分友好,并容易部署,所有交互都是关于客户端和权限范围的。
OAuth 2中的
客户端和
权限范围和我们平时的用户和权限是否相同?
我需要将授权映射到权限范围中或将用户映射到客户端中?
为什么我需要客户端?
你也许在之前在类似的企业级开发案例中尝试映射过相关的角色。这会很棘手!
任何类型的应用都提供用户登录,登录结果是一个
Access Token,所有的之后的API调用都将这个
Access Token加入HTTP请求头中,被调用服务去授权服务器验证
Access Token并获取该Token可访问的权限信息。这样一来,所有服务的访问都会请求另外的服务来完成鉴权。
权限范围和角色,客户端和用户
在OAuth 2中,你可以定义哪个应用(网站、移动客户端、桌面应用、其他)可以访问那些资源。这里只有一个尺寸,来自哪里的哪个用户可以访问那些数据,当然也是哪个应用或者服务可以访问那些资源。换一种说法,权限范围就是控制那些端点对客户端可见,或者用户根据他的权限来获取相关的数据。
在一个在线商店中,前端可以看做一个客户端,可以访问商品、订单和客户信息,但后端可以关于物流和合同等,另一方面,用户可以访问一个服务但并不是全部的数据,这可以是因为用户正在使用Web应用,当他不能的时候,其他用户却可以。服务之间的访问时我们要讨论的另一个维度。如果你熟悉数学,我可以说在
OAuth 2中,客户端-权限范围关系是线性独立于用户-权限关系。
为什么是JWT
OAuth 2并不关心去哪找
Access Token和把它存在什么地方的,生成随机字符串并保存
Token相关的数据到这些字符串中保存好。通过一个令牌端点,其他服务可能会关心这个
Token是否有效,它可以通过哪些权限。这就是用户信息URL方法,授权服务器为了获取用户信息转换为资源服务器。
当我们谈及微服务时,我们需要找一个
Token存储的方式,来保证授权服务器可以被水平扩展,尽管这是一个很复杂的任务。所有访问微服务资源的请求都在Http Header中携带
Token,被访问的服务接下来再去请求授权服务器验证
Token的有效性,目前这种方式,我们需要两次或者更多次的请求,但这是为了安全性也没什么其他办法。但扩展
Token存储会很大影响我们系统的可扩展性,这是我们引入
JWT(读jot)的原因。
+-----------+ +-------------+ | | 1-Request Authorization | | | |------------------------------------>| | | | grant_type&username&password | |--+ | | |Authorization| | 2-Gen | Client | |Service | | JWT | | 3-Response Authorization | |<-+ | |<------------------------------------| Private Key | | | access_token / refresh_token | | | | token_type / expire_in / jti | | +-----------+ +-------------+
简短来说,响应一个用户请求时,将用户信息和授权范围序列化后放入一个JSON字符串,然后使用Base64进行编码,最终在授权服务器用
私钥对这个字符串进行签名,得到一个
JSON Web Token,我们可以像使用
Access Token一样的直接使用它,假设其他所有的资源服务器都将持有一个RSA公钥。当资源服务器接收到这个在Http Header中存有
Token的请求,资源服务器就可以拿到这个
Token,并验证它是否使用正确的私钥签名(是否经过授权服务器签名,也就是
验签)。
验签通过,反序列化后就拿到
OAuth 2的验证信息。
验证服务器返回的信息可以是以下内容:
access_token - 访问令牌,用于资源访问
refresh_token - 当访问令牌失效,使用这个令牌重新获取访问令牌
token_type - 令牌类型,这里是
Bearer也就是基本HTTP认证
expire_in - 过期时间
jti - JWT ID
由于
Access Token是
Base64编码,反编码后就是下面的格式,标准的JWT格式。也就是
Header、
Payload、
Signature三部分。
{ "alg":"RS256", "typ":"JWT" } { "exp": 1492873315, "user_name": "reader", "authorities": [ "AURH_READ" ], "jti": "8f2d40eb-0d75-44df-a8cc-8c37320e3548", "client_id": "web_app", "scope": [ "FOO" ] } &:lƧs)ۡ-[+ F"2"Kآ8ۓٞ:u9ٴ̯ޡ 9Q32Zƌ$ec{3mxJh0DF庖[!뀭N)㥔knVVĖV|夻ׄE㍫}Ŝf9>'<蕱굤Bۋеϵov虀DӨ8C4K}Emޢ YVcaqIW&*uʝub!*Ť\՟-{ʖX܌WTq
使用JWT可以简单的传输
Token,用
RSA签名保证
Token很难被伪造。
Access Token字符串中包含用户信息和权限范围,我们所需的全部信息都有了,所以不需要维护
Token存储,资源服务器也不必要求
Token检查。
+-----------+ +-----------+ | | 1-Request Resource | | | |----------------------------------->| | | | Authorization: bearer Access Token | |--+ | | | Resource | | 2-Verify | Client | | Service | | Token | | 3-Response Resource | |<-+ | |<-----------------------------------| Public Key| | | | | +-----------+ +-----------+
所以,在微服务中使用
OAuth 2,不会影响到整体架构的可扩展性。淡然这里还有一些问题没有涉及,例如
Access Token过期后,使用
Refresh Token到认证服务器重新获取
Access Token等,后面会有具体的例子来展开讨论这些问题。
如果您感兴趣,后面还会有实现部分,敬请期待~
由于 http://asciiflow.com/ 流程图使用中文就无法对齐了,本文中流程图都是英文了~
相关文章推荐
- 使用 OAuth 2 和 JWT 为微服务提供安全保障
- 使用 OAuth 2 和 JWT 为微服务提供安全保障
- 使用 OAuth 2 和 JWT 为微服务提供安全保障
- 为使用rpm安装的httpd服务提供基于ssl的安全会话
- 使用 OpenSSL API 进行安全编程,第 3 部分: 提供安全服务 OpenSSL 提供必要的能力
- 使用 OpenSSL API 进行安全编程,第 3 部分: 提供安全服务
- 使用 OpenSSL API 进行安全编程,第 3 部分: 提供安全--服务
- 使用 OpenSSL API 进行安全编程,第 3 部分: 提供安全服务
- 手机短信验证码服务,为个人信息安全提供保障
- 使用 OpenSSL API 进行安全编程,第 3 部分: 提供安全服务
- 手机短信验证服务,为个人信息安全提供保障
- 使用 OpenSSL API 进行安全编程,第 3 部分: 提供安全服务
- Linux 下使用 TCP 封装器来加强网络服务安全的技巧
- 内网架设WSUS 提供补丁服务 构架基础安全
- 上海远丰:主动服务提供信誉保障
- 使用dnsmasq提供DNS和DHCP服务
- 使用OAuth Server PHP实现OAuth2服务
- 为什么 FleaPHP 使用 Table Data Gateway 代替 Active Record 来提供数据库访问服务
- Ubuntu 搭建 NFS服务提供给 SC2440或SC6410开发板使用【要点整理】
- FleaPHP 使用 Table Data Gateway 代替 Active Record 来提供数据库访问服务