# oauth手册

⽂档版本 内容修订 修订⼈ 修订⽇期
V1.0 初稿 郭程豪 2023-08-01

# 目录

# 注册服务

登陆用户认证平台(https://id.sansi.net)企业用户使用企业微信扫码登陆,其他用户联系管理员注册

# 判断中控是否开启用户认证

从环境变量中获取SANSI_ACCOUNT_AUTH=http://192.168.31.66:3500变量,如果有值,说明中控已开启了用户认证,需要对请求中的token进行校验

const SANSI_ACCOUNT_AUTH = process.env.SANSI_ACCOUNT_AUTH;

# 接入方式-授权码模式

# 1. 引导用户登陆

重定向页面,第三方应用将页面重定向至用户认证平台登陆页,clientId需提前在用户认证平台注册获取

http://192.168.31.66:3500/login?response_type=code&client_id=ebcfba346a9844ccace356aeb186a826&redirect_uri=http://192.168.158.75:3436&scope=admin,user&state=47298951f14a75110b8fe1
参数名 参数值 是否必填 参数类型 描述说明
response_type code String 返回类型,固定为code
client_id ebcfba346a9844ccace356aeb186a826 String client_id
redirect_uri http://192.168.158.75:3436 String 回调地址
scope admin,user String 范围
state abcde String 唯一性随机码

image1

# 2. 同意授权后,返回code(无需对接)

用户登陆成功后,点击同意授权,页面重定向至 redirect_uri

image2

  • 接口URL: POST http://192.168.31.66:3500/account/api/v1/oauth/authorize
  • Content-Type: multipart/form-data
  • 认证方式: 无需认证
参数名 参数值 是否必填 参数类型 描述说明
response_type code String 返回类型,固定为code
client_id a17446eb12264007ae735ecbd79ffb6c String client_id
redirect_uri http://192.168.158.75:3436 String 回调地址
scope admin,user String 范围
state abcde String 唯一性随机码
  • 回调示例
http://192.168.158.75:3436?code=MZGWNJLKMMITNJNLYY0ZZTA1LTLLZJITMWFHMJI3ZGYYZTVJ&state=47298951f14a75110b8fe1

跳转页面如下 image3

# 3. code换取token

用户认证平台通过回调地址redirect_uri将用户授权码(code)传递给应用服务器或者直接在Webview中,应用服务器使用code换取token

  • 接口URL: POST http://192.168.31.66:3500/account/api/v1/oauth/token
  • Content-Type: multipart/form-data
  • 认证方式: 无需认证

请求示例

import axios from "axios";

const form = new FormData();
form.append("grant_type", "authorization_code");
form.append("client_id", "a17446eb12264007ae735ecbd79ffb6c");
form.append("client_secret", "fca40b86c56548a6ba37db0a8be31a881011ea6f22424cc7850221fc3c92a682cc981df3ec9545bebdcfb188fff2358fa9338db1b8544a0b9c71a8f8529b8031");
form.append("redirect_uri", "http://localhost:3436");
form.append("code", "YJA2MTZJODKTZWM3NI0ZOTIXLTG5ZJMTZDFLZJU0YME1M2ZL");

const options = {
  method: 'POST',
  url: 'http://192.168.31.66:3500/account/api/v1/oauth/token',
  headers: {
    lang: 'zh-CN',
    'content-type': 'multipart/form-data'
  },
  data: form
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});
参数名 参数值 是否必填 参数类型 描述说明
grant_type authorization_code String 固定值
client_id a17446eb12264007ae735ecbd79ffb6c String clientId
client_secret fca40b86c56548a6ba37db0a8be31a881011ea6f224 String clientSecret
redirect_uri http://localhost:3469/kl/api/v1/oauth/callback String 回调地址
code YJA2MTZJODKTZWM3NI0ZOTIXLTG5ZJMTZDFLZJU0YME1M2ZL String 授权码(有效期5分钟)

返回示例

{
  "access_token": "NMRIZDLHNMYTNZI2NS0ZMDMZLTHHNJQTNGNJNWYWZMQ1ZGYY",
  "expires_in": 7200,
  "refresh_token": "NTK5NDY2ZWMTMDGWMC01ZWY3LWJJMJCTZTK5YTBMMMJKNDMX",
  "scope": "admin",
  "token_type": "Bearer"
}

# 接入方式-密码授权模式

代理用户密码登陆,用户应用中输入帐号密码,应用代替用户登陆(服务端请求)

  • 接口URL: POST http://192.168.31.66:3500/account/api/v1/oauth/token
  • Content-Type: multipart/form-data
  • 认证方式: 无需认证

请求示例

import axios from "axios";

const form = new FormData();
form.append("grant_type", "password");
form.append("client_id", "ebcfba346a9844ccace356aeb186a826");
form.append("client_secret", "d80d5182071d43eeac45598ab89a99797a49deb55dec4b4aa009daba01bace27ec77f015abb9435b8c17edb2f06ec62049c0321280fa415689899eed0a385d45");
form.append("username", "xxxxx");
form.append("password", "xxxxx");

const options = {
  method: 'POST',
  url: 'http://192.168.31.66:3500/account/api/v1/oauth/token',
  headers: {
    lang: 'zh-CN',
    'content-type': 'multipart/form-data'
  },
  data: form
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});
参数名 参数值 是否必填 参数类型 描述说明
grant_type password String 返回类型,固定为code
client_id ebcfba346a9844ccace356aeb186a826 String client_id
client_secret d80d5182071d43eeac45598ab... String clientSecret
username 018470 String 账户
password 123456 String 密码

返回示例

{
  "access_token": "ZWFMYMZJNGITOTLINY0ZMGMYLTKZM2ITMTFIYTI3ZDUXNDLI",
  "expires_in": 7200,
  "refresh_token": "ODQXMWQ2YTATYZQXYY01OTU4LTKZMTMTYWJIM2ZHZWVHZGFK",
  "token_type": "Bearer"
}

# 刷新token

用户应用中输入帐号密码,应用代替用户登陆(服务端请求)

  • 接口URL: POST http://192.168.31.66:3500/account/api/v1/oauth/token
  • Content-Type: multipart/form-data
  • 认证方式: 无需认证

请求示例

import axios from "axios";

const form = new FormData();
form.append("grant_type", "refresh_token");
form.append("client_id", "ebcfba346a9844ccace356aeb186a826");
form.append("client_secret", "d80d5182071d43eeac45598ab89a99797a49deb55dec4b4aa009daba01bace27ec77f015abb9435b8c17edb2f06ec62049c0321280fa415689899eed0a385d45");
form.append("refresh_token", "OGQ5N2NHNWYTMDG3NC01M2ZLLTG1ZDCTZJCYZTA5MMYWYTC3");

const options = {
  method: 'POST',
  url: 'http://192.168.31.66:3500/account/api/v1/oauth/token',
  headers: {
    lang: 'zh-CN',
    'content-type': 'multipart/form-data'
  },
  data: form
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});
参数名 参数值 是否必填 参数类型 描述说明
grant_type refresh_token String 返回类型,固定为code
client_id ebcfba346a9844ccace356aeb186a826 String client_id
client_secret d80d5182071d43eeac45598ab89a99... String clientSecret
refresh_token OGQ5N2NHNWYTMDG3NC01M2ZLLTG1ZDCTZJCYZTA5MMYWYTC3 String refreshToken

返回示例

{
  "access_token": "ZWFMYMZJNGITOTLINY0ZMGMYLTKZM2ITMTFIYTI3ZDUXNDLI",
  "expires_in": 7200,
  "refresh_token": "ODQXMWQ2YTATYZQXYY01OTU4LTKZMTMTYWJIM2ZHZWVHZGFK",
  "token_type": "Bearer"
}

# 使用token获取用户信息

应用通过 access_token 获取用户信息,同时会先校验token

  • 接口URL: GET http://192.168.31.66:3500/account/api/v1/oauth/user
  • 认证方式: Bearer auth

请求示例

import axios from "axios";

const options = {
  method: 'GET',
  url: 'http://192.168.31.66:3500/account/api/v1/oauth/user',
  headers: {
    lang: 'zh-CN',
    Authorization: 'Bearer M2I1NWUWNWYTYMQ3NC0ZMZFMLWE1YMITYTRJODEXMJA0NZA2'
  }
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

返回示例

{
  "id": "67c2edc046b54371b54b4284af3050e4", //id
  "userId": "67c2edc046b54371b54b4284af3050e4", //id
  "name": "郭程豪", //名称
  "username": "018470", //账户
  "logo": "https://wework.qpic.cn/wwpic/630634_oZm07_oCQr6j5AU_1690886135/0", //logo
  "email": "", //邮箱
  "mobile": "185xxxx4250", //手机
  "accessToken": "MTA0ZGRJNZATZWE4YS0ZZDGYLWEYNGQTMJGZMDFHMDVMZTFJ",
  "accessTokenCreateAt": "2023-08-31T09:17:29.665587+08:00",
  "accessTokenExpiresIn": 28800000000000,
  "refreshToken": "MTQXMDHMNTGTYTEYNI01NJG0LTLLNJUTNJG3NMZHN2Y0MMNJ",
  "refreshTokenCreateAt": "2023-08-31T09:17:29.665587+08:00",
  "refreshTokenExpiresIn": 604800000000000
}

# 校验token是否有效

  • 接口URL: GET http://192.168.31.66:3500/account/api/v1/oauth/token
  • 认证方式: Bearer auth

请求示例

import axios from "axios";

const options = {
  method: 'GET',
  url: 'http://192.168.31.66:3500/account/api/v1/oauth/token',
  headers: {
    lang: 'zh-CN',
    Authorization: 'Bearer M2I1NWUWNWYTYMQ3NC0ZMZFMLWE1YMITYTRJODEXMJA0NZA2'
  }
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

返回成功示例

{
  "message": "success" //描述
}

返回失败示例

{
  "code": "ERR_INVALID_TOKEN", //返回码
  "message": "Token 无效!" //错误描述
}