基于Token的权限认证服务

在这两年接触的项目基本上是基于前后端分离、多种后端服务组合而成的,这有别于先前的“单体”的项目,而且我们还需要考虑外部应用接入的场景、用户-服务的鉴权、服务-服务的鉴权等多种鉴权场景。

本方案基于客户端Token与网关结合的方式。所有的客户端请求都经过网关,网关将校验客户端发送上来的Token等信息,如果通过则转发给对应的服务,如果不通过则直接返回40X给客户端。

架构

auth-arch.png
  1. 获得authorize,用户/设备从UAA Service中获得authorize信息
  2. 附加mac token,请求在访问服务时候附加上mac token
  3. Gateway进行认证,Gateway将收到的每个mac token发送给UAA Service进行校验
  4. 每个服务只有权限去操作自己负责的那部分功能(待规划)

流程

auth-sequence.png
  1. 用户/客户端从UAA Service中获得Token
  2. 如果UAA Service中无此用户/客户端,则抛出AuthorizationException异常并返回401
  3. 如果UAA Service认证成功,返回对应的Token信息
  4. 用户的请求数据,请求头Authorization中带上Token信息
  5. 网关过滤所有的请求,携带Token、请求信息向UAA Service请求鉴权
  6. 如果UAA Service鉴权失败,抛出AuthenticationException异常并返回403
  7. 如果UAA Service鉴权成功,返回用户的权限等信息
  8. 网关向业务发起请求(此时,不携带授权信息。因为鉴权等都在网关处理了)
  9. 业务服务向微服务发起请求,同样也不携带授权信息
  10. 返回结果给用户

协议

根据开发阶段的鉴权、用户-服务的鉴权、服务-服务的鉴权等不同的场景,定义了三种方式的协议。

Debug Token协议

debug token适用于在开发测试环境调试API。

协议

在Client发出api请求之前,必须将debug token的信息放在HTTP Header的Authorization里面。

1
Authorization:DEBUG userid="123456",realm=""

上面的代码中:

userid为调试的用户

realm为可选字段,预定为用户所在的领域(应用、行业或者机构)

示例(伪代码)

1
2
3
4
GET /v0.1/resources HTTP/1.1
Host: a.dynamax.io
Accept: application/json
Authorization: DEBUG userid='',realm=''

Mac Token协议

mac token适用于不安全网络下的API授权

协议

通过“登录接口”可以获得Token。mac_token的数据结构如下:

1
2
3
4
5
6
7
8
{
"user_id":"", //用户标识
"access_token":"", //token标识
"expires_at":"", //本token的过期时间
"refresh_token":"", //用以续期
"mac_key":"", //hmac的密钥
"mac_algorithm":"hmac-sha-256" //hmac算法的名称
}

hmac算法见:[[https://tools.ietf.org/html/rfc2104]]

在Client发出api请求之前,必须将mac token的信息放在HTTP Header的Authorization里面。

1
Authorization:MAC id="",nonce="",mac=""

上面的代码中:

id为mac_token.access_token

nonce为 时间戳:随机码(客户端生成),有效时间+-5分钟

mac为请求签名: mac=base64(hmac(mac_token.mac_key,mac_token.mac_algorithm,request_content))

request_content = nonce + \n + http-method + \n + request-url + \n + host + \n

http-method,请求的方法,大写,如:GET

request-url,请求的地址(包含参数的部分,不包含域名部分),区分大小写,如/v0.1/databases

host,为HTTP Header中的host,区分大小写,如dynamax.io

示例(伪代码)

获得token

request

1
2
3
4
5
6
7
8
POST /v0.1/tokens HTTP/1.1
Host: api.uaa.dynamax.io
Accept: application/json
Content-Type: application/json
{
"username":"330134",
"password":"******"
}

reqponse

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 201 Created
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-Cache
{
"user_id":"",
"access_token":"",
"expires_at":"",
"refresh_token":"",
"mac_key":"",
"mac_algorithm":""
}

访问资源

1
2
3
4
GET /v0.1/resources HTTP/1.1
Host: resources.dynamax.io
Accept: application/json
Authorization:MAC id="adFeww3Fw4VV09876",nonce="1234234345343:adfasd32",mac="SDFS8weadfa42234"

Bearer Token协议

bearer token适用于安全网络下的api授权。也就是说,bearer适用于微服务之间的相互调用的api授权。

协议

可以通过“bearer_token”的接口获得相应的bearer_token,数据结构如下:

1
2
3
4
5
6
{
"user_id":"", //用户标识
"access_token":"" //token标识
"expires_at":"" //本token的过期时间
"refresh_token":"" //用以续期
}

在Client发出api请求之前,必须将bearer token的信息放在HTTP Header的Authorization里面。

1
Authorization:BEARER "XXX123XXXXX" user_id:""

上面的代码中:

“XXX123XXXXX”为bearer_token.access_token

user_id可选,该值位获得bearer token对应的账号。该值用来传递用户信息到其他服务端,用来判断是否有权限。

待优化的点

  • 怎么鉴定一个请求是否需要认证?白名单?
  • 各个业务服务以及微服务是否需要再次认证?会不会出现绕过网关的情况?
  • 所有的服务都通过网关,在UAA Service中进行鉴权,会不会造成UAA Service是个瓶颈点?
  • 是否能够很方便地扩展到OAuth2?