NodeJS使用Koa框架开发对接QQ登陆功能

1,781 阅读6分钟

最近开始更新我的资源网站了,网站用到了QQ登陆功能,因为之前没开发过,因此就去看了官方文档解决了网站的QQ登陆功能,解决开发的过程,那简直是一波三折应该是还是对后端逻辑不太熟悉~ (me 前端开发小菜鸟),在这里记录一下我的整个开发流程,和碰到的问题,分享给有需要的小伙伴

本人确实是文章写的比较少,所以格式排版不太好看,大家多多体谅,后续会去好好学习一哈,嘿嘿

安排.gif

开发准备

  • 注册开发者账号 首先我们需要先去腾讯开发者平台认证注册成为个人开发者 输入网址:https://open.tencent.com/ 然后 点击 QQ开放平台——然后点击顶部的 应用管理会提示你登陆,使用自己的QQ账号登陆后,如果是新用户会提示你注册成为开发者,这里我已经注册并认证成功了,所以我就可以直接创建应用了,我这里是网站使用的,所以我就创建的网站Web'应用,APP小程序申请移动端的进行了 下面看我的截图 image.png

image.png

image.png

image.png

image.png

到这一步基本上就创建完成了一个应用,会有7天的等待,官方会审核检查你填写的信息是否准确,如果都是真实有效的用不了几天审核通过了,就申请到了appid和appkey的。

  • 接入QQ登录时,网站需要不停的和Qzone进行交互,发送请求和接受响应。
    1. 对于PC网站:请在你的服务器上ping graph.qq.com ,保证连接畅通。
  • 2.移动应用无需此步骤

放置“QQ登录”按钮_OAuth2.0

image.png

这里说一下我碰到的几个坑

  1. 网站名称我没有填写我到时候域名备案写的网站名称,于是出了一次错误被驳回
  2. 网站的备案号格式:(地区)蜀ICP备XXXXX号 我填写的格式不正确又一次被驳回
  3. 就是大家可能都比较容易犯错误的,回调地址的填写,刚开始我一直卡这里,总共的填写后面我也会反复给大家强调,在这里就是Api接口地址可以这样去理解,(目前我这样理解,有更好意见的欢迎反馈评论给我) 如我的网址是:lovehaha.cn 我的api接口是 lovehaha.cn/test 那么我在后端写了一个专门处理腾讯qq返回的数据的路由,是 /qqauthor 那么我的回调地址就是: lovehaha/test/qqauthor
  4. 审核的时候,网站需要可以访问,同时需要查看QQ图标的位置是否正确,应在登陆页或首页,同时回调地址的路由可以正常收到腾讯返回的数据。

代码部署

前面都顺顺利利成功了后,需要到开发者平台应用管理哪里先填写个QQ调试账号然后就开始我们的代码配置部署吧!

后端使用的是Node的Koa框架 框架的安装配置很简单(首先肯定需要大家有node环境 我这里是v14.16.1版本的,安装了Node 版本大于10还是几就自带npm了)

命令:

  • npm install koa-generator -g (全局安装koa-generator是koa框架的生成器)
  • koa 文件名称 创建项目
  • npm install 安装依赖包
  • npm run dev 就可以运行了默认应该是3000端口访问

在这里我就简单介绍一下,下面介绍我的后端代码处理逻辑

整体逻辑:

  • 获取Authorization Code
  • 通过Authorization Code 获取 Access Token (Code ————> 换 Token)
  • 通过Access Token 获取 用户的Openid
  • 最后通过获取的 Token 和 Openid 获取用户的信息

PS:(可选)权限自动续期,获取Access Token Access_Token的有效期默认是3个月,过期后需要用户重新授权才能获得新的Access_Token。本步骤可以实现授权自动续期,避免要求用户再次授权的操作,提升用户体验。(官网文档有教程,我这里没用)

/**
 * QQ登陆授权判断
 * code 是前端点击QQ登陆按钮图标然后请求,然后请求这个回调地址 返回的
 * 我这里就可以取到了
 */
router.get('/qqauthor', async (ctx, next) => {
  const { code } = ctx.request.query
  console.log("code", code) // 打印查看是否获取到
  let userinfo
  let openid 
  let item
  if (code) {
  
    let token = await QQgetAccessToken(code) // 获取token 函数 返回 token 并存储
    console.log('返回的token',token)  
    openid = await getOpenID(token)  // 获取 Openid 函数 返回 Openid 并存储
    console.log('返回的openid', openid)   
    if (openid && token) {
      userinfo = await  QQgetUserInfO(token, openid)  // 如果都获取到了,获取用户信息
      console.log("返回的结果", userinfo)
    }
    
  }
  
  // 封装: 
  if (userinfo) {
    let obj = {
      nickname: userinfo.nickname,
      openid: openid,
      gender: userinfo.gender === '男' ? 1 : 2,
      province: userinfo.province,
      city: userinfo.city, 
      year: userinfo.year,
      avatar: userinfo.figureurl_qq_2 ? userinfo.figureurl_qq_2 : userinfo.figureurl_qq_1
    }  
    console.log('封装的obj', obj)
    item = await register({ userInfo: obj, way: 'qq' })
    /** 从这里到封装 都是改变我获取的用户信息存储到数据库里面,根据数据库的存储,创建新用户,如果有
    * 用户我就查询并获取用户的id 然后返回给前端 用户的 id
    */
    ctx.state = {
        id: item.data.id
      }
    await ctx.render('login', ctx.state)  // 如果获取到用户 id 返回 前端一个页面并携带参数 用户ID
  }
})


/**
 * 
 * @param {string} code 
 * @param {string} appId 密钥
 * @param {string} appKey key
 * @param {string} state client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回
 * @param {string} redirectUrl (回调地址)
 * @returns 
 */
async function QQgetAccessToken(code) {
    let result
    let appId = '申请成功就有了'
    let appKey = '申请成功就有了'
    let state = '自定义'
    let redirectUrl = 'https://xxxxx/qqauthor'  // 回调地址是一样的 我这里就是我的获取登陆接口的地址
    
    // 安装了 axios 请求 接口 获取返回的token
    await axios({
      url:`https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=${appId}&client_secret=${appKey}&code=${code}&state=${state}&redirect_uri=${redirectUrl}&fmt=json`,
      method:'GET'
    }).then(res =>{
      console.log(res.data)
      result = res.data.access_token
    //   res.data.access_token
    }).catch(err => {
       console.log(err)
       result = err
    })
    
    return result
}


/**
 * 根据Token获取Openid
 * @param {string} accessToken token 令牌
 * @returns 
 */
async function getOpenID(accessToken) {
    let result
    
    // 跟上面差不多就不解释了
    await axios({
        url: `https://graph.qq.com/oauth2.0/me?access_token=${accessToken}&fmt=json`,
        method: 'GET'
    }).then(res => {
        // 获取到了OpenID
       result = res.data.openid
    }).catch(err => {
       result = err
    })
    
    return result
}


/**
 * 根据Openid 和 Token 获取用户的信息
 * @param {string} accessToken
 * @param {string} openid 
 * @returns 
 */
async function QQgetUserInfO (token, openid) {
  let result    
  await axios({
    url: `https://graph.qq.com/user/get_user_info?access_token=${token}&oauth_consumer_key=101907569&openid=${openid}`,
    method: 'GET'  
  }).then(res => {
      result =  res.data
  }).catch(err => {
    console.log(err)
    result = err
  })
  
  return result
}

前后端调试

前端我这里使用的是Vue2.0的语法去写的上login.vue 页面代码

<template>
<div class="icon" @click="qqAuth">
        <img src="@/static/img/qq48-48.png" alt="" />
        <span>QQ账号登陆</span>
</div>
</template>

// 这里我就直接写
<script>
export default {
methods: {
  // 简单粗暴
  qqAuth () {
      const appId = 申请就有了  
      const redirectUrl = 'https://xxx/qqauthor' // 回调地址 我这里路由是/qqauthor 你的是什么填什么
      const state = 'ahh' // 可自定义
      const display = '' // 可不传仅PC网站接入时使用。用于展示的样式。
      const scope = ''  // 请求用户授权时向用户显示的可进行授权的列表。 可不填
      const url = `
      https://graph.qq.com/oauth2.0/authorize?
      response_type=code&
      client_id=${appId}&
      redirect_uri=${redirectUrl}
      &state=${state}
      &scope=${scope}
      `
      window.open(url, '_blank')  // 开始访问请求 ,这个时候用户点击登陆,就会跳转到qq登陆界面,
      登陆后会返回code 到最开始我们写好的后端接口也就是回调地址哪里,开始操作
  },
}
 </script>

这个时候用户点击登陆触发qqAuth事件,就会跳转到qq登陆界面,登陆成功后会返回code到最开始我们写好的后端接口也就是回调地址哪里,我们把获取Code操作最后获取用户信息存储并返回一个登陆成功的页面携带用户的ID,这个返回的页面,我写了一个 a 标签 携带着 返回的 用户ID

image.png

我这里的href地址是我自己可以访问并且在线上真实的地址,跳转到了首页,我在这个页面的Mounth 写了一个事件 页面加载的时候获取当前页面的URL如果,并且分割URL字符串,判断是否存在ID,存在ID证明是用户登陆成功返回的,获取当前用户的ID,然后再通过ID请求后端,查找到了用户的数据,缓存,完成整个QQ登陆逻辑功能 image.png

完成开发

开发完成了就上线了,但肯定我的这个是存在更优的解决办法,我记录下来,供大家提供一种思路,希望大家可以喜欢,返回页面是使用的Koa的njk框架,比较方便。

总结

哎,水平不行,踩坑踩了好久,记录吧! 睡觉了!