OAuth2协议对接
基本介绍
OAuth 2.0 是一个授权框架,它允许第三方应用在获得用户授权后,代表用户去访问该用户在另一个服务提供商上存储的资源(如个人信息、照片、文件等),而无需将用户的用户名和密码提供给第三方应用。
本文介绍ChatBI系统如何对接客户的OAuth2系统。
OAuth2的核心原理比较好理解,大致流程如下:
- 用户访问ChatBI系统主页(未登录状态)
- ChatBI主页会让浏览器跳转到一个客户SSO平台的授权地址(见开发第二步)。用户会在那边登录。
- 用户登录成功后,SSO平台会根据第二步传过去的redirect_uri,让浏览器跳转到redirect_uri,同时在这个redirect_uri里面附上用户的code。
- redirect_uri一般在我们这边会做成一个项目级别的API。在这个API中,拿到code后通过客户给的获取用户的API(可能会涉及到多个API),得到用户ID。(见开发第四步,sso.js)
- 在这个API中,拿用户ID去生成我们系统的token,然后让用户重定向到token登录页面。
开发第一步:获取必要资料
向客户获取:
- SSO开发文档
- client_id和client_secret(有些地方也叫app_id和app_secret)
开发第二步:生成授权请求链接地址
根据客户的SSO文档,生成生成授权请求链接地址。一般来说,格式都是这样的:
https://xxxxx.com/xxx/oauth2.0/authorize?client_id=xxxxx&redirect_uri=xxxxxx&response_type=code
其中,
- client_id就是第一步获取的内容。
- redirect_uri是SSO平台登录成功后,让SSO平台跳转到的ChatBI这边的API
- response_type=code一般来说都是固定的
开发第三步:将这个url地址配置到系统中
系统配置专家模式:
"SSO": {
"redirectUrl": {
"web": "https://xxxxx.com/xxx/oauth2.0/authorize?client_id=xxxxx&redirect_uri=xxxxxx&response_type=code",
"mobile": "https://xxxxx.com/xxx/oauth2.0/authorize?client_id=xxxxx&redirect_uri=xxxxxx&response_type=code"
}
}
web和mobile可以不一样。如果一样,mobile字段可以不填。
配置完成后,ChatBI系统在未登录的状态下,就会跳转到相应的链接。
开发第四步:
新增一个项目级别的API。建议叫做sso.js。用于执行整个SSO流程
代码框架如下:
import createError from "http-errors";
import axios from "axios";
import { getUser, signToken } from '../../../src/api/v1/auth.js';
// 根据客户给的文档资料配置。可以在docker-compose.yml的app中添加DEV环境变量。达到不同环境配置信息不同的作用。
const config = {
client: {
id: process.env.DEV ? 'xxxx' : 'xxxx',
secret: process.env.DEV ? 'xxxxxxxxx' : 'xxxxxxxxx'
},
auth: {
tokenHost: process.env.DEV ? 'https://xxxxxx' : 'https://xxxxxx',
tokenPath: '/siam/oauth2.0/accessToken',
authorizePath: '/siam/oauth2.0/authorize',
profilePath: '/siam/oauth2.0/profile',
}
};
// debug设为true可以打开调试功能,在服务端的log里面打印出来
const debug = process.env.DEV === '1';
const debugLog = (...args) => {
if (debug) {
console.log(...args);
}
}
export default async (req, res, next) => {
try {
const baseUrl = `${req.protocol}://${req.get('host')}`;
debugLog('baseUrl', baseUrl);
// 构建获取访问令牌请求
const accessTokenResponse = await axios.get(`${config.auth.tokenHost}${config.auth.tokenPath}`, {
params: {
client_id: config.client.id,
client_secret: config.client.secret,
grant_type: 'authorization_code',
code: req.query.code,
redirect_uri: `${baseUrl}/api/project/sso`
}
});
debugLog('accessTokenResponse', accessTokenResponse.data);
const accessToken = accessTokenResponse.data['access_token'];
debugLog('accessToken', accessToken);
// 根据给的文档,找到获取用户信息的API并且调用
const response = await axios.get(`${config.auth.tokenHost}${config.auth.profilePath}?access_token=${accessToken}`);
debugLog(response.data);
// 获得用户信息
const account = await getUser({ username: response.data.id });
if (!account) {
throw new Error('您未在ChatBI中开启账号,请联系技术支持。')
} else {
// 签发token
const token = await signToken(account);
return res.redirect(`/user/login?token=${token}`);
}
} catch (error) {
return next(createError(400, error));
}
}
然后在routes.js中加入该接口:
import sso from './sso.js';
export default [
{
method: 'GET',
path: '/sso',
skipAuth: true,
function: sso,
},
];
重启服务即可。