Skip to main content

自定义登录方式

目前爱速搭私有版本支持两种方式来对接自己的登录系统。要启用自定义登录方式,需要修改 ISUDA_AUTH_ENABLED_LIST 环境变量。

比如:

ISUDA_AUTH_ENABLED_LIST=email,oauth

表示同时开启邮箱登录和 oauth 登录,具体登录相关配置请接着往下看。

oauth 2.0 登录

首先请确保 ISUDA_AUTH_ENABLED_LIST 中包含了 oauth。

然后配置

  • ISUDA_OAUTH_AUTHORIZE_URL 为 oauth 授权地址,通常是 sso 登录地址如:http://xxx.com/login
  • ISUDA_OAUTH_TOKEN_URL 为 oauth 获取 token 的 api 地址。
  • ISUDA_OAUTH_CLIENT_ID 为 oauth clientId
  • ISUDA_OAUTH_CLIENT_SECRET 为 oauth clientSecret
  • ISUDA_OAUTH_SCOPE 默认值为 email
  • ISUDA_OAUTH_INFO_URL 获取用户资料的接口地址。

在申请 oauth 账号的时候如果需要配置回调地址,请填写 http://{{你的域名}}/auth/oauth/callback

如果退出的时候想同时退出 sso 服务,可以配置:

接入流程

假设你的爱速搭部署在 http://aisuda-server.com 下,然后爱速搭环境变量配置如下:

ISUDA_OAUTH_AUTHORIZE_URL=http://oauth-server.com/oauth
ISUDA_OAUTH_TOKEN_URL=http://oauth-server.com/access_token
ISUDA_OAUTH_CLIENT_ID=dummy_client_id
ISUDA_OAUTH_CLIENT_SECRET=dummy_client_secret
ISUDA_OAUTH_SCOPE=email
ISUDA_OAUTH_INFO_URL=http://oauth-server.com/auth/userinfo

1.首先打开爱速搭登录页面,点击登录框底部的 OAuth 登录,跳转至 http://oauth-server.com/oauth 进行用户校验,地址栏参数大概如下:

http://oauth-server.com/oauth?response_type=code&redirect_uri=http://aisuda-server.com/auth/oauth/callback&client_id=dummy_client_id

2.用户校验成功后,跳转 http://aisuda-server.com/auth/oauth/callback?code=abcd,code 参数就是 oauth 标准中的 authorization_code

3.然后会调用 ISUDA_OAUTH_TOKEN_URL 接口获取 access_token,请求格式如下:

POST https://oauth-server.com/access_token

grant_type=authorization_code
&client_id=dummy_client_id
&client_secret=dummy_client_secret
&redirect_uri=http://aisuda-server.com/auth/oauth/callback
&code=abcd

返回格式如下:

{
"token_type": "Bearer",
"expires_in": 86400,
"access_token": "l7AxzvRf1fxArtvOWSPGtN8XBiF4PSXuBFFDj_6IVOFFb3RCmdClqAt_Z6xUnX2M1p0nJDNm",
"refresh_token": "lDh0jrOTgugCpGC6_TRchD-N",
"scope": "email" // 可以不返回
}

4.然后会调用 http://oauth-server.com/auth/userinfo 接口,获取用户信息进行登录,请求格式如下:

GET http://oauth-server.com/auth/userinfo

Header
Authorization: Bearer l7AxzvRf1fxArtvOWSPGtN8XBiF4PSXuBFFDj_6IVOFFb3RCmdClqAt_Z6xUnX2M1p0nJDNm

接口通过 Authorization 获取到 access_token 校验用户,然后返回用户信息,用户信息如下:

{
"id": "oauth Id",
"name": "用户名",
"displayName": "用户展示名称",
"email": "邮箱地址"
}

如果返回的不是这个格式,可以通过以下配置来适配。

  • ISUDA_OAUTH_ID_FIELD 默认为 id 可以修改为如:user.id
  • ISUDA_OAUTH_NAME_FIELD 默认为 name 同样可以变量路径。
  • ISUDA_OAUTH_DISPLAY_FIELD 默认为 displayName 同样可以变量路径。
  • ISUDA_OAUTH_EMAIL_FIELD 默认为 email 同样可以变量路径。

如果不能返回 email 则会进入绑定邮箱环节,如果想跳过这个环节,可以配置

  • ISUDA_OAUTH_EMAIL_SUFFIX 为 xxx.com 这样,邮箱机会用 name 和 这个值拼接一个。

第三方登录

首先请确保 ISUDA_AUTH_ENABLED_LIST 中包含了 third。

第三方登录功能主要用于对接内部非 oauth 标准协议的登录系统,只需要实现两个接口就能完成对接。

主要有以下配置

  • ISUDA_THIRD_AUTH_AUTHORIZE_URL 为第三方登录地址如:http://xxx.com/login?service={{callback}}
  • ISUDA_THIRD_AUTH_PROFILE_URL 获取用户资料接口如:http://xxx.com/profile?code={{code}}
  • ISUDA_THIRD_AUTH_CODE_FIELD 默认为 code 可以配置成其他如 ticket
  • ISUDA_THIRD_AUTH_LOGOUT_URL 退出地址,如果希望爱速搭退出的时候把第三方登录也退出的话。

登录流程为:

  1. 开发一个登录页面,爱速搭在登录的时候会跳转到这个地址,同时将爱速搭回调地址填入 {{callback}},比如 http://xxx.com/login?service=http://aisuda.com/auth/third
  2. 在这个第三方登录页面里实现用户登录,完成登录后生成一个 code,这里可以是随机数也可以是用户信息的对称加密,后面的接口需要通过它获取真实用户信息。
  3. 从 url 里的 service 参数里从取出爱速搭回调地址,将前面的 code 填入,然后通过 302 跳转到爱速搭回调地址,比如 http://aisuda.com/auth/third?code=xxx
  4. 爱速搭平台会从 code 参数里取出这个值,然后去请求 http://xxx.com/profile?code=xxx 获取用户信息,这个接口需要返回如下内容:
{
"email": "xxx@xx.com",
"username": "",
"displayName": "",
"avatar": "",
"phone": ""
}

如果返回 401 code_expired 则自动重新跳转登录

下面是简单的示例代码:

// 爱速搭跳转的登录
app.get('/login', (req, res) => {
// 这里是内部登录逻辑,比如渲染一个登录界面
res.render('login');

// 如果已经登录,就直接跳转,并将 code 填入
res.redirect(301, req.query.service + '?code=xxx');
});

// 前面那个登录页面界面的提交地址示例
app.post('/login', (req, res) => {
const username = req.body.username;
const password = req.body.password;
// ... 内部实现的登录逻辑

// code 的生成逻辑可以自己控制,这里为了简化使用 jwt,比如还可以使用 redis,这里的 code 是个 uuid
const code = jwt.sign(
{
email: 'xxx@xx.com',
username: '',
displayName: '',
avatar: '',
phone: ''
},
'secret'
);

res.redirect(301, req.query.service + '?code=' + code);
});

// 前面是通过 jwt 生成的,其实用户信息已经写入 payload 了,所以只需要验证一下就返回
app.get('/profile', (req, res) => {
const code = req.query.code;
jwt.verify(token, 'secret', function (err, decoded) {
if (err) {
// 解析错误,可能是过期了
res.send(401, err);
}
return decoded;
});
});