![yma16-logo](https://i-blog.csdnimg.cn/blog_migrate/3c822665b8199f151b94601d552d1d31.gif#pic_center)
⭐前言
大家好,我是yma16,本文分享OAuth规则机制下实现个人站点接入qq三方登录。
oauth授权
OAuth是一种授权机制,用于允许用户(资源所有者)向第三方应用程序授予有限的访问权限,而不必将凭证直接提供给第三方应用程序。OAuth的目的是为了保护用户的私密数据,如社交媒体帐户、云存储、银行帐户等。它通过一个流程,将用户授权给第三方应用程序访问用户的资源,而不需要第三方应用程序获得用户的凭证信息。这样做可以减少用户数据泄露的风险。OAuth是一个开放的标准,由OAuth工作组维护,并得到许多组织的支持和使用。
oauth的发展
OAuth协议的发展历史可以追溯到2004年,当时美国国防部提出了一个名为“OpenID Connect”的开放式身份认证和授权标准,旨在解决Web 2.0中的身份认证和授权问题。OAuth1.0于2005年被RFC 5849正式标准化,OAuth2.0于2011年被RFC 6749正式标准化 。
OAuth1.0的主要特点是将用户的认证信息(如用户名和密码)与第三方应用的请求分离开来,从而提高了安全性。
OAuth2.0则在OAuth1.0基础上进一步改进,增加了更多的功能和灵活性,如授权码模式、隐式模式、密码模式等 。
效果
在个人站点实现三方qq登录
链接直达:https://yongma16.xyz
唤起三方登录url
获取qq用户账号头像和openid登入
⭐qq三方登录流程
前提条件: 存在可访问的网站,在qq互联中心创建应用审核
友情提示:网站不可使用外部cdn,可导致审核人员查看白屏而失败
💖qq互联中心创建网页应用
填写域名资料,提交审核
💖配置回调地址redirect_uri
回调地址是用户使用qq登录之后调整的地址会携带code和state的参数
💖流程分析
- 唤起qq授权登录url
- 登录qq成功获取code
- 通过code去换取access_token
- 通过access_token去换取openid
- 通过access_token和openid去换取userinfo
⭐思路分解
1.登录页面新开窗口的auth授权qq页面
2.自定义node服务去渲染回调redirect_uri,成功登录时回传url上的参数给父页面,然后用定时器关闭页面
3. 拿到code后去换取token
4. 拿到token去换取openid
5. 拿到openid去换取userinfo
6. 使用openid去注册网站用户,显示nickname网名
⭐技术选型+实现
💖技术选型:
后端:
node
前端:
vue2
后端node封装接口
💖实现
node封装接口:
api.js
const request = require('request');
const loginUrl='https://graph.qq.com/oauth2.0/authorize'
const codeToTokenUrl='https://graph.qq.com/oauth2.0/token'
const openIdUrl='https://graph.qq.com/oauth2.0/me'
const userInfoUrl='https://graph.qq.com/user/get_user_info'
const appid=自己的appid
const appKey=自己的appkey
const redirect_uri=自己的回调地址
const getAuthUrl=(state)=>{
return new Promise(resolve=>{
const params={
response_type:'code',
client_id:appid,
redirect_uri:encodeURI(redirect_uri),
state:state?state:'myblog',
};
const path=Object.keys(params).forEach(key=>{
return `${key}=${params[key]}`
}).join('&')
const url=loginUrl+'?'+path
resolve(url)
})
};
const getToken=(code)=>{
return new Promise(async ( resolve ,reject)=> {
request(
{
method: 'GET'
, uri: codeToTokenUrl,
qs: {
grant_type: 'authorization_code',
client_id: appid,
client_secret: appKey,
code: code,
redirect_uri: encodeURI(redirect_uri),
fmt: 'json'
}
}, function (error, response) {
if (!error && response.statusCode === 200) {
resolve(response)
} else {
console.log("error",error);
reject(reject)
}
})
}
)
};
const getOpenId=(access_token)=>{
return new Promise(async ( resolve ,reject)=> {
request(
{
method: 'GET'
, uri: openIdUrl,
qs: {
access_token:access_token,
fmt: 'json'
}
}, function (error, response) {
if (!error && response.statusCode === 200) {
resolve(response)
} else {
reject(error)
}
})
}
)
};
const getUserInfo=(access_token,openId)=>{
return new Promise(async ( resolve ,reject)=> {
request(
{
method: 'GET'
, uri: userInfoUrl,
qs: {
access_token:access_token,
oauth_consumer_key:appid,
openid:openId,
fmt: 'json'
}
}, function (error, response) {
if (!error && response.statusCode === 200) {
resolve(response)
} else {
reject(error)
}
})
}
)
}
module.exports={
getAuthUrl,
getToken,
getOpenId,
getUserInfo
};
node开放接口:
const hostname = 'localhost';
const port = 6677;
const express = require("express");
const {getAuthUrl,getToken,getOpenId,getUserInfo}=require('./service/api.js');
const app = express();
app.listen(port,hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
// server your css as static
app.use(express.static(__dirname));
// views
const thirdLoginDir='/views/thirdLogin/';
app.get("/getAuthUrl",async (req, res) => {
try{
const {query}=req
const {state}=query
const url=await getAuthUrl(state)
res.json({
code:authRes.statusCode,
data:url,
})
}
catch (e) {
res.json({
code:0,
msg:e
})
}
});
app.get("/getToken",async (req, res) => {
try{
const {query}=req
const {code}=query
console.log('code',code)
const tokenRsponse=await getToken(code)
res.json({
code:tokenRsponse.statusCode,
data:JSON.parse(tokenRsponse.body),
})
}
catch (e) {
res.json({
code:0,
msg:e
})
}
});
app.get("/getOpenId",async (req, res) => {
try{
const {query}=req
const {access_token}=query
console.log('access_token',access_token)
const openidRes=await getOpenId(access_token)
res.json({
code:openidRes.statusCode,
data:JSON.parse(openidRes.body)
})
}
catch (e) {
res.json({
code:0,
msg:e
})
}
});
app.get("/quickGetOpenId",async (req, res) => {
try{
const {query}=req
const {code}=query
console.log('code',code)
const tokenRsponse=await getToken(code)
const tokenBody=JSON.parse(tokenRsponse.body)
const {access_token}=tokenBody
const openIdRsponse=await getOpenId(access_token)
res.json({
code:tokenRsponse.statusCode,
data:JSON.parse(openIdRsponse.body)
})
}
catch (e) {
res.json({
code:0,
msg:e
})
}
});
app.get("/getUserInfo",async (req, res) => {
try{
const {query}=req
const {access_token,openId}=query
console.log('access_token openId',access_token,openId)
const userInfoRes=await getUserInfo(access_token,openId)
res.json({
code:userInfoRes.statusCode,
data:JSON.parse(userInfoRes.body)
})
}
catch (e) {
res.json({
code:0,
msg:e
})
}
});
app.get("/qq_login_callback", (req, res) => {
res.sendFile(__dirname + thirdLoginDir+"qqLoginCallback.html");
});
app.get("/azure_login_callback", (req, res) => {
res.sendFile(__dirname + thirdLoginDir+"azureLoginCallback.html");
});
回调html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>qqLoginCallback</title>
</head>
<body>
qq login success!
<script type="application/javascript">
function init() {
console.log('qq success login')
console.log('window.location.href',window.location.href)
const href=window.location.href
const data={}
const urlArray=href.split('?')
const urlLength=urlArray.length
if(urlLength>1){
urlArray[urlLength-1].split('&').forEach(item=>{
const itemArray=item.split('=')
const key=itemArray[0]
const value=itemArray[1]
data[key]=value
})
}
if(window.opener)
{
//发送data
window.opener.postMessage(data,'*')
//关闭
setTimeout(()=>{
window.close()
},1000)
}
}
window.onload=init
</script>
</body>
</html>
vue前端qq登录的调用:
async qqLogin () {
try {
const that = this
// qq
const res = await that.$axios
.post('/third-login/getAuthUrl')
console.log('res', res)
if (res.data && res.data.data) {
const resData = res.data.data
console.log('resData', resData)
if (resData ) {
let url = resData
console.log('url', url)
const openHandle = window.open(url, 'width:800px;height:700px', '_black')
console.log('openHandle', openHandle)
window.onmessage = async (res) => {
const {origin, data} = res
if (origin.includes('yongma16.xyz')) {
const {code, state} = data
console.log('code state', code, state)
that.thirdLoginConfig.qCode = code
that.thirdLoginConfig.qState = state
const tokenRes = await that.getAccessToken(code)
console.log('tokenRes', tokenRes)
}
}
}
}
return new Promise(resolve => {
resolve(true)
})
} catch (e) {
return new Promise((resolve, reject) => {
reject(e)
})
}
},
async getAccessToken (code) {
try {
const tokenUrl = '/third-login/getToken'
const params = {
code
}
const tokenRes = await this.$axios.get(tokenUrl, {params})
console.log('tokenRes', tokenRes)
if (tokenRes) {
const {access_token, expires_in, refresh_token} = tokenRes.data.data
if (access_token) {
this.thirdLoginConfig.qToken = access_token
await this.getOpenId(access_token)
}
}
} catch (e) {
console.log('token error', e)
}
},
async getOpenId (token) {
try {
const tokenUrl = '/third-login/getOpenId'
const params = {
access_token: token
}
const openIdRes = await this.$axios.get(tokenUrl, {params})
console.log('openIdRes', openIdRes)
if (openIdRes) {
const {openid} = openIdRes.data.data
if (openid) {
this.thirdLoginConfig.qOpenid = openid
await this.getQUserinfo(this.thirdLoginConfig.qToken, openid)
}
}
} catch (e) {
console.log('token error', e)
}
},
async getQUserinfo (token, openId) {
try {
const userInfoUrl = '/third-login/getUserInfo'
const params = {
access_token: token,
openId: openId
}
const userRes = await this.$axios.get(userInfoUrl, {params})
if (userRes) {
this.thirdLoginConfig.qUserInfo = userRes.data.data
this.registerThirdLogin()
}
console.log('userRes', userRes)
} catch (e) {
console.log('token error', e)
}
}
效果:
⭐结束
本文分享到这结束,如有错误或者不足之处欢迎指出!
👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 感谢你的阅读!