三方登录--微博
1、思维导图
1.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)
1.2 获取微博access_token
后端获取code后,结合client_id、client_secret、redirect_uri参数进行传递,获取微博
access_token
https://api.weibo.com/oauth2/authorize?
client_id=4122644977
&response_type=code
&state=study&
forcelogin=true&
redirect_uri=https%3A%2F%2Fstudy.163.com%2Fsns%2Fweibo%2FoAuthCallback.htm%3Foaut
hType%3Dlogin%26returnUrl%3DaHR0cHM6Ly9zdHVkeS4xNjMuY29tL3Byb3ZpZGVyLzQwMDAwMDAwM
DQ3ODAxMi9pbmRleC5odG0%2FZnJvbT1zdHVkeQ%3D%3D%26nrsstcw%3Dfalse%26nc%3Dtrue###
https://study.163.com/provider/400000000478012/index.htm?from=study1.3 获取微博用户基本信息并保存到数据库
使用获得的access_token调用获取用户基本信息的接口, 获取用户第三方平台的基本信息
用户基本信息 保存到数据库,然后关联本地用户 ,然后将用户信息返回给前端
1.4 生成token给Vue
django后端借助微博认证成功后,可以 使用JWT生成token ,返回给Vue
Vue将token存储到localStorage中 ,以便用户访问其他页面进行身份验证
2.第三方登录与本地登录的关联(三种情况)
2.1 情况1: 本地未登录,第一次登录第三方此时相当于注册,直接把第三方信息拉取来并注册成本地用户就可以了,并建立本地用户与第三方用户(openid)的绑定关系
2.2 情况2:本地未登录,再次登录第三方此时用户已注册,获取到openid后直接找出对应的本地用户即可
2.3 情况3:本地登录,并绑定第三方这个只要将获取到的openid绑定到本地用户就可以了
3.oauth认证原理
- OAuth是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源,而无需将用户名和密码提供给第三方应用。
- OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。
- 这个code如果能出三方换取到数据就证明这个用户是三方真实的用户
4.为什么使用三方登录
- 服务方希望用户注册, 而用户懒得填注册时的各种信息(主要是为了保证用户的唯一性,各种用户名已占用,密码格式限制).
- 而像微信, QQ, 微博等几乎每个人都会安装的应用中用户肯定会在其中某一个应用中已经注册过,证明该用户在已经注册的应用中的唯一性.
- 第三方登录的实质就是在授权时获得第三方应用提供的代表了用户在第三方应用中的唯一性的openid.并将openid储存在第三方服务控制的本地储存.
微博开放平台设置app和key
接口文档
- 查看接口文档
https://open.weibo.com/wiki/授权机制说明
- OAuth2.0授权认证
-
请求用户授权Token: https://open.weibo.com/wiki/Oauth2/authorize
获取授权过的Access Token, UID: https://open.weibo.com/wiki/Oauth2/access_token
代码实现:
vue代码

<template> <div> <p><input type="button" value="微博登陆" @click="weibo_login()"></p> <P><a v-if="a==2" :href="weibo_url" class="weibo_login">微博登陆</a></P> </div> </template> <script> import axios from 'axios' export default { name:"weibo", data(){ return{ a:1, weibo_url:'' } }, methods: { weibo_login(){ axios({ url:"http://127.0.0.1:8000/wb/weibourl/", method:"get" }).then(res=>{ this.a = 2 this.weibo_url = res.data.weibo_url }) } }, } </script> weibo.vue

<template> <div> <h1>页面跳转中。。。</h1> </div> </template> <script> import axios from 'axios' export default { name:'weibo_callback', data(){ return{ } }, methods:{ get_code(){ var code = this.$route.query.code console.log(code) axios({ url:"http://127.0.0.1:8000/wb/call_back/?code="+code, method:"get" }).then(res=>{ console.log(res.data) if(res.data.code==200){ sessionStorage.setItem("jwt_token",res.data.token) window.location.href="www.baidu.com" }else if(res.data.code==404){ sessionStorage.setItem("u_id",res.data.uid) this.$router.push({path:"/binduser"}) }else{ alert("授权失败") } }) } }, mounted(){ this.get_code() } } </script> weibo_callback.vue

<template> <div> <p>用户名:<input type="text" v-model="username"></p> <p>密码:<input type="password" v-model="password"></p> <p>手机号:<input type="text" v-model="phone"></p> <p><input type="button" @click="send_bind_info()"></p> </div> </template> <script> document.title = "绑定页面"; import axios from "axios"; export default { // axios-> access_token data: function() { return { password: "", username: "", phone:"" }; }, methods: { send_bind_info: function() { let post_data = new FormData(); let u_id = sessionStorage.getItem("u_id"); post_data.append("password", this.password); post_data.append("username", this.username); post_data.append("phone", this.phone); post_data.append("u_id", u_id); axios({ url: "http://127.0.0.1:8000/wb/bind_user/", method: "post", data: post_data }).then(res => { console.log(res.data) }); } } }; </script> binduser.vue
django代码

import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'fivz67l6ttp5jlg%$jnrinq=j72re)x-54k(q-%y^l5+(7^h6i' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'appqiniu', 'rest_framework', 'corsheaders', 'rest_framework.authtoken', 'users', 'weiboapp', "redisapp" ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'corsheaders.middleware.CorsMiddleware', ] ROOT_URLCONF = 'django_online.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'django_online.wsgi.application' # Database # https://docs.djangoproject.com/en/2.2/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': '123456', # 数据库用户密码 'NAME': 'drf1908a_ol' # 数据库名字 } } # Password validation # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.2/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.2/howto/static-files/ STATIC_URL = '/static/' CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = () CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'XMLHttpRequest', 'X_FILENAME', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'Pragma', ) SESSION_ENGINE='django.contrib.sessions.backends.cache' REST_FRAMEWORK = { # 身份认证 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), #全局配置接口权限 # 'DEFAULT_PERMISSION_CLASSES': ( # 'rest_framework.permissions.IsAuthenticated', # ), } 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表作为系统认证时使用表 WEIBO_APP_KEY = '505677658' WEIBO_APP_SECRET = '5689ef360f8e25c1df8a54e8e5653cd2' WEIBO_CALL_BACK = 'http://127.0.0.1:8080/#/weibo_callback/' # 回调路由 setting.py

#! /usr/bin/env python # -*- coding: utf-8 -*- from django.urls import path,re_path,include from weiboapp import views from rest_framework_jwt.views import obtain_jwt_token # 验证密码后返回token urlpatterns = [ path('weibourl/', views.WBUrl.as_view(), ), path('call_back/', views.WBCallBack.as_view(), ), path('bind_user/', views.BindUser.as_view(), ), ] urls.py

from rest_framework_jwt.settings import api_settings from rest_framework import serializers from users.models import User class UserSerializer(serializers.Serializer): id =serializers.IntegerField(read_only=True) username = serializers.CharField() password = serializers.CharField() phone = serializers.CharField() token = serializers.CharField(read_only=True) def create(self, data): user = User.objects.create(**data) #数据库里密码的加密(固定的步骤) user.set_password(data.get('password')) user.save() # 补充生成记录登录状态的token 固定的格式,用过来生成jwt的token jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) #把token发放在user里返回 user.token = token return user def update(self, instance, validated_data): pass serializers.py

from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class User(AbstractUser): username = models.CharField(max_length=64, unique=True) password = models.CharField(max_length=255) phone = models.CharField(max_length=64) pic = models.CharField(max_length=512) token = models.CharField(max_length=255) class WbUser(models.Model): user_app=( (1,"微信"), (2,"微博"), (3,"qq"), (4,"支付宝") ) uid= models.CharField(max_length=128,unique=True) users = models.ForeignKey(User,on_delete=models.SET_NULL,null=True) name = models.CharField(max_length=32,null=True) user_from = models.IntegerField(choices=user_app) model.py

from django.shortcuts import render from rest_framework.views import APIView from sylxm.settings import WEIBO_APP_KEY, WEIBO_APP_SECRET from urllib.parse import urlencode from rest_framework.response import Response from oauthapp.models import * from rest_framework_jwt.settings import api_settings import requests from userapp.serializer import * from rest_framework.permissions import IsAuthenticated, AllowAny # Create your views here. class WeiBoURL(APIView): # permission_classes = [AllowAny] def post(self, request): url = 'https://api.weibo.com/oauth2/authorize?' # 微博授权的url地址 data = { 'client_id': WEIBO_APP_KEY, # WEIBO_APP_KEY 'response_type': 'code', 'redirect_uri': 'http://127.0.0.1:8888/oauth/callback/', # vue回调,微博后台 } weibo_url = url + urlencode(data) return Response({'code': '0', 'msg': '成功', 'data': {'url': weibo_url}}) class WeiBoCallback(APIView): def post(self, request): code = request.data.get("code") data = { 'client_id': WEIBO_APP_KEY, 'client_secret': WEIBO_APP_SECRET, 'grant_type': "authorization_code", 'code': code, 'redirect_uri': 'http://127.0.0.1:8888/oauth/callback/', } url = "https://api.weibo.com/oauth2/access_token" # 需要用一个http请求去请求微博准备的信息--requests weibo_data = requests.post(url=url, data=data).json() print(weibo_data) # http json_weibo_data = weibo_data uid = json_weibo_data.get("uid") # 判断是否获取到uid if uid: try: uid_user = OauthUser.objects.get(uid=uid) res_data = { 'code': 0, 'msg': "授权成功", 'data': { "type": "0", "uid": uid, "username": uid_user.user.username, "token": create_token(uid_user.user) } } return Response(res_data) except Exception as e: res_data = { 'code': 0, "msg": '授权成功', 'data': { 'type': '1', 'uid': uid, } } return Response(res_data) else: return Response({'code': 999, "msg": "获取微博信息失败"}) # 检查参数是否齐全 class OauthUserBand(APIView): def post(self, request): oauth_type = 1 username = request.data.get('username') password = request.data.get('password') weibo_uid = request.data.get('weibo_uid') if not all([username, password, weibo_uid]): return Response({"code": 4005, "msg": '参数不完整'}) # 判断username是否存在 try: user = User.objects.get(username=username) oauthinfo = OauthUser.objects.create(uid=weibo_uid, oauth_type=oauth_type, user=user) data = { 'authenticated': True, 'id': user.id, 'role': None, 'name': user.nick_name, 'username': username, 'emial': user.email, 'token': create_token(user), 'type': 0, } res_data = { "code": 0, "msg": '登录成功', "data": data } return Response(res_data) except Exception as e: password = make_password(password) user = User.objects.create(username=username, password=password) oauthinfo = OauthUser.objects.create(uid=weibo_uid, oauth_type=oauth_type, user=user) data = { 'authenticated': True, 'id': user.id, 'role': None, 'name': user.nick_name, 'username': username, 'emial': user.email, 'token': create_token(user), 'type': 0, } res_data = { "code": 0, "msg": '登录成功', "data": data } return Response(res_data)