全部产品

JWT应用接入

更新时间:2020-10-14 13:55:43

在 PDS 控制台上,开发者可以创建出一个域(Domain)。

域:拥有独立的访问入口、资源空间、用户体系等。

PDS 为每个域提供一套独立的用户体系。

  • 支持 OAuth2.0 应用接入: 目前支持基于钉钉和 RAM 子用户的 OAuth 登录接入。
  • 支持 JWT 应用接入:支持已有的用户体系的接入,比如已有的 web 服务想要增加云盘的功能等。

JWT 应用介绍

此类应用可以通过私钥计算出一个 JWT(JSON Web Token),该JWT具有访问已经配置了公钥的服务端的API的能力(权限)。

t1

JWT应用,适合服务端应用访问 PDS 的数据,比如企业通过自建应用访问企业的数据(数据所有权属于企业)的场景,这个场景不需要最终用户参与授权。

适用场景

适用场景1: 企业A已有独立的账号体系和登录入口,想要使用已有的登录入口结合 PDS 搭建一套已有独立账号的云存储系统。

适合场景2: 企业A开发一个运维统计应用,用来定期统计一些数据指标。

JWT 应用原理

  • 需要生成一对公私钥。
  • 应用服务端配置私钥,PDS API 端配置公钥。
  • 应用服务端可以使用私钥计算出一个 JWT Token,这个 JWT 具有一定的权限,可以直接调用 PDS API 相应的接口。

目前使用最广泛的场景,就是调用 PDS API 的 GetToken 接口,获取任意用户的 AccessToken。 非常适合已有用户体系接入。

t2

下面介绍详细的接入步骤。

接入步骤概览

  • 租户在 PDS 控制台创建一个JWT应用。
  • 然后生成一对公私钥。公钥保存到 PDS 服务端,私钥自己复制保存。
  • 使用我们提供的 SDK 可以通过私钥生成一个有特定权限的并且有时效的 JWT。
  • 使用该 JWT 调用 getToken 接口换取一个代表用户身份的 AccessToken。

详细步骤

(1) 创建域

a1

(2) 创建应用

进入域详情,在应用列表界面,创建一个应用:

k1

(3) 设置公钥

应用创建好后,点击”设置公钥”:

k3

生成公私钥:k5

生成公私钥后,记得复制私钥,自己保存。然后点确定即可。

k4

(4) 服务端计算JWT

需要开发代码。

逻辑在用户登录成功之后,通过私钥计算出JWT。 具体的计算方法:

node.js 参考代码:

  1. const JWT = require('jsonwebtoken');
  2. const privateKeyPEM = '';
  3. var current_time_sec = Math.floor(Date.now()/1000);
  4. var opt = {
  5. iss:'',
  6. sub:'',
  7. sub_type:'',
  8. aud:'',
  9. jti: '',
  10. exp: current_time_sec + 60,
  11. iat: current_time_sec ,
  12. //nbf: '',
  13. auto_create: true,
  14. };
  15. var token = JWT.sign(opt, privateKeyPEM, {
  16. algorithm: 'RS256'
  17. });

opt 参数说明:

字段名 是否必选 类型 描述
iss 必选 String App ID
sub 必选 String User ID、Domain ID
sub_type(扩展字段) 必选 String 账号类型,目前支持填 user、service,此处填user,则sub为userID,签发普通用户accessToken。 此处填service,则sub为domainID,签发domain服务账号accessToken(超级管理员权限)
aud 必选 String Domain ID
jti 必选 String 应用生成JWT的唯一标识,长度16-128位,推荐使用uuid即可
exp 必选 Integer JWT过期时间, Unix Time,单位秒,最多在当前时间基础上加60秒
iat 可选 Integer 签发时间,Unix Time,单位秒,在此时间之前无法使用,如:1577682075
nbf 可选 Integer 生效时间,Unix Time,单位秒,不指定则默认生效
auto_create(扩展字段) 可选 Boolean 如果用户不存在,则自动创建,默认不创建用户。

(5) 通过JWT换取任意用户的AccessToken

调用CCP API换取。

  1. POST /v2/oauth/token
  2. Content-Type: application/x-www-form-urlencoded
  3. grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&client_id=${APP_ID}&assertion=xxxxxxxxxx

注意:要设置请求的 content-type 为 application/x-www-form-urlencoded

注意:请求参数要放在body里

请求参数说明:

字段名 是否必选 类型 描述
grant_type 必选 String 申请授权的类型,此处应为字符串常量: urn:ietf:params:oauth:grant-type:jwt-bearer
client_id 必选 String 应用ID
assertion 必选 String 上一步骤计算出来的JWT

返回 token json 样例

  1. {
  2. access_token: 'eyJh****eQdnUTsEk4',
  3. refresh_token: 'kL***Lt',
  4. expires_in: 7200,
  5. token_type: 'Bearer'
  6. }

浏览器端拿到用户的AccessToken后,就可以直接调用CCP API了。

(6) 使用 Basic UI (可选)

如果您不想自己开发UI,而我们官方提供的Basic UI可以满足您的要求,可以直接使用Basic UI。

方法1:

使用window.open 打开 basic ui,postMessage传递AccessToken过去即可。

示例代码:

  1. const ENDPOINT = `https://${domain_id}.apps.aliyunpds.com`
  2. var win = window.open(ENDPOINT + '/accesstoken')
  3. window.addEventListener('message', onMessage, false)
  4. async function onMessage(e) {
  5. if (e.data.code == 'token' && e.data.message == 'ready') {
  6. var result = await getToken();// 从服务端获取 AccessToken
  7. //result = {"access_token": ...}
  8. win.postMessage({
  9. code: 'token',
  10. message: result
  11. }, '*')
  12. window.removeEventListener('message', onMess)
  13. }
  14. }

方法2:

使用 iframe 嵌入 basic ui,postMessage 传递 AccessToken 过去即可。

示例代码:

  1. //iframe嵌入URL构成:
  2. const iframeURL = `https://${domain_id}.apps.aliyunpds.com/accesstoken?origin=${location.origin}`

html代码:

  1. //注意替换变量iframeURL
  2. <iframe id="ifr" src="iframeURL"></iframe>
  1. window.addEventListener('message', onMess)
  2. async function onMessage(e) {
  3. if (e.data.code == 'token' && e.data.message == 'ready') {
  4. var result = await getToken();// 从服务端获取 AccessToken
  5. //result = {"access_token": ...}
  6. document.getElementById('ifr').contentWindow.postMessage({
  7. code: 'token',
  8. message: result
  9. }, '*')
  10. window.removeEventListener('message', onMess)
  11. }
  12. }

注意: 使用方法2,还需要在basic ui中配置这个安全设置,把宿主页的origin配置上

假设宿主页为 http://demopds.com/a.html, origin为 http://demopds.com, 这里配置 demopds.com 即可。

v2

附录:

服务端获取AccessToken 的 Node.js 代码实现:

  1. const fs = require('fs')
  2. const JWT = require('jsonwebtoken');
  3. const axios = require('axios')
  4. init()
  5. async function init() {
  6. try {
  7. //这几个变量需要根据实际情况填写
  8. var params = {
  9. domain_id: '${DOMAIN_ID}',
  10. client_id: '${APP_ID}',
  11. user_id: '${USER_ID}',
  12. privateKeyPEM: "${PRIVATE_KEY_PEM}"
  13. };
  14. var obj = await JWTGetUserToken(params)
  15. var tokenInfo = obj.data
  16. console.log(tokenInfo)
  17. } catch (e) {
  18. if (e.response) {
  19. console.log(e.response.status)
  20. console.log(e.response.headers)
  21. console.log(e.response.data)
  22. } else {
  23. console.log(e)
  24. }
  25. }
  26. }
  27. async function JWTGetUserToken({ domain_id, client_id, user_id, privateKeyPEM }) {
  28. var now_sec = parseInt(Date.now()/1000)
  29. var opt = {
  30. iss: client_id,
  31. sub: user_id,
  32. sub_type: 'user',
  33. aud: domain_id,
  34. jti: Math.random().toString(36).substring(2),
  35. exp: now_sec + 60,
  36. // iat: now_sec,
  37. // nbf: '',
  38. auto_create: true,
  39. };
  40. var token = JWT.sign(opt, privateKeyPEM, {
  41. algorithm: 'RS256'
  42. });
  43. const PRE = `https://${domain_id}.auth.aliyunpds.com`
  44. return await axios({
  45. method: 'post',
  46. url: PRE + '/v2/oauth/token',
  47. //注意:要设置请求的 content-type 为 application/x-www-form-urlencoded
  48. headers: {
  49. 'Content-Type': 'application/x-www-form-urlencoded'
  50. },
  51. //注意:请求参数要放在body里
  52. data: params({
  53. grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
  54. client_id: client_id,
  55. assertion: token
  56. })
  57. })
  58. }
  59. function params(m){
  60. const params = new URLSearchParams();
  61. for(var k in m){
  62. params.append(k, m[k]);
  63. }
  64. return params;
  65. }