近日,在接入第三方登录时,踩了不少坑,尤其接入支付宝和微博时。
主要问(tǔ)题(cáo)包括:
- 支付宝各种key太多,不知道API对应哪些keys;
- 文档不详细,缺少入参、出参或格式描述;
- 接口变更后,未同步更新接口文档;
- 微博node.js版本的SDK为个人维护,上次更新停留在9、11年前,处于废弃状态;
- 微博应用审核资料的填写过于繁琐、死板,审核效率太低(2-3个工作日);
- 接口报错缺少详细原因描述,缺少相关文档链接(比如错误码的链接等,需要搜索);
- ……
不过总体上还算顺利,并未花费太多时间。在此,做一汇总,以期给新入坑的小伙伴减少一些不必要的时间。
1、OAuth授权流程
时序图就不画了,麻烦。简单描述下:
- 跳转打开第三方登录授权页;
- 用户点击授权后携带
code
跳转到回调页; - 使用
code
调用接口获取access_token
; - 使用
access_token
调用接口获取用户信息; - 判断是否已存在用户,有则生成session,否则生成新用户并生成session;
- 结束登录,前台跳转到登录后的成功页或管理页。
2、支付宝
支付宝相对比较完善,有各种SDK,直接使用即可。
controller
:
typescriptCopy code- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
const alipayToken: AlipayToken = await this.alipayService.getAccessToken(payload.authCode);
this.logger.sysLogger.info({ payload, token: alipayToken });
const alipayUser = await this.alipayService.getUserInfo(alipayToken.accessToken);
if (!alipayUser || !alipayUser.userId) {
throw new InternalServerErrorException(Message.THIRD_PARTY_LOGIN_ERROR, ResponseCode.THIRD_PARTY_LOGIN_ERROR);
}
userId = await this.alipayService.checkAlipayUserExist(alipayUser.userId);
if (!userId) { // if not exist, create a new
this.logger.sysLogger.info(alipayUser);
isNew = true;
userId = await this.userService.saveAlipayUser(alipayUser);
user.name = alipayUser.nickName;
}
const alipayToken: AlipayToken = await this.alipayService.getAccessToken(payload.authCode);
this.logger.sysLogger.info({ payload, token: alipayToken });
const alipayUser = await this.alipayService.getUserInfo(alipayToken.accessToken);
if (!alipayUser || !alipayUser.userId) {
throw new InternalServerErrorException(Message.THIRD_PARTY_LOGIN_ERROR, ResponseCode.THIRD_PARTY_LOGIN_ERROR);
}
userId = await this.alipayService.checkAlipayUserExist(alipayUser.userId);
if (!userId) { // if not exist, create a new
this.logger.sysLogger.info(alipayUser);
isNew = true;
userId = await this.userService.saveAlipayUser(alipayUser);
user.name = alipayUser.nickName;
}
getAccessToken
:
typescriptCopy code- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
async getAccessToken(code: string, refreshToken?: string): Promise<AlipayToken> {
await this.initSdk();
try {
const payload: Record<string, any> = {
grantType: 'authorization_code',
code: code
};
refreshToken && (payload['refreshToken'] = refreshToken);
return await this.alipaySdk.exec('alipay.system.oauth.token', payload);
} catch (e) {
this.logger.error({
message: 'get alipay access token error',
data: {
param: { code },
res: e
}
});
let message = Message.THIRD_PARTY_LOGIN_ERROR;
try {
const res = JSON.parse(e.serverResult.data);
message = res.error_response.sub_msg || message;
} catch (e) {
}
throw new InternalServerErrorException(message);
}
}
async getAccessToken(code: string, refreshToken?: string): Promise {
await this.initSdk();
try {
const payload: Record = {
grantType: 'authorization_code',
code: code
};
refreshToken && (payload['refreshToken'] = refreshToken);
return await this.alipaySdk.exec('alipay.system.oauth.token', payload);
} catch (e) {
this.logger.error({
message: 'get alipay access token error',
data: {
param: { code },
res: e
}
});
let message = Message.THIRD_PARTY_LOGIN_ERROR;
try {
const res = JSON.parse(e.serverResult.data);
message = res.error_response.sub_msg || message;
} catch (e) {
}
throw new InternalServerErrorException(message);
}
}
getUserInfo
:
typescriptCopy code- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
async getUserInfo(token: string): Promise<AlipayUser> {
await this.initSdk();
try {
return await this.alipaySdk.exec('alipay.user.info.share', {
auth_token: token
});
} catch (e) {
this.logger.error({
message: 'get alipay user info error',
data: {
param: { token },
res: e
}
});
let message = Message.THIRD_PARTY_LOGIN_ERROR;
try {
const res = JSON.parse(e.serverResult.data);
message = res.error_response.sub_msg || message;
} catch (e) {
}
throw new InternalServerErrorException(message);
}
}
async getUserInfo(token: string): Promise {
await this.initSdk();
try {
return await this.alipaySdk.exec('alipay.user.info.share', {
auth_token: token
});
} catch (e) {
this.logger.error({
message: 'get alipay user info error',
data: {
param: { token },
res: e
}
});
let message = Message.THIRD_PARTY_LOGIN_ERROR;
try {
const res = JSON.parse(e.serverResult.data);
message = res.error_response.sub_msg || message;
} catch (e) {
}
throw new InternalServerErrorException(message);
}
}
3、微博
因为SDK处于不可用状态,因此需要自己根据文档实现接口的调用和响应的处理。