NodeJS使用Koa框架开发对接QQ登陆功能
开发准备
- 注册开发者账号
首先我们需要先去腾讯开发者平台认证注册成为个人开发者 输入网址:https://open.tencent.com/ 然后 点击 QQ开放平台——然后点击顶部的 应用管理会提示你登陆,使用自己的QQ账号登陆后,如果是新用户会提示你注册成为开发者,这里我已经注册并认证成功了,所以我就可以直接创建应用了,我这里是网站使用的,所以我就创建的网站Web'应用,APP小程序申请移动端的进行了 下面看我的截图
到这一步基本上就创建完成了一个应用,会有7天的等待,官方会审核检查你填写的信息是否准确,如果都是真实有效的用不了几天审核通过了,就申请到了appid和appkey的。
- 接入QQ登录时,网站需要不停的和Qzone进行交互,发送请求和接受响应。
- 对于PC网站:请在你的服务器上ping graph.qq.com ,保证连接畅通。
- 2.移动应用无需此步骤
放置“QQ登录”按钮_OAuth2.0
这里说一下我碰到的几个坑
- 网站名称我没有填写我到时候域名备案写的网站名称,于是出了一次错误被驳回
- 网站的备案号格式:(地区)蜀ICP备XXXXX号 我填写的格式不正确又一次被驳回
- 就是大家可能都比较容易犯错误的,回调地址的填写,刚开始我一直卡这里,总共的填写后面我也会反复给大家强调,在这里就是Api接口地址可以这样去理解,(目前我这样理解,有更好意见的欢迎反馈评论给我) 如我的网址是:lovehaha.cn 我的api接口是 lovehaha.cn/test 那么我在后端写了一个专门处理腾讯qq返回的数据的路由,是 /qqauthor 那么我的回调地址就是: lovehaha/test/qqauthor
- 审核的时候,网站需要可以访问,同时需要查看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
我这里的href地址是我自己可以访问并且在线上真实的地址,跳转到了首页,我在这个页面的Mounth 写了一个事件
页面加载的时候获取当前页面的URL如果,并且分割URL字符串,判断是否存在ID,存在ID证明是用户登陆成功返回的,获取当前用户的ID,然后再通过ID请求后端,查找到了用户的数据,缓存,完成整个QQ登陆逻辑功能