20.第三方微博登录Django+Vue

1.微博三方登录流程

在这里插入图片描述

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=study

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

1.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储存在第三方服务控制的本地储存.

4.微博账号 注册

4.1.注册账号

  • 注册新浪微博账号

新浪微博开放平台:https://open.weibo.com/

  • 进入首页。创建网站接入的应用:

在这里插入图片描述

  • 选择立即接入,进入到创建应用页面

在这里插入图片描述

  • 创建应用

在这里插入图片描述

在这里插入图片描述

  • 高级信息页面

在这里插入图片描述

4.2接口文档

  • 查看接口文档

      https://open.weibo.com/wiki/授权机制说明
    

在这里插入图片描述

在这里插入图片描述

  • OAuth2.0授权认证
接口 说明
OAuth2/authorize 请求用户授权Token
OAuth2/access_token 获取授权过的Access Token,UID

5.生成微博授权URL接口

5.1创建apps/oauth模块进行oauth认证

'''1.1 在apps文件夹下新建应用: oauth''' 
cd syl/apps
python ../manage.py startapp oauth   # 切换到apps文件夹下执行创建命令

'''1.2 添加子路由: oauth/urls.py''' 
from django.urls import path 
from . import views
urlpatterns = [

]

'''1.3 在syl/settings.py中添加应用''' 
INSTALLED_APPS = [
	'oauth.apps.OauthConfig', 
]

'''1.4 在syl/urls.py主路由中添加''' 
urlpatterns = [
	path('oauth/', include('oauth.urls')), 
]

5.2生成微博授权URL接口

1.1 添加子路由: oauth/urls.py
urlpatterns = [
	path('weibo/', views.WeiboUrl.as_view()),  # /oauth/weibo/ 返回微博登录地址 
]
1.2 syl/settings.py中配微博地址
1.3 视图函数: oauth/views.py
from rest_framework.permissions import AllowAny 
from rest_framework.response import Response 
from rest_framework.views import APIView 
from urllib.parse import urlencode

# 生成前端跳转到微博扫码页面的url 
class WeiboUrl(APIView):
	'''
		生成微博的登陆页面路由地址
		https://api.weibo.com/oauth2/authorize?       # 微博oauth认证地址
		client_id=4152203033&                         # 注册开发者id
		response_type=code&
		redirect_uri=http://127.0.0.1:8888/oauth/callback/     # 获取code后将code回 调给后端地址
	'''

	 # 自定义权限类
	 permission_classes = (AllowAny,)
	 def post(self, request):
	 	url = 'https://api.weibo.com/oauth2/authorize?'              # 微博授权的 url地址
	 	data = {
	 		'client_id': '3516473472',                               # WEIBO_APP_KEY,
	 		'response_type': 'code',
	 		'redirect_uri': 'http://127.0.0.1:8888/oauth/callback/',  # VUE的回 调,微博后台授权的回调地址
	 	}
	 	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/we ibo_back/
	 	# return Response({'weibo_url': weibo_url})
	 	return Response({'code': '0', 'msg': '成功', 'data': {'url': weibo_url}})

5.3测试生成微博授权URL接口

  • 测试接口获取新浪微博地址

      http://192.168.56.100:8888/oauth/weibo/
    

在这里插入图片描述

  • 在浏览器访问返回地址即可回到新浪扫码界面

      https://api.weibo.com/oauth2/authorize? 
      client_id=3516473472&response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1%3A808 0%2Fweibo_callback
    

在这里插入图片描述

6.在Vue页面加载时动态发送请求获取微博授 权url

6.1 在components\common\lab_header.vue中写oauth动态获取微 博授权URL

 // 获取微博登录地址
 oauth() {
 	// 从后端获取 微博登录地址
 	oauth_post().then((resp) => {
 		console.log(resp)
 		//{'code': '0', 'msg': '成功', 'data': {'url': url}}
 		let url = resp.data.url;
 		this.weibo_url = url;
 	})
},

6.2在vue的mounted函数中调用获取微博授权url函数

mounted() {
	this.oauth()
},

6.3点击"登录"弹出的form表单中加入url

<form
	action="/login"
	method="post" 
	>

	<div class="form-group widget-signin">
		<a :href="weibo_url"><i class="fa fa-weibo"></i></a>
	</div> 
	
</form>

7.微博回调接口

7.1oauth/urls.py中添加路由

urlpatterns = [
	 path('weibo/callback/', views.OauthWeiboCallback.as_view()),  # /oauth/weibo/callback/ 
]

7.2oauth/views.py中添加试图函数

http://192.168.56.100:8888/oauth/weibo/callback/
from .models import OauthUser 
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler 
from user.utils import jwt_response_payload_handler

# 通过vue前端传入的code,微博身份验证 
class OauthWeiboCallback(APIView):
	# 自定义权限类
	permission_classes = (AllowAny,)

	def post(self, request):
		# 接收vue端传过来的code(微博的用户code)
		code = request.data.get('code')
		data = {
			'client_id': '3516473472',
			'client_secret': '7862ee35a0dc6f0345d0464dc34f14fc',
			'grant_type': 'authorization_code',
			'code': code,
			'redirect_uri': 'http://127.0.0.1:8888/oauth/callback/',
		}
		url = 'https://api.weibo.com/oauth2/access_token'
		data = requests.post(url=url, data=data).json()  # 拿取请求的返回结果
		access_token = data.get('uid')         # 获取到的微博token
		weibo_uid = data.get('access_token')   # 获取到少码用户的id

		#  根据uid 查询绑定情况
		try:
			oauth_user = OauthUser.objects.get(uid=weibo_uid, oauth_type='1')
		except Exception as e:
			oauth_user = None
		# 返回动作,  登录成功/需要绑定用户 type 0 登录成功,  1, 授权成功, 需要绑定
		if oauth_user:
			#  如果绑定了, 返回token, 登录成功
			user = oauth_user.user

			payload = jwt_payload_handler(user)
			token = jwt_encode_handler(payload)
			# jwt_response_payload_handler为user模块定义的jwt返回的信息
			data = jwt_response_payload_handler(token, user)
			data['type'] = '0'  # 指定为登录成功
			return Response({'code': 0, 'msg': '登录成功', 'data': data})
		else:
			# 5. 如果没绑定, 返回标志, 让前端跳转到绑定页面
			return Response({'code': 0, 'msg': '授权成功', 'data': {'type': '1', 'uid': weibo_uid}})

7.3oauth/models.py中添加用户绑定模型

# 把三方的用户信息,和本地的用户信息进行绑定 
class OauthUser(models.Model):
	OAUTHTYPE = (
		('1', 'weibo'),
		('2', 'weixin'),
	)
	uid = models.CharField('三方用户id', max_length=64)
	# 三方用户id
	user = models.ForeignKey('user.User', on_delete=models.CASCADE)
	# 本地用户外键,关联User表
	oauth_type = models.CharField('认证类型', max_length=10, choices=OAUTHTYPE)
	

7.4迁移数据库

python manager.py makemigrations 
python manager.py migrate

8.vue微博回调空页面

注:微博回调空页面为: http://127.0.0.1:8888/oauth/callback/

8.1 页面路径components\oauth.vue

<template>
	<div>
		<p>跳转中...</p>
	</div>
</template>
<script>
  import { oauth_callback_post, } from './axios_api/api'
  export default {
    mounted() {
      this.getCode()
    },
    methods: {
    // 获取微博传过来的code,发送给django后端进行验证
      getCode() {
        // 获取url中的code 信息
        // 当前url 是  http://mysyl.com:8080/oauth/callback/?code=fe6cbe07708aecf4a2b3d942ed692c4c
        let code = this.$route.query.code
        console.log(this.$route.query)
        // 给后端发送code
        let params = { code: code }
        oauth_callback_post(params).then((resp) => {
          console.log(resp)
          // code: 0
          // msg: "授权成功"
          // data: {type: "1", uid: "7410919278"}
          if (resp.data.type == '0') {
            // code: 0
            // msg: "登录成功"
            // data: {
            // authenticated: "true"
            // email: ""
            // id: 1
            // name: "admin"
            // role: null
            // token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTk3OTAwNTcyLCJlbWFpbCI6IiIsIm9yaWdfaWF0IjoxNTk3ODE0MTcyfQ.aQT7GSR_xQBPMlB4_k8-zTHnx0ow3OC2KHa3C8MgilY"
            // type: "0"
            // username: "admin"}
            let res = resp.data
            localStorage.setItem('username', res.username)
            // localStorage.setItem('img', res.img)
            localStorage.setItem('token', res.token)
            localStorage.setItem('uid', res.id)
            this.login_username = res.username
            this.opened = false
            // alert(res.message)
            this.$router.push('/')

          }
          if (resp.data.type == '1') {
            this.visiable = true
            this.uid = resp.data.uid
          }
        })
      },
</script>

9.绑定微博用户接口

9.1oauth/urls.py中添加路由

urlpatterns = [
	 path('weibo/binduser/', views.OauthWeiboBindUser.as_view()),  # /oauth/weibo/callback/  
]

9.2oauth/views.py中添加试图函数

class OauthWeiboBindUser(APIView):

	permission_classes = (AllowAny,)

	def post(self, request):
		# 绑定用户, 1. 已注册用户, 2. 未注册用户
		# 1.1 获取用户名, 密码, weibo_uid
		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': 999, 'msg': '参数不全'})
		# 0.判断是否存在此用户
		try:
			user = User.objects.get(username=username)
		except Exception as e:
			user = None
		# 1. 已注册用户
		if user:
			# 1.2 , 如果存在就验证 密码, 验证通过,就绑定, 返回token,登录成功
			if user.check_password(password):
				ou = OauthUser(uid=weibo_uid, user=user, oauth_type='1')
				ou.save()
				payload = jwt_payload_handler(user)   # 通过user对象获取到jwt的 payload信息
				token = jwt_encode_handler(payload)   # 生成token
				data = jwt_response_payload_handler(token, user)
				data['type'] = '0'  # 指定为登录成功
				return Response({'code': 0, 'msg': '登录成功', 'data': data})
			else:
				return Response({'code': 999, 'msg': '密码错误'})
		else:
			# 2. 未注册用户
			# 2.1 生成新用户, 设置用户名密码, 保存, 然后绑定, 返回token, 登录成功
			user = User(username=username)
			user.set_password(password)
			user.save()
			ou = OauthUser(uid=weibo_uid, user=user, oauth_type='1')
			ou.save()
			payload = jwt_payload_handler(user)
			token = jwt_encode_handler(payload)
			data = jwt_response_payload_handler(token, user)
			data['type'] = '0'  # 指定为登录成功
			return Response({'code': 0, 'msg': '登录成功', 'data': data})

10.vue微博回调页面

注:微博回调页面为: http://127.0.0.1:8888/oauth/callback/

10.1页面路径components\oauth.vue

<template>
  <div>
    <div v-show='visiable'>
      绑定用户
      用户名: <input
      type="text"
      v-model="username"
      @blur="check_username"
    >
      <span>{{username_message}}</span>
      密码: <input
      type="password"
      v-model="password"
    >
      <button @click="bindUser">绑定</button>
    </div>
  </div>
</template>
<script>
  import { oauth_callback_post, oauth_binduser_post, user_count } from './axios_api/api'
  export default {
    data() {
      return {
        visiable: false, // 绑定用户窗口
        uid: '', // weibo_uid
        username: '',
        password: '',
        username_message: '',
        username_error: false
      }
    },
    mounted() {
      this.getCode()
    },
    methods: {
      // 判断用户名
      check_username() {
        console.log('判断用户名')
        console.log(this.username == '')
        var reg = new RegExp(/^[a-zA-Z0-9_-]{4,16}$/); //字符串正则表达式 4到14位(字母,数字,下划线,减号)
        if (this.username == '') {
          this.username_message = '用户名不能为空'
          this.username_error = true
          return false
        }
        if (!reg.test(this.username)) {
          this.username_message = '用户名格式不正确'
          this.username_error = true
          return false
        } else {
          // 去后端检查用户名使用数量
          user_count({ type: 'username', data: this.username }).then((res) => {
            console.log(res)
            if (res.data.count > 0) {
              this.username_message = '用户名已存在, 请输入密码'
              this.username_error = false
            } else {
              this.username_message = '用户名可用, 将创建新用户,请输入密码'
              this.username_error = false
            }
          })
        }
      },
      getCode() {
        // 获取url中的code 信息
        // 当前url 是  http://mysyl.com:8080/oauth/callback/?code=fe6cbe07708aecf4a2b3d942ed692c4c
        let code = this.$route.query.code
        console.log(this.$route.query)
        // 给后端发送code
        let params = { code: code }
        oauth_callback_post(params).then((resp) => {
          console.log(resp)
          // code: 0
          // msg: "授权成功"
          // data: {type: "1", uid: "7410919278"}
          if (resp.data.type == '0') {
            // code: 0
            // msg: "登录成功"
            // data: {
            // authenticated: "true"
            // email: ""
            // id: 1
            // name: "admin"
            // role: null
            // token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTk3OTAwNTcyLCJlbWFpbCI6IiIsIm9yaWdfaWF0IjoxNTk3ODE0MTcyfQ.aQT7GSR_xQBPMlB4_k8-zTHnx0ow3OC2KHa3C8MgilY"
            // type: "0"
            // username: "admin"}
            let res = resp.data
            localStorage.setItem('username', res.username)
            // localStorage.setItem('img', res.img)
            localStorage.setItem('token', res.token)
            localStorage.setItem('uid', res.id)
            this.login_username = res.username
            this.opened = false
            // alert(res.message)
            this.$router.push('/')

          }
          if (resp.data.type == '1') {
            this.visiable = true
            this.uid = resp.data.uid
          }
        })
      },
      bindUser() {
        if(this.username_error){
          return
        }
        // 发送  用户名, 密码, weibo_uid 到后端接口, 进行绑定
        let params = { username: this.username, password: this.password, weibo_uid: this.uid }
        oauth_binduser_post(params).then((resp) => {
          console.log(resp)
          let res = resp.data
          localStorage.setItem('username', res.username)
          // localStorage.setItem('img', res.img)
          localStorage.setItem('token', res.token)
          localStorage.setItem('uid', res.id)
          this.login_username = res.username
          this.opened = false
          // alert(res.message)
          this.$router.push('/')
        })
      }
    }
  }
</script>

posted @ 2020-10-09 18:34  Daniel*  阅读(390)  评论(0编辑  收藏  举报