更好的阅读体验请访问:docs.authing.cn/authing/qui…
开始之前
如果你不了解用户池、单点登录和认证授权,建议先阅读基础概念。
预备知识
基本的 HTML 和 CSS 知识
中级 JavaScript 技能
所需工具
你喜欢的文本编辑器
可以在本地运行的 Web 服务器(比如:
npm install http-server -g)
注册一个 Authing 账号
如果你还没有账号,请点击这里注册 Authing 账号,注册完成后请进入控制台并创建一个用户池。
第三方登录 -> OIDC 应用选项卡,点击蓝色的「创建 OIDC 应用」按钮。
参数解释
认证地址,一个 authing.cn 的二级域名,用户将在此网址进行登录。
回调 URL,OIDC 登录成功后,回调到开发者自己业务的地址。本教程为演示,填写的地址是 http://localhost:8080,实际场景下要填写自己的业务地址。
在应用列表中点击刚创建好的应用,记录下 AppID,二级域名,供以后使用。
使用 AuthingSSO SDK 集成单点登录
创建一个空白的 HTML 文档用来编写 Authing 程序
本教程只是为了演示,因此我们没选择高级框架,这可以让我们专注于 Authing 本身。
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Authing SSO Example</title> </head> <body></body></html>添加三个按钮
增加三个按钮控件到 body 中,目的是为了演示如何使用 SDK 管理单点登录状态。
<button id="btn-login">login</button><button id="btn-track-session">trackSession</button><button id="btn-logout">logout</button>引入 AuthingSSO 并初始化
从 CDN 加载 AuthingSSO 的 SDK。填入你的 OIDC 应用 ID 和 域名,进行初始化。
<script src="https://cdn.jsdelivr.net/npm/@authing/sso/dist/AuthingSSO.umd.min.js"></script><script> let auth = new AuthingSSO({ appId: "YOUR_OIDC_APP_ID", appType: "oidc", appDomain: "OIDC_APP_DOMAIN.authing.cn" });</script>为按钮注册点击事件
达到的效果是:
点击 login 按钮,浏览器会跳转到 OIDC 登录页面,与用户完成身份确认。
点击 trackSession 按钮,会显示当前登录状态。
点击 logout 按钮,进行单点登出。
let login = document.getElementById("btn-login");let trackSession = document.getElementById("btn-track-session");let logout = document.getElementById("btn-logout");login.onclick = function() { auth.login();};trackSession.onclick = async function() { let res = await auth.trackSession(); alert(JSON.stringify(res));};logout.onclick = async function() { let res = await auth.logout(); alert(JSON.stringify(res));};完整代码
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Authing SSO Example</title> </head> <body> <button id="btn-login">login</button> <button id="btn-track-session">trackSession</button> <button id="btn-logout">logout</button> <script src="https://cdn.jsdelivr.net/npm/@authing/sso/dist/AuthingSSO.umd.min.js"></script> <script> let auth = new AuthingSSO({ appId: "YOUR_OIDC_APP_ID", appType: "oidc", appDomain: "OIDC_APP_DOMAIN.authing.cn" }); let login = document.getElementById("btn-login"); let trackSession = document.getElementById("btn-track-session"); let logout = document.getElementById("btn-logout"); login.onclick = function() { auth.login(); }; trackSession.onclick = async function() { let res = await auth.trackSession(); alert(JSON.stringify(res)); }; logout.onclick = async function() { let res = await auth.logout(); alert(JSON.stringify(res)); }; </script> </body></html>示例代码可从 Github 上找到,建议将 Github 上的代码下载运行。
运行方法
在终端中运行以下命令
$ git clone https://github.com/Authing/authing-sso-demo
$ cd authing-sso-demo
$ npm install -g http-server
$ http-server之后在浏览器访问 http://localhost:8080。
如果本地 8080 端口已被占用,应用可能会运行在 8081、8082 等后续端口。
运行效果
最初,我们没有登录,因此,点击 trackSession 按钮获取到的登录状态为空。
现在我们点击 login 按钮,会跳转到 OIDC 应用的用户认证页面,输入用户名密码进行登录。
浏览器被重定向到我们之前设置的回调链接,记下 code 参数,用于后面换取用户信息。
点击 trackSession 按钮,此时能够获取到该用户的登录状态,包括用户 ID,应用 ID,应用类型。
点击 logout 按钮,输出单点登出成功。
此时我们再点击 trackSession 按钮,可见登录状态为空,说明用户已经单点登出了。
获取用户信息
使用 OIDC 流程中返回的 code 换取 access_token
向以下地址发送 POST 请求:
POST https://OIDC_APP_DOMAIN.authing.cn/oauth/oidc/tokenbody 参数
参数名 | 意义 |
client_id | OIDC 应用的 app_id |
redirect_uri | 在控制台配置的 OIDC 回调 url 其中的一个值 |
scope | 需要请求的权限,如果需要获取 email 和手机号需要有 phone email,如果需要 refresh_token 需要包含 offline_access 参考 scope 表格 |
response_type | OIDC 模式,可以为 code, id_token, id_token token, code id_token, code token, code id_token token 参考 OIDC 规范 |
prompt | 可以为 none,login,consent 或 select_account,指定 AP 与 End-User 的交互方式,如需 refresh_token,必须为 consent 参考 OIDC 规范 |
state | 一个随机字符串,用于防范 CSRF 攻击,如果 response 中的 state 值和发送请求之前设置的 state 值不同,说明受到攻击 |
nonce | 一个随机字符串,用于防范 Replay 攻击 |
返回示例
{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiJ4R01uczd5cmNFckxiakNRVW9US1MiLCJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJpc3MiOiJodHRwczovL2F1dGhpbmcuY24iLCJpYXQiOjE1NTQ1Mzc4NjksImV4cCI6MTU1NDU0MTQ2OSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBvZmZsaW5lX2FjY2VzcyBwaG9uZSBlbWFpbCIsImF1ZCI6IjVjYTc2NWUzOTMxOTRkNTg5MWRiMTkyNyJ9.wX05OAgYuXeYM7zCxhrkvTO_taqxrCTG_L2ImDmQjMml6E3GXjYA9EFK0NfWquUI2mdSMAqohX-ndffN0fa5cChdcMJEm3XS9tt6-_zzhoOojK-q9MHF7huZg4O1587xhSofxs-KS7BeYxEHKn_10tAkjEIo9QtYUE7zD7JXwGUsvfMMjOqEVW6KuY3ZOmIq_ncKlB4jvbdrduxy1pbky_kvzHWlE9El_N5qveQXyuvNZVMSIEpw8_y5iSxPxKfrVwGY7hBaF40Oph-d2PO7AzKvxEVMamzLvMGBMaRAP_WttBPAUSqTU5uMXwMafryhGdIcQVsDPcGNgMX6E1jzLA", "expires_in": 3600, "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJub25jZSI6IjIyMTIxIiwiYXRfaGFzaCI6Ik5kbW9iZVBZOEFFaWQ2T216MzIyOXciLCJzaWQiOiI1ODM2NzllNC1lYWM5LTRjNDEtOGQxMS1jZWFkMmE5OWQzZWIiLCJhdWQiOiI1Y2E3NjVlMzkzMTk0ZDU4OTFkYjE5MjciLCJleHAiOjE1NTQ1NDE0NjksImlhdCI6MTU1NDUzNzg2OSwiaXNzIjoiaHR0cHM6Ly9hdXRoaW5nLmNuIn0.IQi5FRHO756e_eAmdAs3OnFMU7QuP-XtrbwCZC1gJntevYJTltEg1CLkG7eVhdi_g5MJV1c0pNZ_xHmwS0R-E4lAXcc1QveYKptnMroKpBWs5mXwoOiqbrjKEmLMaPgRzCOdLiSdoZuQNw_z-gVhFiMNxI055TyFJdXTNtExt1O3KmwqanPNUi6XyW43bUl29v_kAvKgiOB28f3I0fB4EsiZjxp1uxHQBaDeBMSPaRVWQJcIjAJ9JLgkaDt1j7HZ2a1daWZ4HPzifDuDfi6_Ob1ZL40tWEC7xdxHlCEWJ4pUIsDjvScdQsez9aV_xMwumw3X4tgUIxFOCNVEvr73Fg", "refresh_token": "WPsGJbvpBjqXz6IJIr1UHKyrdVF", "scope": "openid profile offline_access phone email", "token_type": "Bearer"}验证 access_token 和 id_token 的合法性
OIDC 默认使用 OIDC 应用的 secret 对 token 进行验证(也就是在创建应用时默认选择 HS256 算法)。 如果你使用 javascript 那么可以使用 jsonwebtoken 进行验证:
const jwt = require('jsonwebtoken');let decoded = jwt.verify(token, <appSecret>);如果是其他语言,那么你在服务端需要用 app_secret 作为 HS256 签名参数来计算签名和 JWT 中的签名进行对比,伪代码如下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), "1133fd20c14e4cc29b6ecb71fb8eb952"// app_secret)如果是 RS256 等非对称加密算法,需要使用公钥验证签名。Authing 将使用私钥进行签名,请使用 Authing 的公钥来验证签名:
-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxRijj2seoesv5K0Z+ymRK7DSDPxdsM2sGQD2ZVhLjLsxZWJtXUXh7ERdUU6OT3BqYZZf7CLIhN6yyNtTOgfgpLG9HVJd7ZSKzuy2dS7mo8jD8YRtptAJmNFqw6z8tQp5MNG1ZHqp9isKqJmx/CFYkRdXBmjjj8PMVSP757pkC3jCq7fsi0drSSg4lIxrSsGzL0++Ra9Du71Qe/ODQKU0brxaI1OKILtfcVPTHTaheV+0dw4eYkSDtyaLBG3jqsQbdncNg8PCEWchNzdO6aajUq4wbOzy/Ctp399mz0SGKfuC5S8gqAFABFT3DH3UD21ZztQZwFEV2AlvF+bcGEstcwIDAQAB-----END PUBLIC KEY-----使用 access_token 换取用户信息
开发者在自己的服务中可以使用 access_token 换取用户信息。根据 scope 的不同,这里的返回信息也会有所不同,字段符合 OIDC 规范,字段解释请参考用户信息字段含义。 请求链接:
GET https://users.authing.cn/oauth/oidc/user/userinfo?access_token=<access_token>返回示例:
{ "sub": "<用户在 Authing 的唯一标识>", "nickname": "Authing", "name": "张三", "locale": "en-US"}更多字段解释请参考用户信息字段含义。