JWT 简介

对接流程图

第 1 步 创建 JWT 应用

第 2 步 SDK 下载

第 3 步 业务系统研发

第 4 步 IDaaS 模板 SSO 地址更新

第 5 步 SSO 测试

第 6 步 完成

JWT 简介

Json web token ( JWT ), 是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 Json 对象的形式安全的传递信息,该 token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景

实现原理

SP 接收 IDaaS 平台向 callback url 发出的 id_token 参数(即 JWT 令牌),并使用我们提供的(或第三方提供的) JWT 解密库/方法对 JWT 进行解析,并验证身份。

1)用户通过浏览器登录 IDaaS ,并发起单点请求

2)IDaaS 生成 token 令牌发送到业务应用。

3)业务系统获取到 token 令牌,用我们提供的插件或方法解析 token 令牌,解析成功获取到用户信息并验证

验证通过:业务系统重定向到用户首页,

验证失败:业务系统拒绝登录并页面提示错误信息

对接流程图

第 1 步 创建 JWT 应用

1)登录 IDaaS 管理员平台

使用 IT 管理员账号登录云盾 IDaaS 管理平台。具体操作请参考 IT管理员指南-登录

2)添加 JWT 应用

在【应用】-【添加应用】中,找到应用名称为: JWT,点击右边【添加应用按钮】

3)填写信息并保存

参数说明:

1. 图标:业务应用的 logo 图片。

2. 应用 ID: 自动生成应用 ID,且唯一。

3. 应用名称: 填写创建应用的名称。

4. 应用类型: 代表该服务支持的设备类型,标记使用。

5. redirect_uri:业务系统中(或 PC 程序)的 JWT SSO 地址,在单点登录时 IDaaS 将向该地址用[GET]方式发送 ID_Token 信息,参数名为ID_Token,业务系统通过 ID_Token 与 Public Key 可获取业务系统中的用户信息,如果在业务系统(SP)发起登录,请求 SP 登录地址时如果携带 Service 参数 IDaaS 会检验合法性,成功后会将浏览器重定向到该地址,并携带ID_Token身份令牌。

6. Target_link_uri: 业务系统中在 JWT SSO 成功后重定向的 URI,一般用于跳转到二级菜单等,若设置了该 URI,在 JWT SSO 时会以参数 Target_link_uri 优先传递该值,若未设置该值,此时若SSO中有请求参数。

7.是否包含用户角色:用于第三方权限系统对接,开启后会将账户的角色信息包含在 ID_Token 中。

8.SSO Binding:单点登陆请求方式,REDIRECT 为 GET 类型,也可选择 POST。

9.ID_Token有效期:单位秒。

10.是否显示应用:授权给用户后,是否在用户首页显示。默认开启,若关闭,用户登录将看不到该应用。

11.账户关联方式:

a.账户关联(系统按主子账户对应关系进行手动关联,用户添加后需要管理员审批)

b.账户映射(系统自动将主账户名称或指定的字段映射为应用的子账户)

4)导出公钥

完成上面应用创建,我们在【应用列表】中,就可以找到新创建的应用。点击【详情】按钮,点击【查看详情】。

找到 JWT PublicKey

复制粘贴到文本txt,或者使用下方导出,都可以将 JWT PublicKey 导出。将其交给第三方也系统,用作 ID_Token 解析。

第 2 步 SDK 下载

在这里,第三方业务系统就要开始研发的准备工作了。我们提供 4 种语言的 SDK 集成方式。

JAVA/PHP/.NET/Python ,当然,如果您的业务系统是其他语言也可以进行对接,需要您自行编写解析 ID_Token 的代码。可以参考:

JWT 官网

下载方式:

1)登录到 IDaaS 管理平台。登录操作,请参考 开发者访问方式

2)切换至【开发者】

3)点击【单点登录(SSO)】- 点击【单点登录(SSO)开发者文档】

4)在右侧导航栏最下方找到 【其他】-【相关下载】,按需下载即可

第 3 步 业务系统研发

研发核心思想:

1)能够接收到令牌

2)能够成功解析令牌,拿到 用户信息

3)匹配用户信息是否与当前自己的账号一致

4)跳转至用户首页

1)JAVA 插件式集成

配置环境

JDK 1.6 以上

接收令牌

// id_token 是 IDaaS 请求时带来的,在 body 里获取,PublicKey是在 IDaaS 里注册应用时生成的,注册完可见,此示例代码是获取用户信息。
// JWT SSO
@RequestMapping(value = “/JWT/sso/login”)
public String SSO Url(@RequestParam String id_token, String redirect_url, Model model, HttpServletRequest request){
  //1.接收方法为GET方式,参数名为 id_token
  //2.<解析令牌>为解析 id_token 并验证代码
}

解析令牌

PublicKey: 解析令牌的过程中,我们会使用到应用的 PublicKey。请在 JWT 应用 -> 详细 中将PublicKey字段对应的内容拷贝并存储起来。

//1.使用公钥,解析 id_token
// 使用PublicKey解密上一步获取的 id_token 令牌
DingdangUserRetriever retriever = new DingdangUserRetriever( id_token, PublicKey);
DingdangUserRetriever.User user = null;
try {
  //2.获取用户信息
  user = retriever.retrieve();} catch (Exception e) {
  LOG.warn(“Retrieve SSO user failed”, e);
  return “error”;
}
//3.判断用户名是否在自己系统存在isExistedUsername()方法为业务系统自行判断数据库中是否存在
if (isExistedUsername(user.getUsername())) {
  //4.如果存在,登录成功,返回登录成功后的页面
  User SPUser = userService.updateLoginTimes(user.getUsername());
  request.get session ().setAttribute(Http session SecurityContextRepository. SPRING_SECURITY_CONTEXT_KEY, saveSecurity( SP User));
  //5.如果注册时添加redirect_url,那么返回此自定义url页面
 if (StringUtils.isNotEmpty(redirect_url)) {
    return “redirect:” + redirect_url;
  }
  //6.否则返回系统默认操作页面
  return “redirect:../../index”;
} else {
  //7.如果不存在,返回登录失败页面,提示用户不存在
  model.addAttribute(“error”, “username { “ + user.getUsername() + “ } not exist”);
  return “error”;
}

2)PHP插件式集成

配置环境

在本例中,我们使用composer管理第三方库(可选)

公钥:开发前需要在IT管理员权限下前往应用->详细->导出PKCS8公钥来获取解密 JWT 用的公钥,并安全地放置在能访问到的目录内。

接收令牌

JWT 的 id_token 将会以url参数的方式传进callback页面,我们直接将其读取出来:

/* 使用composer 载入 php-JWT第三方库
* 命令行 composer require firebase/php-JWT
* 库链接:https://github.com/firebase/php-JWT
* 我们使用Firebase的这个第三方库来实现对JWT的解密,如果不用composer的话,请自行添加源文件
* 你也可以使用其他能对 JWT token 进行RS256解码的工具或库
*/
// 在这里将 JWT 库引入,在这里为了便捷demo直接使用
// 推荐使用
require 'vendor/autoload.php';
use \Firebase\ JWT \ JWT ;
// 本地存储public key公钥的位置
$public_key_location = “LOCATION/TO/YOUR/PUBLIC-KEY/XXX.pem”;
// 读取公钥信息,公钥在这里存储在一个.pem文件内
$public_key = file_get_contents($public_key_location);
// 从url的参数中读取 id_token ,即令牌
if (!empty($_GET[“ id_token “])) {
  $JWT = $_GET[“ id_token “];
  // 这里继续第二步:解析令牌
}

解析令牌

通过第三方库 php-JWT,使用公钥对收到的 JWT token (即 id_token )进行解密,获取到用户信息并错误码为0的话,验证通过跳转到登录页面,失败则拒绝:

phptry{
  /**
  * You can add a leeway to account for when there is a clock skew times between
  * the signing and verifying servers. It is recommended that this leeway should
  * not be bigger than a few minutes.
  *
  * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
  */
  // Firebase的 JWT 库的一个参数,不出问题的话可以忽略
  // (可选)当服务器时间与本地时间不符时,可以通过这个leeway参数来调整容错
  JWT::$leeway = 60; // $leeway in seconds
  // 使用公钥、使用RS256算法对 JWT (即第一步传进来的 id_token )进行解密进行解密
  $decoded = JWT::decode($JWT, $public_key, array('RS256'));
  // 将解密的结果从class转化成PHP array
  $decoded_array = (array) $decoded;
  // 打印出解密的结果,成功!
  print(“解密结果:<br>”);
  foreach ($decoded_array as $key => $value) {
    print $key . “: “ . $value . “<br>”;
  }
  // 获取到用户信息后,判断该用户是否存在于你的系统内
  if (userExistsInSystem()) {
    // 如果存在,那么登录成功,跳转到登录后页面
  } else {
    // 如果不存在,那么登录失败,跳转到显示错误页面
  }
catch(Exception $e) {
 print “错误:” . $e->getMessage();
}

3).NET插件式集成

配置环境

.NET Framework 4及以上

接收令牌

// id_token 是 IDaaS 请求时带来的,在body里获取,PublicKey是在 IDaaS 里注册应用时生成的,注册完可见,此示例代码是获取用户信息。
// JWT SSO
[Route(“jwt/sso/login”)]
public ActionResult ssoUrl(String id_token ){
  //1.接收方法为GET方式,参数名为 id_token
  //2.<解析令牌>为解析 id_token 并验证代码
}

解析令牌

PublicKey: 解析令牌的过程中,我们会使用到应用的PublicKey。请在 JWT 应用->详细 中将PublicKey字段对应的内容拷贝并存储起来。

//1. 使用公钥,解析 id_token
string username;
DingdangSDK.DingdangUserRetriever retriever = new DingdangSDK.DingdangUserRetriever( id_token, PublicKey);
DingdangSDK.User user = null;
//2. 获取用户信息
user = retriever.retrieve();
username = user.sub;
//3. 判断用户名是否在自己系统存在
//4. 如果存在,登录成功,返回登录成功后的页面
//5. 如果注册时添加redirect_url,那么返回此自定义url页面
//6. 否则返回系统默认操作页面
//7. 如果不存在,返回登录失败页面,提示用户不存在

4)python插件式集成

下载资源库

本Python JWT 示例使用Py JWT 库来进行 JWT 的解密

公钥:开发前需要在IT管理员权限下前往应用->详细->导出 PKCS8 公钥来获取解密 JWT 用的公钥,并安全地放置在能访问到的目录内。

JWT 的 token 将会以url参数的方式传进来,解密后进行认证判断

// 库的 github 链接 https://github.com/jpadilla/pyJWT
pip install Py JWT
// 注:CentOS系统如果使用时无法导入算法 RSAAlgorthm时需要下载pyJWT的2个依赖包
yum install ibffi-devel
pip install cryptography

接收令牌

def get_id token ( token ):
  if not token .strip():
    print(' token 信息不能为空')
  else:
    //这里继续第二步:解析令牌
get_id token (my_id token ); // 运行程序

解析令牌

## 2.解析令牌
通过JWT解密库,使用公钥对传入的 id_token 进行解密。将公钥以字符串的形式从文件中读取出来,并作为key进行解密:
// 引入用到的包文件
import JWT
import json
from JWT.algorithms import RSAAlgorithm
from JWT.utils import force_bytes
from utils import key_path 
// 本例中key_path辅助方法是写在utils工具类中的
def get_user_ifon( id_token ):
  try:
    algo = RSAAlgorithm(RSAAlgorithm.SHA256)
    pem_key = open(key_path('D:\pythonDemo\key\public_key_pkc8.pem'), 'r')
    public_key = algo.prepare_key(pem_key.read())
    token_info = JWT.decode(force_bytes( id_token ),key=public_key,verify=True)
    user_info = json.loads(json.dumps( token_info))
    username = user_info['sub']
    print(username)
    # 3.判断用户名是否在自己系统存在
    # 4.如果存在, 登录成功,返回登录成功后的页面
    # 5.如果注册时添加redirect_url,那么返回此自定义url页面
    # 6.否则返回系统默认操作页面
    # 7. 如果不存在, 返回登录失败页面, 提示用户不存在
  except Exception as e:
    print(e)

上面用到的key_path方法是用来获取public key位置的辅助方法,具体如下:

def key_path (key_name):
 return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'keys', key_name)

处理结果

解析后数据的数据为JSON,校验数据后进行登录处理

请求成功示例数据:

{“username”:“zhangsan”,”state”,””,error:“0”,”errorMes”:“operation success”}

返回参数:

常量 说明
username 子账户(业务应用)用户名
state 自定义参数,原值返回
error 错误码,0 表示成功
errorMsg 错误信息描述

第 4 步 IDaaS 模板 SSO 地址更新

由于我们在第一步创建应用时,SSO 单点登录地址是自由填写的,非正式地址。所以,当业务系统开发工作结束后。我们需要在返回 IDaaS 将这个地址进行更新,后续才能下步的测试工作。

第 5 步 SSO 测试

1)新建一个普通账号

2)账号授权

在新建账号时,会自动授权,比如可以看到给授权的应用

或者,使用【授权】-【应用授权】- 【按账户授权应用】功能,给账号分配我们需要测试的 JWT 应用。

3)使用测试账号登录

使用我们测试账号登录后即可看到我们创建的应用 logo图标

若此时无法看到图标,请检查

(1)应用是否开启?(【应用列表】查看及开启)

(2)账号是否被授予应用权限?(【应用授权】查看及授权)

4)业务系统检查是否能获取到 id_Token

(1)点击应用 logo

(2)我们可以在浏览器地址栏中看到 id_token 信息,例如

(3)我们也可以使用浏览器的 [开发者工具] 看到 id_token 信息,

(4)此时业务系统就需要检查是够收到 IDaaS 发送的 id_token 并且成功解析,以获取用户名

5)检查是否能正确跳转到业务系统首页

如您的以成功获取并解析 id_token 正常,这时候,我们就需要看是否正常跳转到您的首页了。

第 6 步 完成

当所有测试工作结束后,我们就在 IDaaS 上完成了 SSO 单点的对接工作。如果我们都是使用的测试环境,那么在正式上线切换时,我们需要再次将 JWT 模板中的地址修改为正式生产环境的地址。

如果您有其他的问题,或者咨询事宜,请与我们 联系