微服务架构下的安全设计方案

发布 : 2019-03-23 分类 : 后端 浏览 :

1 微服务安全设计原则

  微服务安全是在实际应用中的一个很普遍要求,安全主要关心调用者是谁调用者能干什么, 以及如何传播这个信息,也就是常说的服务间的认证和授权。

原则:

  • 单点登录:微服务架构下,实现用户只需要登录一次就能访问所有相互信任的应用系统。
  • 无状态:前后端分离,后端不保存用户Session,每次请求都要鉴权
  • 细粒度:每个组件管理自己的功能权限,需要实先确定好权限
  • 非浏览器客户端的操作性:需要考虑到那些非浏览器端的客户请求,对其提供良好的支持

2 微服务常见的认证方案

2.1 分布式Session

  传统的单体应用的session,在Spring cloud微服务架构下,可以采用分布式session机制,可以将用户的认证信息存储在共享存储(如redis)中,用户会话作为key实现简单的分布式哈希映射,当用户访问微服务时,用户数据可以从共享存储中获取。Spring Session对分布式Session提供了支持,也与Spring Boot/Cloud无缝集成。

2.2 API Tokens

  随着 Restful API微服务的兴起,基于 Token 的认证现在已经相当普遍了。

  Token一般会包含用户的相关信息,其它微服务可以从Token里提取出用户、权限等信息完成鉴权。

基于Token认证的典型流程:

image

  1. 用户使用包含用户名和密码的credential从客户端发起资源请求。
  2. 后端接受请求,通过授权中心,生产有效token字符串,返回给客户端。
  3. 客户端获得token后,再次发出资源请求。
  4. 后端接受带token的请求,通过授权中心,获取相关资源,返回给客户端。

优点:

  • 服务端无状态:服务端不需要存储Session,因为Token已携带用户的相关信息
  • 性能好:校验Token不需要访问远程服务或数据库
  • 支持移动端
  • 支持跨程序、跨域调用

缺点:

  • 每次用户请求需要携带有效token,与Auth服务进行交互验证
  • Auth服务可能需要处理大量的生产token的操作,可能存在性能问题

  基于Token的认证方案,业界推荐使用 JSON Web Tokens(JWT),它足够简单且支持程度也比较好

3 JWT

  JSON Web Tokens(JWT)是一种认证协议,是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 Token 也可直接被用于认证,也可被加密。

  授权服务器将用户信息和授权范围序列化后放入一个JSON字符串,然后使用Base64进行编码,最终在授权服务器用私钥对这个字符串进行签名,得到一个JSON Web Token

  假设其他所有的资源服务器都将持有一个RSA公钥,当资源服务器接收到这个在Http Header中存有Token的请求,资源服务器就可以拿到这个Token,并验证它是否使用正确的私钥签名(是否经过授权服务器签名,也就是验签)。验签通过,反序列化后就拿到Toekn中包含的有效验证信息。

3.1 JWT认证流程

image

  • 客户端调用登录接口(或者获取 token 接口),传入用户名密码。
  • 服务端请求身份认证中心,确认用户名密码正确。
  • 服务端创建 JWT,返回给客户端。
  • 客户端拿到 JWT,进行存储(可以存储在缓存中,也可以存储在数据库中,如果是浏览器,可以存储在 Cookie 中)在后续请求中,在 HTTP 请求头中加上 JWT。
  • 服务端校验 JWT,校验通过后,返回相关资源和数据。

3.2 JWT数据结构

image

  JWT 是由三段信息构成的,第一段为头部(Header),第二段为载荷(Payload),第三段为签名(Signature)。每一段内容都是一个 JSON 对象,将每一段 JSON 对象采用 BASE64 编码,将编码后的内容用. 链接一起就构成了 JWT 字符串。如下:

1
2
3

header.payload.signature

  1. 头部(header)

  头部用于描述关于该 JWT 的最基本的信息,例如其类型以及签名所用的算法等

  指明了类型为JWT,签名算法是 RS256算法的header

image

  1. 载荷(payload)

  载荷也是body,是存放有效信息的地方。有效信息包含以下内容:

image

声明项 描述
iss JWT 签发者
sub JWT 所面向的用户
aud 接收 JWT 的一方,如: 测试环境用account-d.docusign.com,正式环境用account.docusign.com
exp JWT 的过期时间,这个过期时间必须要大于签发时间
iat JWT 的签发时间
scope JWT 的唯一身份标识,主要用来作为一次性 token, 从而回避重放攻击。

除了上面的默认字段外,我们还可以自定义私有字段,如:

image

  1. 签名(signature)

  创建签名需要使用 Base64 编码后的 headerpayload 以及一个秘钥,将 base64 编码后的 headerbase64 编码后的 payload 使用.连接组成的字符串,通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 jwt 的第三部分。

image

示例:

1
rs256sign(<base64URL-encoded header>.<base64URL-encoded body>)

JWT 的优点:

  • 跨语言,JSON 的格式保证了跨语言的支撑
  • 基于 token,无状态
  • 占用字节小,便于传输

关于 token 注销:

  token 的注销,由于 token 不存储在服务端,由客户端存储,当用户注销时,token 的有效时间还没有到,还是有效的。所以如何在用户注销登录时让 token 注销是一个要关注的点。一般有如下几种方式:

  • token 存储在localStorage中,这样客户端注销时,自然可以清空掉
  • 注销时,将 token 存放到分布式缓存中,每次校验 token 时检查下该 token 是否已注销。不过这样也就失去了快速校验 token 的优点。
  • 采用短期令牌,比如token有效期是 20 分钟,这样可以一定程度上降低注销后 token 可用性的风险。

4 Oauth2

  OAuth是一个关于授权(authorization)的开放网络标准,在业界得到广泛应用,目前的版本是2.0版。

  简单来说就是客户端应用程序(通常是web浏览器)代表用户(得到了用户的批准)去访问受保护的资源

  举个例子,你想登录豆瓣去看看电影评论,但你从来没注册过豆瓣账号,又不想新注册一个再使用豆瓣,怎么办呢?不用担心,豆瓣已经为你这种懒人做了准备,用你的qq号可以授权给豆瓣进行登录,请看。

第一步:在豆瓣官网点击用qq登录

image

第二步:跳转到qq登录页面输入用户名密码,然后点授权并登录

image

第三步:跳回到豆瓣页面,成功登录

image

上帝视角

image

4.1 四个角色

  1. 资源拥有者(如用户)
  2. 授权服务器(Authorization Server)
  3. 资源服务器(Resource Server)
  4. 客户端(Client application)

4.2 运行流程

image

  1. 用户打开客户端以后,客户端要求用户给予授权
  2. 用户同意给予客户端授权
  3. 客户端使用上一步获得的授权,向认证服务器申请令牌
  4. 认证服务器对客户端进行认证以后,确认无误,同意发放令牌
  5. 客户端使用令牌,向资源服务器申请获取资源
  6. 资源服务器确认令牌无误,同意向客户端开放资源

4.3 客户端的授权模式

  客户端必须得到用户的授权(Authorization Grant),才能获得令牌(access token)。OAuth 2.0 定义了四种授权方式:authorization codeimplicitresource owner password credentialsclient credentials

4.3.1 授权码模式

  授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。流程如下:

  1. 用户访问客户端,后者将前者导向认证服务器。
  2. 用户选择是否给予客户端授权。
  3. 假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向 URI”(redirection URI),同时附上一个授权码。
  4. 客户端收到授权码,附上早先的”重定向 URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
  5. 认证服务器核对了授权码和重定向 URI,确认无误后,向客户端发送访问令牌(access token)更新令牌(refresh token)

例子:

4.3.2 简化模式

  简化模式(Implicit Grant Type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了”授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。流程如下:

  1. 客户端将用户导向认证服务器。
  2. 用户决定是否给于客户端授权。
  3. 假设用户给予授权,认证服务器将用户导向客户端指定的”重定向 URI”,并在 URI 的 Hash 部分包含了访问令牌。
  4. 浏览器向资源服务器发出请求,其中不包括上一步收到的 Hash 值。
  5. 资源服务器返回一个网页,其中包含的代码可以获取 Hash 值中的令牌。
  6. 浏览器执行上一步获得的脚本,提取出令牌。
  7. 浏览器将令牌发给客户端。

4.3.3 密码模式

  密码模式(Resource Owner Password Credentials)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向”服务商提供商”索要授权。在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。流程如下:

  1. 用户向客户端提供用户名和密码。
  2. 客户端将用户名和密码发给认证服务器,向后者请求令牌。
  3. 认证服务器确认无误后,向客户端提供访问令牌。

4.3.4 客户端模式

  客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向”服务提供商”进行认证。

  在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。流程如下:

  1. 客户端向认证服务器进行身份认证,并要求一个访问令牌。
  2. 认证服务器确认无误后,向客户端提供访问令牌。

5 Spring Cloud Security解决方案

原理:

image

5.1 Spring Cloud Security

  Spring Cloud Security包含了Spring SecuritySpring Security Oauth

  1. 基于OAuth2 和 OpenID协议的可配置的SSO登录机制
  2. 基于tokens保障资源访问安全
  3. 引入UAA(User Account and Authentication)鉴权服务,UAA是一个Web服务,用于管理账户、Oauth2客户端和用户用于鉴权的问题令牌(Issue Token)

  实现了Oauth 2授权框架和基于JWT(JSON web tokens)的问题令牌

  基于OAuth2,当用户访问客户端应用时,生成并发放token给目标客户端

主要内容:

  1. 认证对象:如用户、客户端以及目标资源服务器
  2. 认证类型:主要有授权码模式、密码模式以及客户端模式
  3. 认证范围:即认证权限,并作为一个命名的参数附加到AccessToken上

5.2 Spring Cloud Security实战

5.2.1 组件

image

主要组件:

  1. consul:提供服务注册与发现功能
  2. consul-gateway:基于Spring Cloud Gateway的网关,对外提供统一的api入口
  3. consul-auth:基于Spring Cloud Security的鉴权服务
  4. consul-provider:业务组件,资源服务器
  5. consul-consumer:业务组件,,资源服务器,基于openfeign消费consul-provider服务

consul-auth组件作为授权服务器,配置了客户端的认证类型和认证范围:

  1. 认证类型为authorization_codepassword两种
  2. 认证范围为readwrite

image

image

5.2.2 授权码模式

客户端使用authorization_code授权方式,步骤如下:

  1. 访问授权服务器请求授权http://localhost:8081/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com
  2. 授权服务器将浏览器重定向到登录页面:http://localhost:8081/login
  3. 用户输入用户名和密码,登录成功,允许访问受保护资源后,重定向到:https://www.baidu.com/?code=etlPQm,其中的code即授权码,后台可以根据这个授权码去换取access_tokenhttp://localhost:8000/api/oauth/token,其中/api/auth代表通过网关路由到consul-auth服务
1
2
3
4
5
6
7
8
9
10
11
12

HTTP Method: POST
http://localhost:8000/api/oauth/token
Authorization: Basic {BASE64-ENCODED client:secret}
Accept: application/json
Request:
{
"grant_type" : "authorization_code",
"code" : "9Vzv9t",
"redirect_uri" : "http://www.baidu.com"
}

image

  其中的Authorization请求头为Basic + client:secretBase64编码,编码后的值为:

1
2
3

Basic Y2xpZW50OnNlY3JldA==

  1. 获取到access_token之后就可以带上token访问授保护资源了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

HTTP Method: POST
localhost:8000/api/provider/sayHello?name=student
Authorization: Basic {BASE64-ENCODED client:secret}
Accept: application/json
Request:
{
"grant_type" : "authorization_code",
"code" : "9Vzv9t",
"redirect_uri" : "http://www.baidu.com"
}

Response:
hello, student

授权码是一次有效,用完之后就不能再次使用了

5.2.3 password模式

  客户端使用password授权方式,调用/api/auth/oauth/token接口获取access_token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

HTTP Method: POST
http://localhost:8000/api/auth/oauth/token
Authorization: Basic {BASE64-ENCODED client:secret}
Accept: application/json
Request:
{
"grant_type" : "password",
"scope" : "read",
"username" : "admin",
"password" : "11"
}

Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9....",
"expires_in": 3599,
"scope": "read",
"jti": "ef48fc8f-40c5-4eff-b224-347ab2cfa223"
}

image

  表单中包含了字段password(认证类型)和字段scope(认证范围),请求头包含Authorization授权,类型为Basic

  发出认证请求的返回结果,access_token为有效认证token,将来被其他请求使用

  获取access_token后,携带在Authorization请求头,格式为:Bearer + access_token,请求受保护的资源url:/api/user/sayHello?name=student

image

image

  业务组件鉴权(如@PreAuthorize):

image

5.2.4 简化模式

1
2
3
4

HTTP Method: GET
http://localhost:8081/oauth/authorize?client_id=client&response_type=token&redirect_uri=http://www.baidu.com

  其中response_type=token

  授权结束后,重定向到百度的url会携带access_tokenhttps://www.baidu.com/#access_token=eyJhbGciOiJSUzI1NiIsInR...

6 总结

  总的来说,微服务架构下,分布式SessionSpring Cloud Security都可以作为安全设计的方案,分布式Session虽然可以解决,但实现起来相对复杂,终究不是上策。Spring Cloud Security很好的结合了Oauth2.0JWT,加上简单易用,能有效地提高后端服务的可用性和扩展性,能很好的满足微服务架构的安全需求。      

7 参考资料

本文作者 : tangyi
原文链接 : http://ehedgehog.com/2019/03/23/微服务架构下的安全设计方案/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

知识 & 情怀 | 二者兼得

支付宝扫一扫, 向我投食

支付宝扫一扫, 向我投食

留下足迹