近日,在接入第三方登录时,踩了不少坑,尤其接入支付宝和微博时。

主要问(tǔ)题(cáo)包括:

  1. 支付宝各种key太多,不知道API对应哪些keys;
  2. 文档不详细,缺少入参、出参或格式描述;
  3. 接口变更后,未同步更新接口文档;
  4. 微博node.js版本的SDK为个人维护,上次更新停留在9、11年前,处于废弃状态;
  5. 微博应用审核资料的填写过于繁琐、死板,审核效率太低(2-3个工作日);
  6. 接口报错缺少详细原因描述,缺少相关文档链接(比如错误码的链接等,需要搜索);
  7. ……

不过总体上还算顺利,并未花费太多时间。在此,做一汇总,以期给新入坑的小伙伴减少一些不必要的时间。

1、OAuth授权流程

时序图就不画了,麻烦。简单描述下:

  1. 跳转打开第三方登录授权页;
  2. 用户点击授权后携带code跳转到回调页;
  3. 使用code调用接口获取access_token
  4. 使用access_token调用接口获取用户信息;
  5. 判断是否已存在用户,有则生成session,否则生成新用户并生成session;
  6. 结束登录,前台跳转到登录后的成功页或管理页。

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处于不可用状态,因此需要自己根据文档实现接口的调用和响应的处理。