三方登录(微博为例)
三方登錄介紹
- 三方登錄流程(以微博為例)
1)前端獲取認證code
1. 在Vue頁面加載時動態發送請求獲取微博授權url 2. django收到請求的url后,通過微博應用ID(client_id)和回調地址(redirect_uri)動態生成授權url返回給Vue 3. 當用戶點擊上面的url進行掃碼,授權成功會跳轉我們的回調界面并附加code參數 4. Vue獲取到微博返回的code后,會將code發送給django后端(上面的redirect_uri)2)獲取微博access_token
后端獲取code后,結合client_id、client_secret、redirect_uri參數進行傳遞 獲取微博access_token3)獲取微博用戶基本信息并保存到數據庫
使用獲得的access_token調用獲取用戶基本信息的接口,獲取用戶第三方平臺的基本信息 用戶基本信息保存到數據庫,然后關聯本地用戶,然后將用戶信息返回給前端4)生成token給Vue
django后端借助微博認證成功后,可以使用JWT生成token,返回給Vue Vue將token存儲到localStorage中,以便用戶訪問其他頁面進行身份驗證oauth認證原理
- OAuth是一個開放標準,允許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源,而無需將用戶名和密碼提供給第三方應用
- OAuth允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據
- 這個code如果能出三方換取到數據就證明這個用戶是三方真實的用戶
為什么使用三方登錄
- 服務方希望用戶注冊, 而用戶懶得填注冊時的各種信息(主要是為了保證用戶的唯一性,各種用戶名已占用,密碼格式限制)
- 而像微信, QQ, 微博等幾乎每個人都會安裝的應用中用戶肯定會在其中某一個應用中已經注冊過,證明該用戶在已經注冊的應用中的唯一性
- 第三方登錄的實質就是在授權時獲得第三方應用提供的代表了用戶在第三方應用中的唯一性的openid.并將openid儲存在第三方服務控制的本地儲存
第三方登錄與本地登錄的關聯(三種情況)
1)情況1: 本地未登錄,第一次登錄第三方
此時相當于注冊,直接把第三方信息拉取來并注冊成本地用戶就可以了,并建立本地用戶與第三方用戶(openid)的綁定關系
2)情況2:本地未登錄,再次登錄第三方
此時用戶已注冊,獲取到openid后直接找出對應的本地用戶即可
3)情況3:本地登錄,并綁定第三方
這個只要將獲取到的openid綁定到本地用戶就可以了
微博申請應用
微博申請應用參考:https://cloud.tencent.com/developer/article/1441425
官方微博接入文檔:https://open.weibo.com/wiki/Connect/login
- 前端Vue
src\router\index.js 添加路由
import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' import Login from '@/components/Login' import WeiboCallback from '@/components/WeiboCallback' import UserBind from '@/components/UserBind'Vue.use(Router)export default new Router({mode: 'history',routes: [{ path: '/', name: 'HelloWorld', component: HelloWorld },{ path: '/login', name: 'Login', component: Login }, // 登錄頁面{ path: '/weibo_callback', name: 'WeiboCallback', component: WeiboCallback }, // 通過空頁面發送code給后端{ path: '/userbind', name: 'UserBind', component: UserBind }, // 將本地用戶與第三方用戶綁定] })src\components\Login.vue 登錄頁面
<template><div><a :href="weibo_url" class="weibo_login">微博</a></div> </template><style></style><script>import axios from "axios";export default {data: function(){return {weibo_url: '' // 動態從后端獲取的微博掃碼URL}},mounted(){this.get_weibo_url()},methods: {get_weibo_url: function(){// http://127.0.0.1:8000/api/weibo_url/axios({url: 'http://127.0.0.1:8000/api/weibo_url/',method: 'get'}).then(res=>{this.weibo_url = res.data.weibo_url})}}}; </script>src\components\WeiboCallback.vue 通過空頁面發送code給后端
<template><p>跳轉中....</p> </template><script> import axios from 'axios' export default {mounted(){this.get_code()},methods: {get_code: function(){let code = this.$route.query.code // 獲取微博的驗證codeconsole.log(code)axios({url:'http://127.0.0.1:8000/api/weibo_back/?code=' + code,method: 'get'}).then(res=>{console.log(res)if (res.data.code == 200) {console.log('成功')localStorage.username = res.data.usernamelocalStorage.user_id = res.data.user_idlocalStorage.token = res.data.tokenwindow.location.href = '/'}if (res.data.code == 201) {console.log('失敗') // 如果用戶未綁定,跳轉到綁定頁面localStorage.access_token = res.data.responsewindow.location.href = '/userbind'}})}} } </script>src\components\UserBind.vue 將本地用戶與第三方用戶綁定
<template><div><form @submit.prevent="send_bind_info"><p><label>輸入賬號:</label><input type="account" name="account" id="account" v-model="account"></p><p><label>輸入密碼:</label><input type="password" name="pwd" id="pwd" v-model="password"></p><p><input type="submit" value="注 冊" name></p></form></div></div> </template><style></style><script> document.title = "綁定頁面"; import axios from "axios"; export default {// axios-> access_tokendata: function() {return {password: "",account: "",};},methods: {send_bind_info: function() {let post_data = new FormData();let access_token = localStorage.access_token;post_data.append("password", this.password);post_data.append("account", this.account);post_data.append("access_token", access_token);axios({url: "http://127.0.0.1:8000/api/bind_user/",method: "post",data: post_data}).then(res => {});}} }; </script>- django后端
settings.py 配置使用JWT、corsheaders、rest_framework
INSTALLED_APPS = ['rest_framework.authtoken','rest_framework', 'users','corsheaders', ]MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware',# 'django.middleware.common.CommonMiddleware', ]''' 配置jwt驗證 ''' REST_FRAMEWORK = {# 身份認證'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_jwt.authentication.JSONWebTokenAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.BasicAuthentication',), } import datetime JWT_AUTH = {'JWT_AUTH_HEADER_PREFIX': 'JWT','JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),'JWT_RESPONSE_PAYLOAD_HANDLER':'users.views.jwt_response_payload_handler', # 重新login登錄返回函數 } AUTH_USER_MODEL='users.User' # 指定使用users APP中的 model User進行驗證'''配置cors''' CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True'''微博相關配置信息''' WEIBO_APP_KEY = '3516473472' WEIBO_APP_SECRET = '7862ee35a0dc6f0345d0464dc34f14fc' WEIBO_FUNC_BACK = 'http://127.0.0.1:8080/weibo_callback' # VUE的回調urls.py 總路由
from django.contrib import admin from django.urls import path,includeurlpatterns = [path('admin/', admin.site.urls),path('api/', include('users.urls')), ]users/urls.py
#! /usr/bin/env python # -*- coding: utf-8 -*- from django.urls import path,re_path from . import views urlpatterns = [re_path(r'register/',views.RegisterView.as_view(),name='register'),re_path(r'weibo_url/', views.WeiboUrl.as_view(), name='weibo_url'),re_path(r'weibo_back/',views.WeiboBack.as_view(),name='weibo_back'),re_path(r'bind_user/',views.BindUser.as_view(),name='bind_user'), ]users/models.py
from django.db import models from django.contrib.auth.models import AbstractUserclass User(AbstractUser):username = models.CharField(max_length=64, unique=True)password = models.CharField(max_length=255)phone = models.CharField(max_length=64,blank=True,null=True)class SocialUser(models.Model):user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name='用戶')platfrom_type_choices = ((1,'web'),(2,'移動'),)platfrom_ID = models.IntegerField(choices=platfrom_type_choices,verbose_name='平臺類型')platfrom_choices = ((1,'QQ'),(2,'微博'),(3,'微信'),)platfrom_type = models.IntegerField(choices=platfrom_choices,verbose_name='社交平臺')uid = models.CharField(max_length=100,verbose_name='用戶ID')def __str__(self):return self.user.usernameusers/serializers.py
#! /usr/bin/env python # -*- coding: utf-8 -*- from rest_framework_jwt.settings import api_settings from rest_framework import serializers from users.models import User from users.models import SocialUserclass UserSerializer(serializers.Serializer):username = serializers.CharField()password = serializers.CharField()phone = serializers.CharField(required=False,allow_blank=True)token = serializers.CharField(read_only=True)def create(self, data):user = User.objects.create(**data)user.set_password(data.get('password'))user.save()# 補充生成記錄登錄狀態的tokenjwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLERjwt_encode_handler = api_settings.JWT_ENCODE_HANDLERpayload = jwt_payload_handler(user) # 創建載荷token = jwt_encode_handler(payload) # 創建tokenuser.token = tokenreturn userclass SocialUserSerializer(serializers.Serializer):user_id = serializers.IntegerField()platfrom_ID = serializers.IntegerField() # 平臺類型(web/移動)platfrom_type = serializers.IntegerField() # 社交平臺(微博/微信/QQ)uid = serializers.IntegerField() # 社交平臺唯一IDdef create(self, validated_data):social_user = SocialUser.objects.create(**validated_data)return social_userusers/views.py
from django.shortcuts import render import json from rest_framework.views import APIView from rest_framework.views import Response from rest_framework.permissions import IsAuthenticated from rest_framework_jwt.authentication import JSONWebTokenAuthentication from urllib.parse import urlencode import requests from rest_framework_jwt.settings import api_settings from django.db import transaction from rest_framework import serializers from django.core.exceptions import ValidationErrorfrom weiboLogin import settings from users import models from users.serializers import UserSerializer from users.serializers import SocialUserSerializer# 用戶注冊 class RegisterView(APIView):def post(self, request, *args, **kwargs):serializer = UserSerializer(data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data, status=201)return Response(serializer.errors, status=400)# 重寫用戶登錄返回函數 def jwt_response_payload_handler(token, user=None, request=None):''':param token: jwt生成的token值:param user: User對象:param request: 請求'''return {'test':'aaaa','token': token,'user': user.username,'userid': user.id}# 生成前端跳轉到微博掃碼頁面的url class WeiboUrl(APIView):'''生成微博的登陸頁面路由地址https://api.weibo.com/oauth2/authorize? # 微博oauth認證地址client_id=4152203033& # 注冊開發者idresponse_type=code&redirect_uri=http://127.0.0.1:8080/weibo_callback/ # 獲取code后將code回調給后端地址'''def get(self, request):url = 'https://api.weibo.com/oauth2/authorize?'data = {'client_id': settings.WEIBO_APP_KEY,'response_type': 'code','redirect_uri': settings.WEIBO_FUNC_BACK,}weibo_url = url + urlencode(data)# https://api.weibo.com/oauth2/authorize?client_id=4152203033&response_type=code&redirect_uri=http://127.0.0.1:8000/api/weibo_back/return Response({'weibo_url': weibo_url})# 微博掃碼成功后攜帶code調用此接口,認證成功返回jwt token class WeiboBack(APIView):'''通過回調連接,獲取access_tokenhttps://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_IDclient_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&code=CODEredirect_uri=YOUR_REGISTERED_REDIRECT_URI'''def get(self, request):code = request.query_params.get('code') # code為微博微博認證的codedata = {'client_id': settings.WEIBO_APP_KEY,'client_secret': settings.WEIBO_APP_SECRET,'grant_type': 'authorization_code','code': code,'redirect_uri': settings.WEIBO_FUNC_BACK,}url = 'https://api.weibo.com/oauth2/access_token'response = requests.post(url=url, data=data).json() # 拿取請求的返回結果# {'access_token': '2.00jqYNTGfgNAXEbd85e6c672uTGF8E',# 'remind_in': '157679999', 'expires_in': 157679999,# 'uid': '5928542965', 'isRealName': 'true'}print(response)uid = response.get('uid') # uid是微博三方的唯一idif not uid: # 獲取不到則為微博code錯誤return Response({'code': 201, 'error': '三方授權失敗'})try:# 判斷當前UID是否存在與數據庫中,如果存在,代表用戶可以登陸的user = models.SocialUser.objects.get(uid=uid)except:# 如果不存在,代表這個用戶不能登陸,先得跳轉到綁定頁面,將本地用戶與微博用戶進行綁定return Response({'code': 201,'response': json.dumps(response)})else: # 如果獲取到uid,就生成token返回給vuejwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLERjwt_encode_handler = api_settings.JWT_ENCODE_HANDLERpayload = jwt_payload_handler(user.user) # 創建載荷token = jwt_encode_handler(payload) # 創建tokenreturn Response({'code': 200,'token': token,'username': user.user.username,'user_id': user.user.id,})# 將微博用戶綁定到本地用戶 class BindUser(APIView):def post(self, request):account = request.data.get('account') # 綁定的賬號password = request.data.get('password') # 綁定的密碼user_url = "https://api.weibo.com/2/users/show.json?access_token=%s&uid=%s"access_token = json.loads(request.data.get('access_token'))get_url = user_url % (access_token['access_token'], access_token['uid'])# {'access_token': '2.00jqYNTGfgNAXEbd85e6c672uTGF8E','uid': '5928542965', 'isRealName': 'true'}data = requests.get(url=get_url).json() # 通過token獲取微博詳細信息if data.get('error'):return Response({'code': 201,'token': access_token,})else:try:serializer = UserSerializer(data={'username':account,'password':password})if serializer.is_valid():# 創建本地用戶與微博用戶關聯with transaction.atomic():save_id = transaction.savepoint() # 創建一個保存點try:user = serializer.save()socialuser = SocialUserSerializer(data={"user_id": user.id,"platfrom_ID" : 1,"platfrom_type": 2,"uid" : data['id']})if socialuser.is_valid():socialuser.save()return Response({'code': 200,'token': user.token,'username': user.username,'user_id': user.id,})else:transaction.savepoint_rollback(save_id) # 出現異常退回到保存點except ValidationError:raiseexcept Exception as e:transaction.savepoint_rollback(save_id)finally:# 提交事務transaction.savepoint_commit(save_id)except Exception as e:print(e)return Response(status=400)詳情鏈接參考
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的三方登录(微博为例)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DjangoRestFramework基
- 下一篇: 三方支付(支付宝为例)