最近在开发博客网站登录过程中,涉及到了多个前端对应一个后端的前后端分离项目如何使用OAuth完成第三方授权登录的问题,特此总结一篇文章,详细记录了完整的开发过程思路分析和具体的代码实现,大家需要相同的业务场景时可参考使用。
举个例子,当你想要上班摸鱼逛知乎时,但是你从来没有注册过知乎账号,而且你又嫌麻烦不愿意注册知乎账号,此时就可以使用第三方社交账号登录,例如使用QQ账号授权登录后,会自动登录知乎账号,并将自己的用户名、性别、头像等基本信息就会保存在知乎平台做账号绑定。
首先说明一点,OAuth 不是一个API或者公共服务,而是一个验证授权的开放标准,只要授权方和被授权方遵守这个协议去写代码提供服务,那双方就是实现了OAuth模式。目前可以提供OAuth的平台有很多,他们都遵从这个标准,实现了自己的OAuth功能。虽然OAuth指定了明确的标准,但是各家的使用方式还是略有差异。
OAuth主要有OAuth 1.0、OAuth 1.0a、OAuth 2.0三个版本。OAuth 1.0a主要是修复了 OAuth 1.0的安全 问题,OAuth 2 是为了解决 OAuth 1.0a 过于复杂的问题。OAuth2.0 是目前广泛使用的版本,目前第三方平台也都是基于OAuth2标准开放服务。
上述例子中的知乎就是客户端,QQ就是认证服务器,OAuth2.0就是客户端和认证服务器之间为了解决相互不信任而产生的一个授权协议。(要是相互信任那豆瓣直接读取QQ的数据库登录不就好了,搞这么费劲作甚)
整个流程分为以下三个阶段
① 用户点击QQ登录进入授权页面同意授权,登录完成后获取到code;
② 知乎网站请求QQ服务器,通过code换取授权access_token;
③ QQ通过网页授权access_token向知乎返回用户的基本信息。
上面举例仅是简单的业务场景,前后端不分离项目开发思路。但是遇到多个前端对应一个后端的前后端分离项目,开发的流程思路还是略有差异。
完整的设计思路如下:
前端的工作主要有两部分,分别是登录页和回调页
登录页放置第三方登录按钮,当用户点击登录后,后后端API接口传入登录平台、应用类型(桌面端还是移动端)两个参数,获取到client id,然后根据不同的第三方登录平台要求拼接URL地址,跳转到第三方登录页
回调页功能是当用户完成登录授权后,会跳转到回调页,从URL中获取到code参数,传递给后端。等待后端完成登录处理后,获取到用户id和token,并保存到local storage或者session storage中
后端的工作主要有两部分,分别是查询应用client id和完成用户登录
查询应用client id为一个接口,用于登录页请求。根据前端传入的登录平台、应用类型两个参数,返回应用的client id
用户登录为另一个API接口,用户回调页请求。用户传入code后,请求第三方平台OAuth接口,获取用户openid。然后判断当前用户是否已注册过账号(已注册——>直接登录;未注册——>获取用户信息并创建用户然后登录)并返回给前端用户id和token
微博申请地址:https://open.weibo.com/
微博登录文档:https://open.weibo.com/wiki/Connect/login
新浪微博需要实名认证,只认证身份信息,不验证回调地址等信息是否正确。等审核通过再修改回调地址。在审核期间修改回调地址不生效。
支付宝申请地址https://open.alipay.com/
支付宝官方文档:https://opendocs.alipay.com/support/01rg6a
支付宝同样也需要实名认证,但也是只验证身份信息。但调用支付宝OAuth时需要使用支付宝sdk完成。
调用sdk建立连接是需要传入KEY、PRIVATE_KEY、PUBLIC_KEY三个参数,其中PUBLIC_KEY从应用信息——>接口加签方式中查看,PRIVATE_KEY是创建应用时上传的证书key
qq申请地址https://connect.qq.com/
qq文档:https://wiki.connect.qq.com/oauth2-0%e5%bc%80%e5%8f%91%e6%96%87%e6%a1%a3
QQ审核除了实名认证外,还审核应用的信息,记得应用名称填写备案号上的应用名称,并且要在应用中添加QQ登录按钮和跳转链接才能审核通过,审核通过后可以修改回调地址。
百度申请地址:http://developer.baidu.com/console#app/project
百度文档:https://openauth.baidu.com/doc/regdevelopers.html
github申请地址:https://github.com/settings/developers
github官方文档:https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps
微软申请地址:https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
微软参考文档:https://docs.microsoft.com/zh-cn/graph/auth-v2-user
有个tenant参数。根据自己的账户权限,传入具体的值(文档示例和网上的资料都是common,但不一定适合自己的账户权限)
将第三方平台创建的应用key和secret保存到setting配置中,后续函数处理时直接调用setting配置即可。
# 第三方登录
AUTH = {
'WEIBO': {
'PC': {
'KEY': '110*****1',
'SECRET': 'c37a*****c38f',
},
'M': {
'KEY': '27*****52',
'SECRET': 'b41*****2acd'
}
},
'QQ': {
'PC': {
'KEY': '10*****3',
'SECRET': 'c3*****d99',
},
'M': {
'KEY': '1*****5',
'SECRET': '32*****5fb'
}
},
'PAY': {
'PC': {
'KEY': '2021*****589',
'SECRET': '',
'PRIVATE_KEY': 'M2*********M=',
'PUBLIC_KEY': 'MI****QAB'
},
'M': {
'KEY': '202****70',
'SECRET': '',
'PRIVATE_KEY': 'MI*****************KW4=',
'PUBLIC_KEY': 'MI******************8IQAB'
}
},
'GITHUB': {
'PC': {
'KEY': 'be2**************89',
'SECRET': '5b1**************185',
},
'M': {
'KEY': '92*********************8a',
'SECRET': '154**************8982ab'
}
},
'BAIDU': {
'PC': {
'KEY': '44T*******************lN',
'SECRET': 'qEk***************VQTz2',
},
'M': {
'KEY': 'hh4****************EaG',
'SECRET': 'u1RV*************7okSbM'
}
},
'MICROSOFT': {
'PC': {
'KEY': '61e*************84049',
'SECRET': 'XBk***************P~wK',
},
'M': {
'KEY': 'b966******************61f8',
'SECRET': '59J**************iciGJrFr'
}
},
}
模型这儿设计了三张表,分别是用户来源、用户信息、用户第三方登录用户ID。
用户来源表主要记录用户是第三方登录还是直接注册的,并提前在用户来源表中输入相关记录。与用户信息表source字段一对多关联。
用户信息表通过使用AbstractUser可以对Django内置的User进行扩展使用,添加一些用户自定义的属性字段。
第三方登录用户ID用于记录用户ID与第三方平台对应关系,判断用户是否已注册绑定过账号。
from django.contrib.auth.models import User, AbstractUser
from django.db import models
from django.conf import settings
class UserSource(models.Model):
name = models.CharField('来源', max_length=100)
class Meta:
verbose_name = '注册用户来源'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class UserInfo(AbstractUser):
source = models.ForeignKey(UserSource, verbose_name='用户来源', on_delete=models.CASCADE, default=1)
phone = models.CharField(verbose_name='手机号', max_length=20, blank=True, null=True)
sex_choice = [('1', '男'), ('2', '女')]
sex = models.CharField(verbose_name='性别', max_length=1, choices=sex_choice, default=1)
web = models.URLField(verbose_name='个人网站', blank=True, null=True)
signature = models.TextField(verbose_name='个性签名', max_length=200, default="这个人很懒,什么都没留下!")
photo = models.URLField(verbose_name='头像', default='https://oss.cuiliangblog.cn/images/photo.jpg')
area_code = models.CharField(verbose_name='地区编号', max_length=10, blank=True, null=True)
area_name = models.CharField(verbose_name='地区名称', max_length=20, blank=True, null=True)
birthday = models.DateField(verbose_name='生日', blank=True, null=True)
is_flow = models.BooleanField('开启更新订阅', default=0)
search = models.ManyToManyField(SearchKey, verbose_name='搜索记录')
class Meta:
verbose_name = '用户详细信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.username
class OAuthId(models.Model):
user = models.ForeignKey(UserInfo, verbose_name='用户ID', on_delete=models.CASCADE, default=1)
source = models.ForeignKey(UserSource, verbose_name='用户来源', on_delete=models.CASCADE, default=1)
openid = models.CharField(max_length=100, verbose_name='用户OAuthID')
class Meta:
verbose_name = '第三方登录用户ID'
verbose_name_plural = verbose_name
def __str__(self):
return self.user.username
后端向前端提供两个API接口,一个是查询应用client id,另一个是处理登录完成后回调相关业务逻辑
from django.urls import path
from rest_framework import routers
from account import views
app_name = "account"
urlpatterns = [
path('OAuthID/', views.OAuthIDAPIView.as_view()),
# 获取第三方客户端ID
path('OAuthCallback/', views.OAuthCallbackAPIView.as_view()),
# 第三方登录后回调
]
router = routers.DefaultRouter()
urlpatterns += router.urls
import json
import urllib.parse
from loguru import logger
from rest_framework import status, viewsets
from rest_framework.response import Response
from rest_framework.views import APIView
from public.tools import OAuth
from django.conf import settings
class OAuthIDAPIView(APIView):
"""
获取第三方登录应用ID
"""
@staticmethod
def get(request):
platform = request.query_params.get('platform')
kind = request.query_params.get('kind')
result = {'clientId': settings.AUTH[platform.upper()][kind.upper()]['KEY']}
return Response(result, status=status.HTTP_200_OK)
class OAuthCallbackAPIView(APIView):
"""
授权第三方登录后回调地址
"""
@staticmethod
def post(request):
platform = request.data.get('platform')
kind = request.data.get('kind')
code = request.data.get('code')
redirect_uri = request.data.get('redirect_uri')
print(platform, code, redirect_uri, kind)
auth = OAuth(platform, kind, code, redirect_uri)
result = {}
if platform == 'WEIBO':
result = auth.weiboLogin()
elif platform == 'QQ':
result = auth.qqLogin()
elif platform == 'PAY':
result = auth.payLogin()
elif platform == 'GITHUB':
result = auth.githubLogin()
elif platform == 'BAIDU':
result = auth.baiduLogin()
elif platform == 'MICROSOFT':
result = auth.microsoftLogin()
return Response(result, status=status.HTTP_200_OK)
import random
import datetime
import uuid
from urllib.parse import urlencode
from loguru import logger
import requests
from alipay.aop.api.request.AlipaySystemOauthTokenRequest import AlipaySystemOauthTokenRequest
from alipay.aop.api.request.AlipayUserInfoShareRequest import AlipayUserInfoShareRequest
from alipay.aop.api.constant.ParamConstants import *
from alipay.aop.api.response.AlipaySystemOauthTokenResponse import AlipaySystemOauthTokenResponse
from alipay.aop.api.response.AlipayUserInfoShareResponse import AlipayUserInfoShareResponse
from django.core.cache import cache
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
from django.utils import timezone
import json
from account.models import UserInfo, UserSource, OAuthId
from rest_framework_simplejwt.tokens import RefreshToken
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
import traceback
class OAuth:
"""
第三方登录
"""
def __init__(self, platform, kind, code, redirect_uri):
print(platform, kind, code, redirect_uri)
self._client_key = settings.AUTH[platform][kind]['KEY']
# 应用id
self._client_secret = settings.AUTH[platform][kind]['SECRET']
# 应用key
self._code = code
# 用户code
self._redirect_uri = redirect_uri
# 登录回调地址
self.openid = ''
# 用户第三方登录ID
self.source_id = ''
# 用户来源id
self.user_id = ''
# 用户id
self.platform = platform
# 第三方登录平台
self.kind = kind
# 前端类型(PC或M)
def __checkUserRegister(self):
"""
检查用户是否已注册
:return:
"""
print('开始检测用户是否已注册过')
user = OAuthId.objects.filter(source_id=self.source_id, openid=self.openid)
if user.count() != 0:
self.user_id = user.first().user.id
return True
else:
return False
def __createUser(self, username, **kwargs):
"""
创建新用户
:param username: 用户名
:param kwargs: 用户信息
:return: None
"""
print("开始创建新用户啦")
while UserInfo.objects.filter(username=username): # 防止用户名重复
username = username + str(random.randrange(10))
userinfo = {
'source_id': self.source_id,
'username': username,
'password': str(uuid.uuid1())
}
for key, value in kwargs.items():
userinfo[key] = value
logger.info('存储信息:{}'.format(userinfo))
print(userinfo)
# 用户信息表插入数据
new_user = UserInfo.objects.create_user(**userinfo)
self.user_id = new_user.id
# OAuthId表插入数据
OAuthId.objects.create(user_id=self.user_id, source_id=self.source_id, openid=self.openid)
def __userLogin(self):
"""
用户登录签发token
:return:
"""
print("开始登录了")
user = UserInfo.objects.get(id=self.user_id)
user.last_login = timezone.now()
user.save()
refresh = RefreshToken.for_user(user)
result = dict()
result['token'] = str(refresh.access_token)
result['userid'] = user.id
result['username'] = user.username
return result
def weiboLogin(self):
"""
微博登录
:return:
"""
print("微博登录了")
self.source_id = UserSource.objects.get(name='微博').id
# 获取用户access_token和uid
access_token_url = 'https://api.weibo.com/oauth2/access_token?client_id={0}&client_secret={1}&grant_type=authorization_code&code={2}&redirect_uri={3}'.format(
self._client_key, self._client_secret, self._code, self._redirect_uri)
access_response = requests.post(access_token_url).json()
print(access_response['access_token'], access_response['uid'])
self.openid = access_response['uid']
# 判断用户是否已注册过
user = self.__checkUserRegister()
if user:
print('已注册过,直接登录')
return self.__userLogin()
else:
# 获取用户信息
userinfo_url = "https://api.weibo.com/2/users/show.json?access_token={0}&uid={1}".format(
access_response['access_token'], access_response['uid'])
userinfo_response = requests.get(userinfo_url).json()
print(userinfo_response)
logger.info('微博用户信息:{}'.format(userinfo_response))
username = userinfo_response['name']
signature = userinfo_response['description']
photo = userinfo_response['avatar_large']
web = userinfo_response['url']
area_name = userinfo_response['location']
if userinfo_response['gender'] == 'f':
sex = 2
else:
sex = 1
# 新建用户
self.__createUser(username, signature=signature, photo=photo, web=web, area_name=area_name, sex=sex)
# 用户登录
return self.__userLogin()
def qqLogin(self):
"""
QQ登录
"""
print("QQ登录了")
self.source_id = UserSource.objects.get(name='qq').id
# 获取用户access_token
access_token_url = 'https://graph.qq.com/oauth2.0/token?client_id={0}&client_secret={1}&grant_type=authorization_code&code={2}&redirect_uri={3}&fmt=json'.format(
self._client_key, self._client_secret, self._code, self._redirect_uri)
access_response = requests.get(access_token_url).json()
print(access_response['access_token'])
# 使用Access Token获取用户的OpenID
openID_url = 'https://graph.qq.com/oauth2.0/me?access_token={}&fmt=json'.format(access_response['access_token'])
openID_response = requests.get(openID_url).json()
print("openID:", openID_response['openid'])
self.openid = openID_response['openid']
# 判断用户是否已注册过
user = self.__checkUserRegister()
if user:
print('已注册过,直接登录')
return self.__userLogin()
else:
# 获取用户信息
print('开始获取用户信息')
userinfo_url = "https://graph.qq.com/user/get_user_info?access_token={0}&oauth_consumer_key={1}&openid={2}&fmt=json".format(
access_response['access_token'], self._client_key, self.openid)
userinfo_response = requests.get(userinfo_url).json()
logger.info('QQ用户信息:{}'.format(userinfo_response))
print(userinfo_response)
username = userinfo_response['nickname']
photo = userinfo_response['figureurl_2']
area_name = userinfo_response['province'] + ' ' + userinfo_response['city']
if userinfo_response['gender'] == '女':
sex = 2
else:
sex = 1
# 新建用户
self.__createUser(username, photo=photo, area_name=area_name, sex=sex)
# 用户登录
return self.__userLogin()
def baiduLogin(self):
"""
百度账号登录
"""
print("百度登录了")
self.source_id = UserSource.objects.get(name='百度').id
print(self.source_id)
# 获取用户access_token
access_token_url = 'https://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&client_id={0}&client_secret={1}&code={2}&redirect_uri={3}'.format(
self._client_key, self._client_secret, self._code, self._redirect_uri)
access_response = requests.get(access_token_url).json()
print(access_response['access_token'])
# 使用Access Token获取用户信息
userinfo_url = "https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token={}".format(
access_response['access_token'])
userinfo_response = requests.get(userinfo_url).json()
logger.info('百度用户信息:{}'.format(userinfo_response))
print(userinfo_response)
self.openid = userinfo_response['portrait']
# 判断用户是否已注册过
user = self.__checkUserRegister()
if user:
print('已注册过,直接登录')
return self.__userLogin()
else:
# 获取用户信息
username = userinfo_response['username']
photo = 'https://himg.bdimg.com/sys/portrait/item/' + userinfo_response['portrait']
# 新建用户
self.__createUser(username, photo=photo)
# 用户登录
return self.__userLogin()
def microsoftLogin(self):
"""
微软账号登录
"""
print("微软账号登录了")
self.source_id = UserSource.objects.get(name='微软').id
print(self.source_id)
# 获取用户access_token
access_token_headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
access_token_data = {
"client_id": self._client_key,
"client_secret": self._client_secret,
"code": self._code,
"redirect_uri": self._redirect_uri,
"grant_type": 'authorization_code',
"scope": 'offline_access user.read'
}
access_token_url = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/token'
access_response = requests.post(access_token_url, headers=access_token_headers,
data=urlencode(access_token_data)).json()
print(access_response['access_token'])
# 使用Access Token获取用户信息
userinfo_headers = {
"Authorization": 'Bearer ' + access_response['access_token'],
"Host": 'graph.microsoft.com'
}
userinfo_url = "https://graph.microsoft.com/v1.0/me"
userinfo_response = requests.get(userinfo_url, headers=userinfo_headers).json()
logger.info('微软用户信息:{}'.format(userinfo_response))
print(userinfo_response)
self.openid = userinfo_response['id']
# 判断用户是否已注册过
user = self.__checkUserRegister()
if user:
print('已注册过,直接登录')
return self.__userLogin()
else:
# 获取用户信息
username = userinfo_response['displayName'].replace(" ", "")
# 新建用户
self.__createUser(username)
# 用户登录
return self.__userLogin()
def __payToken(self, client):
"""
支付宝通过code获取用户token
:param client:
:return:
"""
# 构造请求参数对象
request = AlipaySystemOauthTokenRequest()
request.code = self._code
request.grant_type = "authorization_code"
response_content = None
# 执行API调用
try:
response_content = client.execute(request)
except Exception as e:
print(traceback.format_exc(), e)
if not response_content:
print("failed execute")
else:
# 解析响应结果
response = AlipaySystemOauthTokenResponse()
response.parse_response_content(response_content)
if response.is_success():
# 如果业务成功,可以通过response属性获取需要的值
auth_token = response.access_token
self.openid = response.user_id
return auth_token
# 响应失败的业务处理
else:
# 如果业务失败,可以从错误码中可以得知错误情况,具体错误码信息可以查看接口文档
print(response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg)
def __payUserInfo(self, client, token):
"""
获取支付宝用户信息
:return:
"""
request = AlipayUserInfoShareRequest()
# 添加auth_token
udf_params = dict()
udf_params[P_AUTH_TOKEN] = token
request.udf_params = udf_params
response_content = None
# 执行API调用
try:
# 执行接口请求
response_content = client.execute(request)
except Exception as e:
print(traceback.format_exc(), e)
if not response_content:
print("failed execute")
else:
response = AlipayUserInfoShareResponse()
# 解析响应结果
response.parse_response_content(response_content)
# 响应成功的业务处理
if response.is_success():
# 如果业务成功,可以通过response属性获取需要的值
# print(response)
logger.info('支付宝用户信息:{}'.format(response))
username = response.nick_name
photo = response.avatar
area_name = response.province + ' ' + response.city
if response.gender == 'f':
sex = 2
else:
sex = 1
# 新建用户
self.__createUser(username, photo=photo, area_name=area_name, sex=sex)
# 响应失败的业务处理
else:
# 如果业务失败,可以从错误码中可以得知错误情况,具体错误码信息可以查看接口文档
print(response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg)
def payLogin(self):
"""
支付宝登录
"""
print("支付宝登录了")
self.source_id = UserSource.objects.get(name='支付宝').id
# 实例化客户端
alipay_client_config = AlipayClientConfig()
alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
alipay_client_config.app_id = self._client_key
alipay_client_config.app_private_key = settings.AUTH[self.platform][self.kind]['PRIVATE_KEY']
alipay_client_config.alipay_public_key = settings.AUTH[self.platform][self.kind]['PUBLIC_KEY']
client = DefaultAlipayClient(alipay_client_config)
# 获取用户token
token = self.__payToken(client)
# 判断用户是否已注册过
user = self.__checkUserRegister()
if user:
print('已注册过,直接登录')
return self.__userLogin()
else:
# 获取用户信息
print('开始获取用户信息')
self.__payUserInfo(client, token)
return self.__userLogin()
def __githubToken(self):
"""
github获取用户token
:return:
"""
response = None
headers = {
'accept': 'application/json'
}
url = 'https://github.com/login/oauth/access_token?client_id={0}&client_secret={1}&code={2}'.format(
self._client_key, self._client_secret, self._code)
i = 0
while i < 3:
try:
print("开始尝试获取token", timezone.localtime())
response = requests.post(url, headers=headers, timeout=5).json()
if response:
return response
except requests.exceptions.RequestException:
i += 1
if response is None:
print("获取token请求失败了")
return False
def __githubUserInfo(self, token):
"""
github获取用户信息
:return:
"""
response = None
headers = {
'accept': 'application/json',
'Authorization': 'token ' + token
}
url = 'https://api.github.com/user'
i = 0
while i < 3:
try:
print("开始尝试获取用户信息", timezone.localtime())
response = requests.get(url, headers=headers, timeout=5).json()
if response:
print(response)
logger.info('github用户信息:{}'.format(response))
return response
except requests.exceptions.RequestException:
i += 1
if response is None:
print("获取用户信息失败了")
return False
def githubLogin(self):
"""
github登录
:return:
"""
print("github登录了")
self.source_id = UserSource.objects.get(name='github').id
# 获取用户access_token
access_response = self.__githubToken()
if access_response:
print(access_response['access_token'])
# 获取用户信息
userinfo_response = self.__githubUserInfo(access_response['access_token'])
if userinfo_response:
print(userinfo_response)
self.openid = userinfo_response['id']
# 判断用户是否已注册过
user = self.__checkUserRegister()
if user:
print('已注册过,直接登录')
return self.__userLogin()
else:
if userinfo_response['name']:
username = userinfo_response['name']
else:
username = userinfo_response['login']
signature = userinfo_response['bio']
photo = userinfo_response['avatar_url']
if userinfo_response['blog']:
web = userinfo_response['blog']
else:
web = userinfo_response['html_url']
area_name = userinfo_response['location']
# 新建用户
self.__createUser(username, signature=signature, photo=photo, web=web, area_name=area_name)
# 用户登录
return self.__userLogin()
else:
return False
// 获取第三方登录ID
export function getOAuthID(platform) {
return index.get('/account/OAuthID/' + '?platform=' + platform + '&kind=PC')
}
// 第三方授权登录后回调
export function postOAuthCallback(params) {
return index.post('/account/OAuthCallback/', params)
}
<template>
<div class="other-login">
<el-divider>
<span>第三方账号登录</span>
</el-divider>
<div class="other-logo">
<span @click="otherLogin('QQ')" class="pointer"><MyIcon type="icon-qq-logo"/></span>
<span @click="otherLogin('PAY')" class="pointer"><MyIcon type="icon-alipay-logo"/></span>
<span @click="otherLogin('BAIDU')" class="pointer"><MyIcon type="icon-baidu-logo"/></span>
<span @click="otherLogin('WEIBO')" class="pointer"><MyIcon type="icon-weibo-logo"/></span>
<span @click="otherLogin('GITHUB')" class="pointer"><MyIcon type="icon-github-logo"/></span>
<span @click="otherLogin('MICROSOFT')" class="pointer"><MyIcon type="icon-microsoft-logo"/></span>
</div>
</template>
<script setup>
import icon from '@/utils/icon'
import {onBeforeMount, onMounted, reactive, ref} from "vue";
import {getBgiUrl} from "@/api/public";
import {useRouter} from "vue-router";
import VerifyImgBtn from "@/components/verify/VerifyImgBtn.vue";
import VerifyCodeBtn from "@/components/verify/VerifyCodeBtn.vue"
import {ElMessage} from 'element-plus'
import {getOAuthID, getRegister, postCode, postLogin, postRegister} from "@/api/account";
import store from "@/store";
import {getSiteConfig} from "@/api/management";
const router = useRouter();
let {MyIcon} = icon()
// 第三方登录
const otherLogin = (kind) => {
ElMessage('正在跳转至第三方平台,请稍候……')
console.log(kind)
let domain = window.location.protocol + "//" + window.location.host
if (kind === 'WEIBO') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://api.weibo.com/oauth2/authorize?client_id=' + response.clientId +
'&response_type=code&redirect_uri=' + domain + '/OAuth/' + kind
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
ElMessage.error('获取第三方登录ID失败!')
});
}
if (kind === 'QQ') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://graph.qq.com/oauth2.0/authorize?client_id=' + response.clientId +
'&response_type=code&redirect_uri=' + domain + '/OAuth/' + kind + '&state=' +
Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
ElMessage.error('获取第三方登录ID失败!')
});
}
if (kind === 'PAY') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=' + response.clientId +
'&scope=auth_user&redirect_uri=' + encodeURIComponent(domain + '/OAuth/' + kind) +
'&state=' + Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
ElMessage.error('获取第三方登录ID失败!')
});
}
if (kind === 'GITHUB') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://github.com/login/oauth/authorize?client_id=' + response.clientId +
'&scope=user&redirect_uri=' + domain + '/OAuth/' + kind + '&state=' +
Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
ElMessage.error('获取第三方登录ID失败!')
});
}
if (kind === 'BAIDU') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://openapi.baidu.com/oauth/2.0/authorize?client_id=' + response.clientId +
'&redirect_uri=' + domain + '/OAuth/' + kind + '&response_type=code&state=' +
Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
ElMessage.error('获取第三方登录ID失败!')
});
}
if (kind === 'MICROSOFT') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=' + response.clientId +
'&response_type=code&redirect_uri=' + domain + '/OAuth/' + kind +
'&response_mode=query&scope=offline_access user.read&state=' + Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
ElMessage.error('获取第三方登录ID失败!')
});
}
</script>
<style scoped lang="scss">
</style>
<template>
<Loading :type="'tips'" :text="'正在调用'+platform_name+'平台登录,请稍候……'"/>
</template>
<script setup>
import {useRouter} from "vue-router";
import {ElMessage} from 'element-plus'
import {onMounted, reactive, ref} from "vue";
import {postOAuthCallback} from "@/api/account";
import Loading from "@/components/common/Loading.vue"
import store from "@/store";
const router = useRouter()
// 平台名称
const platform_name = ref('')
// 回调登录表单
const OAuthForm = reactive({
platform: '',
kind: 'PC',
code: '',
redirect_uri: ''
})
// 向后端发送登录回调请求
const postCallback = () => {
postOAuthCallback(OAuthForm).then((response) => {
console.log(response)
ElMessage({
message: '登录成功!',
type: 'success',
})
store.commit('setKeepLogin', false)
store.commit('setUserSession', response)
console.log(store.state.nextPath)
router.push(store.state.nextPath)
}).catch(response => {
//发生错误时执行的代码
console.log(response)
ElMessage.error('自动登录异常,请更换其他登录方式!')
router.push('/loginRegister')
});
}
ref('')
onMounted(() => {
OAuthForm.platform = router.currentRoute.value.params.platform
OAuthForm.redirect_uri = window.location.protocol + "//" + window.location.host + router.currentRoute.value.path
if (OAuthForm.platform === 'PAY') {
OAuthForm.code = router.currentRoute.value.query.auth_code
} else {
OAuthForm.code = router.currentRoute.value.query.code
}
console.log(OAuthForm)
switch (OAuthForm.platform) {
case 'WEIBO':
platform_name.value = '新浪微博'
break;
case 'QQ':
platform_name.value = '腾讯QQ'
break;
case 'PAY':
platform_name.value = '支付宝'
break;
case 'GITHUB':
platform_name.value = 'GitHub'
break;
case 'BAIDU':
platform_name.value = '百度'
break;
case 'MICROSOFT':
platform_name.value = '微软'
break;
default:
platform_name.value = '第三方'
}
postCallback()
})
</script>
<style scoped>
</style>
业务逻辑与手机端一致,只是授权页跳转时URL参数手机端和PC端略有差异
<template>
<div class="other">
<van-divider>第三方账号登录</van-divider>
<div class="other-logo">
<span @click="otherLogin('QQ')">
<MyIcon class="logo-icon" type="icon-qq-logo"/>
<p>QQ</p>
</span>
<span @click="otherLogin('PAY')">
<MyIcon class="logo-icon" type="icon-alipay-logo"/>
<p>支付宝</p>
</span>
<span @click="otherLogin('BAIDU')">
<MyIcon class="logo-icon" type="icon-baidu-logo"/>
<p>百度</p>
</span>
<span @click="otherLogin('WEIBO')">
<MyIcon class="logo-icon" type="icon-weibo-logo"/>
<p>微博</p>
</span>
<span @click="otherLogin('GITHUB')">
<MyIcon class="logo-icon" type="icon-github-logo"/>
<p>GitHub</p>
</span>
<span @click="otherLogin('MICROSOFT')">
<MyIcon class="logo-icon" type="icon-microsoft-logo"/>
<p>微软</p>
</span>
</div>
</div>
</template>
<script setup>
import {Form, Button, Field, Divider, Icon, Checkbox, Toast} from 'vant';
import VerifyImgBtn from "@/components/verify/VerifyImgBtn.vue";
import {reactive, ref} from "vue";
import {getOAuthID, postLogin} from '@/api/account'
import store from "@/store/index";
import {useRouter} from "vue-router";
import icon from '@/utils/icon'
let {MyIcon} = icon()
const router = useRouter()
// 第三方登录
const otherLogin = (kind) => {
Toast('正在跳转至第三方平台,请稍候……')
console.log(kind)
let domain = window.location.protocol + "//" + window.location.host
if (kind === 'WEIBO') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://open.weibo.cn/oauth2/authorize?client_id=' + response.clientId +
'&response_type=code&redirect_uri=' + domain + '/OAuth/' + kind + '&display=mobile'
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
Toast.fail('获取第三方登录ID失败!')
});
}
if (kind === 'QQ') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://graph.qq.com/oauth2.0/authorize?client_id=' + response.clientId +
'&response_type=code&redirect_uri=' + domain + '/OAuth/' + kind + '&display=mobile'
+ '&state=' + Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
Toast.fail('获取第三方登录ID失败!')
});
}
if (kind === 'PAY') {
getOAuthID(kind).then((response) => {
console.log(response)
let parameter = 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=' +
response.clientId + '&scope=auth_user&redirect_uri=' + domain + '/OAuth/' + kind + '&state=' + Math.random().toString(36).slice(-6)
let url = 'alipays://platformapi/startapp?appId=20000067&url=' + encodeURIComponent(parameter)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
Toast.fail('获取第三方登录ID失败!')
});
}
if (kind === 'GITHUB') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://github.com/login/oauth/authorize?client_id=' + response.clientId +
'&scope=user&redirect_uri=' + domain + '/OAuth/' + kind + '&state=' + Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
Toast.fail('获取第三方登录ID失败!')
});
}
if (kind === 'BAIDU') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://openapi.baidu.com/oauth/2.0/authorize?client_id=' + response.clientId +
'&redirect_uri=' + domain + '/OAuth/' + kind + '&response_type=code&display=mobile&state=' + Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
Toast.fail('获取第三方登录ID失败!')
});
}
if (kind === 'MICROSOFT') {
getOAuthID(kind).then((response) => {
console.log(response)
let url = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=' + response.clientId +
'&response_type=code&redirect_uri=' + domain + '/OAuth/' + kind +
'&response_mode=query&scope=offline_access user.read&state=' + Math.random().toString(36).slice(-6)
console.log(url)
window.location.href = url;
}).catch(response => {
//发生错误时执行的代码
console.log(response)
Toast.fail('获取第三方登录ID失败!')
});
}
}
</script>
<style lang="scss" scoped>
@import "src/assets/style/index";
</style>
崔亮的博客-专注devops自动化运维,传播优秀it运维技术文章。更多原创运维开发相关文章,欢迎访问https://www.cuiliangblog.cn