OAuth2.0 协议授权码模式与应用对接
时序图
上图为 OAuth2.0 授权码模式认证时序图,描述了一个应用的授权认证及访问过程。
阶段一为单点认证阶段,主要有以下流程:
- 访问应用,应用检测未登录,请求获取授权码端口(接口1)。如果此时无有效的认证信息则会跳转登录页。
- 用户输入用户信息登录成功后会携带授权码
code
重定向回应用系统。 - 使用授权码
code
调用统一认证的获取访问令牌接口(接口2)获取访问令牌并保存在前端,方便后续接口的调用。 - 使用访问令牌
access_token
调用统一认证的获取用户信息接口(接口3)获取用户信息。
阶段二为访问后端接口阶段,主要有以下流程:
- 操作应用调用后端接口前,应用会检查当前的访问令牌
access_token
是否已过期,如果过期则调用刷新访问令牌接口(接口4),使用refresh_token
刷新访问令牌。 - 调用后端接口时,应用会将访问令牌
access_token
放在请求头中,应用后端调用访问令牌校验接口(接口5) 校验访问令牌的有效性。 如果有效则执行相应的业务逻辑并返回业务数据,浏览器进行渲染;如果无效则 http 码为401
,并返回相应的错误信息。
准备工作
在数字底座注册应用,注册时需指定应用访问 URL,注册后可获取应用的 client_id
和 client_secret
供后面使用
接口
以下是上述过程中会用到的接口:
1.获取授权码
请求地址:http://{IP}:{PORT}/sso/oauth2.0/authorize
请求方法:GET
描述:授权用户启动身份验证流程,浏览器访问该端口
请求参数:
参数名 | 描述 |
---|---|
response_type | 返回类型为固定值 code 。 |
client_id | 申请的客户端 id。 |
redirect_uri | 重定向 url,认证通过后会重定向回来并以授权码 code 作为请求参数。 |
请求示例:
GET http://{IP}:{PORT}/sso/oauth2.0/authorize?response_type=code&client_id=clientid&redirect_uri=http://localhost:7070/demo/org
响应示例:
HTTP/1.1 302 Found
Location: http://localhost:7070/demo/org?code=OC-5-SRjzjBnsZf2t6NJk521L-ulc6iewYssv
2.获取访问令牌
接口地址:http://{IP}:{PORT}/sso/oauth2.0/accessToken
请求方法:GET|POST
描述:通过授权码 code
获取访问令牌
请求参数:
参数名 | 描述 |
---|---|
grant_type | 授权类型为固定值 authorization_code 。 |
client_id | 申请的客户端 id。 |
client_secret | 申请的客户端密钥。 |
code | 请求授权返回的授权码 code,授权码使用一次后便会失效。 |
redirect_uri | 重定向 url。 |
响应字段:
字段 | 描述 |
---|---|
access_token | 访问令牌。 |
refresh_token | 刷新令牌。 |
token_type | 令牌类型。 |
expires_in | 过期时间(秒)。 |
scope | 权限范围。 |
请求示例:
GET http://{IP}:{PORT}/sso/oauth2.0/accessToken?grant_type=authorization_code&client_id=clientid&client_secret=secret&code=OC-5-SRjzjBnsZf2t6NJk521L-ulc6iewYssv&redirect_uri=http://localhost:7070/demo/org
响应示例:
{
"access_token": "AT-2-Jaxf6uh1fEPux0RN7O-bhYmHulUvXNtR",
"refresh_token": "RT-2-g-wAcxjz6HOHs7O8YeGn1qElkZYDtM4S",
"token_type": "Bearer",
"expires_in": 28800,
"scope": ""
}
3.获取用户信息
接口地址:http://{IP}:{PORT}/sso/oauth2.0/profile
请求方法:GET|POST
描述:获取认证过的用户信息
请求参数:
参数名 | 描述 |
---|---|
access_token | 访问令牌。 |
响应字段:
字段 | 描述 |
---|---|
dn | 由name组成的父子关系列表(倒序),之间用逗号分隔 |
managerLevel | 管理员类型 |
password | 密码 |
globalManager | 是否为管理员 |
tenantName | 租户名 |
caid | caid |
loginName | 登录名 |
personType | 人员类型 |
IDNum | 身份证号 |
邮箱 | |
original | 是否为原始账号(针对多岗) |
sex | 性别 |
guidPath | 由ID组成的父子关系列表(正序),之间用逗号分隔 |
mobile | 电话号码 |
positions | 岗位列表,用逗号分隔 |
parentId | 人员父节点 id |
positionId | 当前岗位 id |
name | 姓名 |
tenantId | 租户 id |
avator | 头像 url |
personId | 人员 id |
originalId | 原始账号 id |
tenantShortName | 租户登录名 |
请求示例:
GET http://{IP}:{PORT}/sso/oauth2.0/profile?access_token=AT-2-Jaxf6uh1fEPux0RN7O-bhYmHulUvXNtR
响应示例:
{
"oauthClientId": "clientid",
"loginType": "loginName",
"dn": "cn=系统管理员,o=虚拟组织",
"managerLevel": 1,
"password": "$2a$10$xo6A/X7aeLcEvYJRyu.kUOd9EJ1o3c8ZK6BNqnhda30eRwnA7VTqm",
"globalManager": true,
"tenantName": "default",
"caid": "",
"loginName": "systemManager",
"personType": "deptPerson",
"idNum": "",
"email": "",
"original": true,
"sex": 1,
"deptId": "",
"guidPath": "1663980559743000576,1663980559877218304",
"mobile": "",
"positions": "",
"parentId": "1663980559743000576",
"positionId": "",
"name": "系统管理员",
"tenantId": "11111111-1111-1111-1111-111111111113",
"avator": "",
"personId": "1663980559877218304",
"originalId": "",
"tenantShortName": "default",
"service": "http://localhost:7070/demo/org",
"id": "systemManager",
"client_id": "clientid"
}
4.刷新访问令牌
接口地址:http://{IP}:{PORT}/sso/oauth2.0/accessToken
请求方法:GET|POST
描述:当访问令牌过期了可通过 refresh_token
去获取新的访问令牌
请求参数:
参数名 | 描述 |
---|---|
grant_type | 授权类型为固定值 refresh_token 。 |
client_id | 申请的客户端 id。 |
client_secret | 申请的客户端密钥。 |
refresh_token | 刷新令牌。 |
请求示例:
GET http://{IP}:{PORT}/sso/oauth2.0/accessToken?grant_type=refresh_token&client_id=clientid&client_secret=secret&refresh_token=RT-2-g-wAcxjz6HOHs7O8YeGn1qElkZYDtM4S
响应示例:
{
"access_token": "AT-3-yVC0kA-0XJ7TT3USwucOklHx2W1bo3Ah",
"refresh_token": "RT-3-HsClpsOdEtmvJ3QtxQS7A-iVRkAwIAoc",
"token_type": "Bearer",
"expires_in": 28800,
"scope": ""
}
5.访问令牌校验
接口地址:http://{IP}:{PORT}/sso/oauth2.0/introspect
请求方法:GET|POST
描述:查询访问令牌 access_token
的状态,其中应用的凭证 client_id
和 client_secret
需以 Basic Auth 的形式提供
请求参数:
参数名 | 描述 |
---|---|
token | 访问令牌 access_token |
请求头:
参数名 | 描述 |
---|---|
Authorization | 应用的凭证,参数值为 Basic {Auth} 其中 {Auth} 为经过 base64 编码的 client_id + ":" + client_secret,即 base64_encode({client_id}:{client_secret})。 |
请求示例:
GET http://{IP}:{PORT}/sso/oauth2.0/introspect
Authorization: Basic Y2xpZW50aWQ6c2VjcmV0
Content-Type: application/x-www-form-urlencoded
token=AT-3-yVC0kA-0XJ7TT3USwucOklHx2W1bo3Ah
响应示例:
{
"token": "AT-3-yVC0kA-0XJ7TT3USwucOklHx2W1bo3Ah",
"active": true,
"sub": "systemManager",
"scope": "CAS",
"iat": 1748241683,
"exp": 1748270483,
"realmName": "y9AuthenticationHandler",
"uniqueSecurityName": "systemManager",
"tokenType": "Bearer",
"aud": "clientid",
"attr": "{\"oauthClientId\":\"clientid\",\"loginType\":\"loginName\",\"dn\":\"cn=系统管理员,o=虚拟组织\",\"managerLevel\":1,\"password\":\"$2a$10$xo6A/X7aeLcEvYJRyu.kUOd9EJ1o3c8ZK6BNqnhda30eRwnA7VTqm\",\"globalManager\":true,\"tenantName\":\"default\",\"caid\":\"\",\"loginName\":\"systemManager\",\"personType\":\"deptPerson\",\"idNum\":\"\",\"email\":\"\",\"original\":true,\"sex\":1,\"deptId\":\"\",\"guidPath\":\"1663980559743000576,1663980559877218304\",\"mobile\":\"\",\"positions\":\"\",\"parentId\":\"1663980559743000576\",\"positionId\":\"\",\"name\":\"系统管理员\",\"tenantId\":\"11111111-1111-1111-1111-111111111113\",\"avator\":\"\",\"personId\":\"1663980559877218304\",\"originalId\":\"\",\"tenantShortName\":\"default\"}",
"client_id": "clientid",
"grant_type": "refresh_token"
}
返回的 json 数据中,active
字段表示访问令牌是否有效。