【vue+nestjs】qq第三方授权登录【超详细】

news2025/2/22 20:30:27

项目场景:

前端使用vue3+ts 后端使用nestjs


1.申请appId,appKey

1.进入qq互联官网。创建应用
在这里插入图片描述
特别注意
在填写网站回调域时,需要你线上真实能访问的。不然审核不通过。我的回调地址是前端路由地址

2.代码演示

特别注意:
如果你跟我一样是前后端分离的模式开发的,应用回调地址填写的应该是你的前端路由地址。在你的前端页面获取code,把code值传给后端接口。后端接口通过code获取gitee用户信息。

代码演示

我的应用回调地址:http://localhost:8080/vuecms/qq

  1. 前端点击qq图标登录代码:
<div @click="handleToLogin('qq')">
     gitee
</div>

const handleToLogin = (type:string)=>{
   window.location.href="http://localhost:3000/user/oauth/qq"
}
  1. http://localhost:3000/user/oauth/qq后端接口代码
	@Get('/oauth/qq')
  async qqLogin(@Res() response: Response) {
    let appId = 你的appId;
    let redirectUrl = 你的回调地址;
    const state = Date.now()
    let scope = "get_user_info,list_album"
    return qqOauthConfig.authorizeUrl+`&client_id=${appId}&redirect_uri=${redirectUrl}&state=${state}&scope=${scope}`;
  }
  1. 回调地址前端代码
<template>
    <div class="u-f u-f-ac u-f-ajc" style="width: 100%;height:100vh">
        <template v-if="isOauth">
            <el-result
                    icon="success"
                    title="授权成功,跳转中..."
            >
            </el-result>
        </template>
        <template v-else>
            <el-result
                    icon="error"
                    title="授权失败"
            >
            </el-result>
        </template>
    </div>
</template>

<script setup lang="ts">
    import {useRoute,useRouter} from "vue-router";
    import {onMounted} from "@vue/runtime-core";
    import {requestGiteeLogin} from "@/network/common/oauthPage";
    import {setToken, setUserId, setUsername} from "@/utils/storage";
    import {handleGetCurInstance} from "@/utils/utils";
    import {ref} from "vue"
    let route = useRoute()
    let router = useRouter()
    let query = route.query;
    let {model} = handleGetCurInstance()
    let isOauth = ref(true)
    onMounted(()=>{
    	//获取返回的code,通过code对后端发起请求,获取qq用户信息
         let {code,state} = query;
        let form = {
            code,state
        }
        requestQQLogin(form).then(res=>{
            let {data,code,message} = res;
            if(code==200){
                setToken(data.token)
                setUserId(data.id)
                setUsername(data.username)
                window.location.href="/"
            }else{
                model.handleMsg(message,"warning")
                isOauth.value =false;
            }
        })
    })
</script>
  1. requestQQLogin请求的后端代码
// qq 的认证配置
export const qqOauthConfig = {
  cid: "",//gitee官网设置获取
  secret: "",
  redirectURL: '',//gitee官网配置进行填写
  authorizeUrl: 'https://graph.qq.com/oauth2.0/authorize?response_type=code',
  getAccessTokenUrl: 'https://graph.qq.com/oauth2.0/token?grant_type=authorization_code',
  openId:"https://graph.qq.com/oauth2.0/me",
  qqUserAPI:"https://graph.qq.com/user/get_user_info"
};
	//qq登录
  @Post('/oauth/qqLogin')
  async getQQInfo(@Body() qqLoginDto:QqLoginDto,@IpAddress() clientIp: string) {
      let {code,operationSystem,browser} = giteeLoginDto
    let accessToken:any = await this.handleGetQQAccessToken(code)
    if(!accessToken.data){
      return this.msgService.fail("code过期,请重新登录")
    }
    let giteeInfo:any = await this.getQQInfoByAccessToken(accessToken.data.accessToken,accessToken.data.appId);
    if(!giteeInfo.data){
      return this.msgService.fail("获取qq账号信息失败")
    }
    let { nickname } = giteeInfo.data.userData;
    let clientId = 0;
    let qqId = sysConfigEnum.qqLoginConfig + JSON.parse(JSON.stringify(giteeInfo.data.openid));
    //判断qq是否有关联账号。如果有就登陆,没有就新创建一个账号
    let userNum = await this.userEntity.createQueryBuilder().where({ qqId:qqId }).getCount()
    let username;
    //没有账号,注册帐号
    if(userNum<=0){
      let roleData = await this.roleEntity.createQueryBuilder().where({roleName:"试用角色"}).getOne()
      username = handleGetCode(8);
      username = await this.handleGetUsername(username);
      let originalPwd = handleGetCode(8);
      let password = JSON.parse(JSON.stringify(originalPwd))
      password = securityMd5(password)
      let userData;
      try {
        userData = await this.userEntity.createQueryBuilder().insert().values({username,originalPwd,password,qqId:qqId,roleId:roleData.id}).execute();
      }catch (error) {
        throw new HttpException(error,HttpStatus.SERVICE_UNAVAILABLE)
      }
      clientId = userData.identifiers[0]["id"]
    }else{
      let userData = await this.userEntity.createQueryBuilder().where({qqId:qqId}).getOne()
      username = userData.username
      clientId = userData.id;
    }
    let ip  = handleDealIpv6ToIpv4(clientIp)
    let token = this.authService.createToken({id:clientId,username,ip})
    await this.updateUserInfoStatus(clientId,token,ip,operationSystem,browser)
    return {
      id:clientId,username,token
    }
  }
  
//获取gitee的accessToken
  async handleGetQQAccessToken(code:string):Promise<resInterface>{
    let key = sysConfigEnum.qqLoginConfig
    let data = await this.sysConfigService.handleGetSysData(key)
    if(!data.appId || !data.appKey || !data.redirectUrl){
      return {data:false,msg:""};
    }
    let appId = data.appId;
    let appKey = data.appKey;
    let redirectUrl = data.redirectUrl;//回调路劲获取code
    let authData = await axios.get(qqOauthConfig.getAccessTokenUrl+`&code=${code}&client_id=${appId}&client_secret=${appKey}&redirect_uri=${redirectUrl}`).then(res=>{
      let resArr = res.data.split("&")
      let accessToken = resArr[0].split("=")[1]
      let expiresIn = resArr[1].split("=")[1]
      let refreshToken = resArr[2].split("=")[1]
      return {accessToken,expiresIn,refreshToken};
    }).catch(err=>{
      return err.data
    })
    if(authData?.error){
      return this.msgService.commonRes(false,authData?.error?.error_description);
    }else{
      return this.msgService.commonRes({accessToken:authData?.accessToken,appId},"");
    }
  }
  //通过access_token获取gitee信息
  async getQQInfoByAccessToken(accessToken: boolean | string,appId:string){
    let authData = await axios.get(qqOauthConfig.openId+`?access_token=${accessToken}`).then(res=>{
      let data = JSON.parse(res.data.substring(9, res.data.length-3))
      let clientId = data.client_id;
      let openid = data.openid;
      return {clientId,openid};
    }).catch(err=>{
      return err.data
    })
    if(authData?.error){
      return this.msgService.commonRes(false,authData?.error?.error_description);
    }
    let userData = await axios.get(qqOauthConfig.qqUserAPI+`?access_token=${accessToken}&oauth_consumer_key=${appId}&openid=${authData.openid}`).then(res=>{
      return res.data;
    }).catch(err=>{
      return err.data
    })
    if(userData?.error){
      return this.msgService.commonRes(false,authData?.error?.error_description);
    }else{
      return this.msgService.commonRes({userData,openid:authData.openid},"");
    }
  }

3.特别注意

如果以上步骤都没问题。需要把本地测试回调地址改为线上路径

如果你还是不懂,你可以克隆下我的项目。开源免费。如果对你有帮助,给我一个star就行了
https://gitee.com/derekgo/vue-cms_xg

踩坑不易,还希望各位大佬支持一下 \textcolor{gray}{踩坑不易,还希望各位大佬支持一下} 踩坑不易,还希望各位大佬支持一下

📃 个人主页: \textcolor{green}{个人主页:} 个人主页: 沉默小管

📃 个人网站: \textcolor{green}{个人网站:} 个人网站: 沉默小管

📃 个人导航网站: \textcolor{green}{个人导航网站:} 个人导航网站: 沉默小管导航网

📃 我的开源项目: \textcolor{green}{我的开源项目:} 我的开源项目: vueCms.cn

🔥 技术交流 Q Q 群: 837051545 \textcolor{green}{技术交流QQ群:837051545} 技术交流QQ群:837051545

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

如果有不懂可以留言,我看到了应该会回复
如有错误,请多多指教

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1116936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

17、监测数据采集物联网应用开发步骤(12.2)

阶段性源码将于本章节末尾给出下载 监测数据采集物联网应用开发步骤(12.1) 新建web数据接口http-request解析类com.zxy.tcp.Request.py #! python3 # -*- coding: utf-8 -Created on 2017年05月10日 author: zxyong 13738196011 import urllib.parse,json from com.zxy.comm…

华为OD机试 - 根据某条件聚类最少交换次数 - 滑动窗口(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

解决ubuntu系统运行pyside2或6的问题

解决ubuntu系统运行pyside2或6时出现的问题 当运行程序时&#xff0c;出现“qt.qpa.plugin: Could not load the Qt platform plugin “xcb” in “/usr/local/lib/python3.6/dist-packages/cv2/qt/plugins” even though it was found. This application failed to start bec…

全志R128软件配置——RTOS 软件包配置

RTOS 软件包配置 本文将介绍 RTOS 软件包、地址&#xff0c;内核配置等。 Kconfig 简介 有过 linux 内核开发经验的人&#xff0c;对 menuconfig 不会陌生。对于各类内核&#xff0c;只要是支持 menuconfig 配置界面&#xff0c;都是使用 Kconfig。 换言之&#xff1a; me…

c++_learning-c++标准库STL和boost库

c的标准库 STL标准库&#xff1a;#include<iostream>&#xff1a;#include<iomanip>&#xff1a;#include<cstdlib>&#xff1a;#include<cmath>&#xff1a;#include<tuple>&#xff1a;利用可变参数模板&#xff0c;借助“递归继承”或“递归组…

AD画板时,元器件跑到屏幕左下角,看不见啦,咋办?

解决办法&#xff1a; EDIT---------------->SELECT---------------->ALL 然后鼠标选中&#xff0c;整体移动----》OK

手机主流存储器件的分析与发展

一、前言 存储器件作为系统中存储数据的物理单元&#xff0c;承担着非常重要的责任&#xff0c;它的运行状态时刻影响着整个系统的运行效率&#xff0c;存储容量和数据安全。所以整个产业针对存储器件的寿命&#xff0c;稳定性&#xff0c;容量&#xff0c;性能以及价格等方面进…

紫光展锐携中国联通完成RedCap芯片V517孵化测试

近日&#xff0c;紫光展锐携手中国联通5G物联网OPENLAB开放实验室&#xff08;简称“OPENLAB实验室”&#xff09;共同完成RedCap芯片V517创新孵化&#xff0c;并实现在联通5G全频段3.5GHz、2.1GHz、900MHz下的端到端业务验证测试。 V517是一款基于紫光展锐5G成熟平台设计与研发…

电脑技巧:推荐八个实用的在线学习网站

目录 1、程序员英语词汇宝典 2、国图公开课 4、Maspeak 5、Visuwords 6、Learning Music 7、考试酷 8、好知网 今天给大家分享8个非常使用的学习网站&#xff0c;值得收藏&#xff01; 1、程序员英语词汇宝典 官网&#xff1a;https://learn-english.dev/ 程序员英语词…

【论文解读】Prefix-Tuning: Optimizing Continuous Prompts for Generation

一.介绍 1.1 前置知识 1.1.1 in-context learning At the limit, GPT-3 (Brown et al, 2020) can be deployed using in-context learning, which is a form of prompting, without modifying any LM parameters. "部署" 指的是将 GPT-3 模型用于实际应用或特定任务…

项目添加以vue为后缀名的vue文件,怎么解析打包

我们都知道&#xff0c;将css文件打包起来&#xff0c;需要加载css-loader和style-loader&#xff0c;那么vue文件打包也需要 下载插件&#xff1a; npm install vue-loader vue-template-compiler --save -dev 下载过程&#xff1a; 下载成功样子&#xff1a; 下载完之后&am…

学信息系统项目管理师第4版系列33_信息化发展

1. 企业信息化发展战略要点 1.1. 【高22下选12】 1.2. 以信息化带动工业化 1.3. 信息化与企业业务全过程的融合、渗透 1.4. 信息产业发展与企业信息化良性互动 1.5. 充分发挥政府的引导作用 1.6. 高度重视信息安全 1.7. 企业信息化改组改造和形成现代企业制度有机结合 …

Leetcode 剑指 Offer II 049. 求根节点到叶节点数字之和

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个二叉树的根节点 root &#xff0c;树中每个节点都存放有…

基于孔雀优化的BP神经网络(分类应用) - 附代码

基于孔雀优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于孔雀优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.孔雀优化BP神经网络3.1 BP神经网络参数设置3.2 孔雀算法应用 4.测试结果&#xff1a;5.M…

基于SpringBoot的招生管理系统

基于SpringBoot的招生管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 登录界面 管理员界面 用户界面 摘要 基于SpringBoot的招生管理系统是一款现…

基于战争策略优化的BP神经网络(分类应用) - 附代码

基于战争策略优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于战争策略优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.战争策略优化BP神经网络3.1 BP神经网络参数设置3.2 战争策略算法应用 4.测试结果…

mysqld: File ‘./binlog.index‘ not found (OS errno 13 - Permission denied) 问题解决

问题背景 Centos7 安装Mysql 8后启动时遇到的问题&#xff0c;看了好几个博客方案无效&#xff0c;搞了半小时才找到正解&#xff0c;在此次进行记录。 在此假设你已经修改了对应目录的权限&#xff0c;比如配置的mysql data目录初始化后已经执行了chown -R mysql:mysql /XXX/…

bug记录——设置了feign的fallback,但是没有生效

问题描述 feign的代码 package com.tianju.order.feign;import com.tianju.order.feign.fallback.StorageFallback; import com.tinaju.common.dto.GoodsDto; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMap…

虹科分享 | 超低温冷冻箱温度分布验证的9步指南

虹科分享 | 超低温冷冻箱温度分布验证的9步指南 背景&#xff1a; 在生物制药行业&#xff0c;温度分布验证是确保对时间和温度敏感的产品在保证质量和安全的条件下储存和运输的关键步骤。这对于超低温冷冻箱尤为重要&#xff0c;因为超低温冷冻箱用于在低于 -60℃ 的温度下储…

[人工智能-综述-13]:第九届全球软件大会(南京)有感 -2-新型的云服务:AI即服务,传统的云服务:IaaS,PaaS,SaaS, DaaS

目录 一、传统的云服务 1.1 概述 1.2 从大数据云服务走向AI云服务 二、AI即服务&#xff1a;新型的云服务 1.1 概述 1.2 基于AI服务的应用程序 1.3 基于大语言模型的AI应用程序 1.4 AI 编程云服务平台 1.5 大模型在AI应用程序编程平台中的应用的主要思想 一、传统的…