BUG弄潮儿 2024-04-14 09:15 广东
「万事开头难,视频号500粉直播需要你的助力!你的支持是我前进的动力!」
来源:juejin.cn/post/7299736066423930914
概述
基于 OAuth2 建设 Open API 平台授权机制,通过安全标准的方式授权给外部,保证部门应用数据的安全性。
OAuth2 定义了4种授权方式,但目前只需要供客户端在后台调用即可,所以仅考虑凭证式授权方式。
设计方案
整体流程示意图:
下面按照上面提到的四大模块来进行分析:
客户端
客户端通常是第三方服务,通过 Open API 获取资源。
客户端获取资源步骤:
第一步,用户需要在开放平台登录,然后注册客户端凭证,注册过程中给该凭证指定权限范围(Scope),注册完后会生成 client_id 和 client_secret,其中 client_secret 需要保密管理。
第二步,利用上一步拿到的 client_id 和 client_secret 发起请求获取令牌。
第三步,调用 API ,并在请求头上附带令牌。
令牌会过期,过期后重新请求获取令牌即可。
❝区分两种权限类型:
API 权限,用户创建客户端凭证时指定(Scope)
数据权限,继承用户的数据权限
授权服务
授权服务负责授权和维护客户端凭证与权限的关系。
依赖 spring-security-oauth2-authorization-server 组件来构建该服务。
❝spring-security-oauth2-authorization-server 组件里面提供了基于 OAuth2 实现的授权服务,开箱即用.
库表介绍
-
oauth2_registered_client:spring-security-oauth2-authorization-server 组件依赖的表,记录已注册了的客户端凭证。
-
oauth2_authorization:spring-security-oauth2-authorization-server 组件依赖的表,记录已生成的令牌。
-
oauth2_user_registered_client_relation:记录用户和客户端凭证的关系。
-
oauth2_scope:权限范围(模块),支持两个层级。
-
oauth2_open_api:记录对外API清单以及与 scope 的关系。
各个表的关系如下:
授权
目前仅支持凭证式授权方式,不需要刷新令牌,令牌过期后重新获取即可。
spring-security-oauth2-authorization-server 组件内置了授权接口 /oauth/token。
JWT
令牌使用 JWT 生成,使用 JWT 的好处是:
-
JWT 是一种自包含结构,可以存放一些关键信息,不需要额外查询。仅使用 JWT 令牌即可完成校验令牌有效性的动作。
-
JWT 令牌由客户端来保管,且在里面存放客户端信息,服务器端不需要维护客户端状态,有利于服务端的无状态化和水平扩展。
-
JWT 令牌通过签名来防止信息被篡改。
坏处是:
-
虽然信息不能被篡改,但是还是有泄漏的风险,建议对内容进行加密。
❝JWT 可以到 JWT.IO
https://jwt.io/
网站解析。
网关
网关是客户端访问的唯一入口,是外部请求连接内部服务的桥梁,授权,鉴权,路由都在这里完成。
网关处理两类请求,一类是授权(获取令牌)
请求,一类是获取资源
请求。
授权(获取令牌)请求
这类请求逻辑比较简单,网关接收到后直接路由到授权服务。
获取资源请求
此类请求需要网关先到授权服务获取到权限信息,鉴权通过后再路由到资源服务。
注意,网关对鉴权通过的请求在路由前会设置请求头 Open-Api-Client-Id
,Open-Api-User-Id
,作用有两个:
-
可以通过这两个请求头判断是否是 Open API 请求。
-
资源服务可以从这两个请求头获取到 client_id 和 user_id 信息。
资源服务
资源服务是实际的资源提供者。
请求来到资源服务代表鉴权已经通过,可以通过请求头 Open-Api-Client-Id
和 Open-Api-User-Id
来获取 Open API 调用者 client_id 和对应的 user_id 信息。
请求
获取令牌
请求接口:/oauth2/token
请求方式:POST
请求头:
请求参数:
名称 | 类型 | 描述 |
---|---|---|
client_id | string | client_id |
client_secret | string | client_secret |
grant_type | string | 授权方式,目前仅支持 client_credentials |
响应字段:
名称 | 类型 | 描述 |
---|---|---|
access_token | string | 令牌 |
token_type | string | 令牌类型,目前仅支持 Bearer |
expires_in | int | 过期时间,单位秒,令牌过期后需要重新获取 |
scope | string | 权限范围 |
响应示例:
{
"access_token": "eyJraWQiOiJyc2EtandrLWtpZCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI1MjMxODcwODQ5OTMxMjgwNTExIiwiYXVkIjoiNTIzMTg3MDg0OTkzMTI4MDUxMSIsIm5iZiI6MTY0NjAyODk1OSwic2NvcGUiOlsiMS0wIiwiMi0wIiwiMy0wIl0sImlzcyI6Imh0dHA6XC9cL3ZvZG1hcmtldHNzby55ZnhuLmxpemhpLmZtIiwiZXhwIjoxNjQ2MDMwMTU5LCJpYXQiOjE2NDYwMjg5NTksImNpZCI6IjUyMzE4NzA4NDk5MzEyODA1MTEifQ.Wn9APM9l64J761MJDs2nh61lITme575UyCXvyL0UftYEOebpoo60PSN3kLVGOXC5LIEztmzWF4ZcEOlkoHToU-9op3XML3e-HikmcpPOIExjDNdbimrY_zoHvzuX0LkTRxDNEwCrHkl7kHueJlwwXcaKM6jOLxyBD6wRL43DvaF4__hiI_RvMVh069SOgQlVRr0wpKtjegEfoXiMOhGYuRt_ArKBv8SWYjGL2Zz0dPA6Kxy8NowgVEHtwRRPSklSKPzq6sWsU9XMq1uGHBcf1EAMFD-CQAcFsL_b7oS4rGgOgT7kYDHRaMKGILnCEY4VZDA3FNTy1vnllumzN6quEA",
"token_type": "Bearer",
"expires_in": 1800,
"scope": "1-0 2-0 3-0"
}
获取资源
获取资源请求需要包含 Authorization
请求头,示例:
Authorization = Bearer eyJraWQiOiJyc2EtandrLWtpZCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI1MjMxODcwODQ5OTMxMjgwNTExIiwiYXVkIjoiNTIzMTg3MDg0OTkzMTI4MDUxMSIsIm5iZiI6MTY0NjAyODk1OSwic2NvcGUiOlsiMS0wIiwiMi0wIiwiMy0wIl0sImlzcyI6Imh0dHA6XC9cL3ZvZG1hcmtldHNzby55ZnhuLmxpemhpLmZtIiwiZXhwIjoxNjQ2MDMwMTU5LCJpYXQiOjE2NDYwMjg5NTksImNpZCI6IjUyMzE4NzA4NDk5MzEyODA1MTEifQ.Wn9APM9l64J761MJDs2nh61lITme575UyCXvyL0UftYEOebpoo60PSN3kLVGOXC5LIEztmzWF4ZcEOlkoHToU-9op3XML3e-HikmcpPOIExjDNdbimrY_zoHvzuX0LkTRxDNEwCrHkl7kHueJlwwXcaKM6jOLxyBD6wRL43DvaF4__hiI_RvMVh069SOgQlVRr0wpKtjegEfoXiMOhGYuRt_ArKBv8SWYjGL2Zz0dPA6Kxy8NowgVEHtwRRPSklSKPzq6sWsU9XMq1uGHBcf1EAMFD-CQAcFsL_b7oS4rGgOgT7kYDHRaMKGILnCEY4VZDA3FNTy1vnllumzN6quEA
其中,
-
Bearer
代表令牌类型,后面跟着访问令牌。
API 权限通配符配置示例
这里给一些 API 权限配置示例(只是建议)
URI | 请求方式 | 描述 | API 权限通配符配置(对应 oauth2_open_api 表 path 字段) |
---|---|---|---|
/api/demo_entities/{id} | GET | 根据id获取资源 | GET_/api/demo_entities/{id:\d+} |
/api/demo_entities | POST | 新建资源 | POST_/api/demo_entities,POST_/api/demo_entities/ |
/api/demo_entities | GET | 查询资源 | GET_/api/demo_entities,GET_/api/demo_entities/ |
/api/demo_entities | PUT | 更新资源 | PUT_/api/demo_entities,PUT_/api/demo_entities/ |
/api/demo_entities/{id} | DELETE | 根据id删除资源 | DELETE_/api/demo_entities/{id:\d+} |