django实现,微博第三方登录超详细

登录流程

  • 前端获取认证code
  1. 在Vue页面加载时动态发送请求获取微博授权url
  2. django收到请求的url后,通过微博应用ID(client_id)和回调地址(redirect_uri)动态生成授权url返回给Vue
  3. 当用户点击上面的url进行扫码,授权成功会跳转我们的回调界面并附加code参数
  4. Vue获取到微博返回的code后,会将code发送给django后端(上面的redirect_uri)
  • 获取微博access_token
    后端获取code后,结合client_id、client_secret、redirect_uri参数进行传递,获取微博access_token

  • 获取微博用户基本信息并保存到数据库
    使用获得的access_token调用获取用户基本信息的接口,获取用户第三方平台的基本信息
    用户基本信息保存到数据库,然后关联本地用户,然后将用户信息返回给前端

  • 生成token给Vue
    django后端借助微博认证成功后,可以使用JWT生成token,返回给Vue
    Vue将token存储到localStorage中,以便用户访问其他页面进行身份验证

前言:Vue Django 的跨域已经解决,Vue 是一个已经存在的项目 美化后的页面都是已经存在的,数据库的表格都已存在。
核心思想:

  1. 在微博开放平台 创建网页应用
  2. 通过微博规定的 规格参数换取
  3. 审核全部通过 登录信息入库
  4. 判断登录的 新老用户 是否绑定邮箱

登录微博开放平台,如果是新用户会审核一些东西,可以略过那些,选择性填写。。
django实现,微博第三方登录超详细_第1张图片
django实现,微博第三方登录超详细_第2张图片
django实现,微博第三方登录超详细_第3张图片
注册好应用后我们不需要 填写那些 信息,我们主要用到的是这两个参数,是会用它去调取微博的各项参数最后换取 登录权
django实现,微博第三方登录超详细_第4张图片
设置回调页,就是说登陆之后的跳转页面
django实现,微博第三方登录超详细_第5张图片

查看获取第三方登录权限的文档

(这里很重要)
django实现,微博第三方登录超详细_第6张图片
django实现,微博第三方登录超详细_第7张图片
我们只会用到这三个授权,看一下里面的文档。(将三个文档全部观看)
django实现,微博第三方登录超详细_第8张图片

这些文档中的返回值字段,只要是 True 形式的就必须填写,执行流程就是 这三个图片中的字段,在接口中对应的去请求。
比如第一张图片所需的参数,去请求给定的 url,就会获取到 对应的参数 然后在去请求下一个url。第三方登录就完成了。

首先 已知的条件
App Key:2851645393
App Secret:e646691419daa4bcb80c1bd95164b583
Url:https://api.weibo.com/oauth2/authorize (第一个文档中 给定好的)
CallBack: http://127.0.0.1:8080/course_index

由文档可知(文档在上面的截图) :

1. client_id true string 申请应用时分配的AppKey。
2. redirect_uri true string 授权回调地址,站外应用需与设置的回调地址一致,站内应用需填写canvas page的地址。

注:文档中的参数 字段 必须要遵守,字段名也要一致
我们预热一下,点击查看我拼接查看好的地址 https://api.weibo.com/oauth2/authorize?client_id=2851645393&redirect_uri=http://127.0.0.1:8080/course_index。
可以点击扫描二维码,毕竟登陆快、哈哈

如果:

django实现,微博第三方登录超详细_第9张图片
再多次尝试登陆之后会出现这样的页面代表着,浏览器已经存储了登录的Cookie Token 清楚浏览器数据缓存就 OK。像下面这样
django实现,微博第三方登录超详细_第10张图片
当然你也可以直接授权,但是 后期的 保存登录状态就 不好设置了 会重叠 不利于 浏览。
好了现在写后台接口请求对应的 url
写 接口之前 先把前台的 扫码前地址和扫码后的地址 写好。这里我用的是 Vue.js 前端框架。

//这是项目首页的 a 标签
<a href="https://api.weibo.com/oauth2/authorize?client_id=598520287&redirect_uri=http://127.0.0.1:8080/weibo_callback
">-weibo"></i></a>

看一下 效果
django实现,微博第三方登录超详细_第11张图片
我们点击扫码登陆后 (没写跳转后的页面肯定有报错)发现 url 收到了 code 参数
在这里插入图片描述
写一个 Vue 页面接收这个 code 然后发送给后台 Django。

<template>
    <div id="weibo_callback">
      <h3>{{message}}</h3>
    </div>
</template>

<script>
import axios from 'axios'
    export default {
        name: "weibo_callback",
        data() {
            return{
                message:''
            }
        },
        mounted() {
            var code = this.$route.query.code;
            var form_data = new FormData();
            form_data.append('code', code);
            console.log(code);
            axios({
                'url': 'api/user/get_weibo_code/',
                'method': 'post',
                'data': form_data
            })

我们通过 Vue 的 this.$route.query 获取到 code 值,然后存放到 FormData 发送到后台。
到这里我 有一个 思想,定义一个 类 然后 去换取参数的用另外定义的方法–(此时用到的函数试图是不配置路由的,他们只是换取所需值的方法,最终都是类试图进行调取)

用类试图接收 code 值,再用函数方法去请求 weibo api接口

# 接收 微博回调页面 发送的 code
class Get_WeiBo_Code(APIView):
    def post(self, request):
        code = request.data['code']

django实现,微博第三方登录超详细_第12张图片

# views.py
# Vue 发送的 不是这个接口 在下面 那个类试图
# 通过 授权过的 token 获取 Access_token

def get_weibo_accesstoken(code):
    # 获取 access_token 的必要参数 ↓
    url = 'https://api.weibo.com/oauth2/access_token'
    data = {
        'client_id': '2851645393',   # 创建应用的 App Key
        'client_secret': '646691419daa4bcb80c1bd95164b583',   # 创建应用的 App Secret
        'grant_type': 'authorization_code',   # 文档 写死
        'redirect_uri': 'http://127.0.0.1:8080/course_index',   #  回调地址
        'code': code    # 接收到的编码
    }
    # 构造 post 对 weibo 的 url 发起请求
    res = requests.post(url=url, data=data).text`在这里插入代码片`
    print('<206>Token 换取的结果:', res)
    # 得到请求转换为 字典 然后 取 键
    return json.loads(res)['access_token']
    

因为要换取的东西有多个,accesstoken userinfo 等 所以 定义对应的请求方法,然后在 类方法调用 就简单很多了。
def get_weibo_accesstoken(code):post 携带参数去请求,res 转成文本 取出 取出access_token 返回。

拿到 access_token 参数请求微博,他会返回我们 uid ,这个 uid 是 用户的唯一标识符。每次用户 扫码 发送的 uid 都是固定的,写一张表建立一个这样的字段然后保存他。
同时再去用刚才微博返回过来的 accesstoken 请求微博换取token
django实现,微博第三方登录超详细_第13张图片


# 查询 Access_token 的相关信息 (获取 weibo 唯一标识符 uid)
def get_weibo_userinfo(access_token):
    url = 'https://api.weibo.com/oauth2/get_token_info'
    data = {
        'access_token': access_token,
    }
    res = requests.post(url=url, data=data).text
    print('<218>Access 的相关信息:', res)
    return json.loads(res)['uid']


# 接收 微博回调页面 发送的 code
class Get_WeiBo_Code(APIView):
    def post(self, request):
        code = request.data['code']
        print(code)
        # 通过 code 获取授权的 access_token
        access_token = get_weibo_accesstoken(code)
        uid = get_weibo_userinfo(access_token)

# models.py
# 第三方平台登陆标识 表
class Social(models.Model):
    # 这里说一下 uid 是 每个平台都会 拥有的 是为了标明用户的身份
    uid = models.CharField(max_length=100, verbose_name='uid')
    user = models.ForeignKey('User', on_delete=models.CASCADE)

    THIRD_PARTY_CHOICE = {
         (1, 'QQ'),
         (2, 'WX'),
         (3, 'WB'),
    }

    third_party = models.IntegerField(choices=THIRD_PARTY_CHOICE)

    def __str__(self):
        return self.user.name

创建好 表后 迁移。

# terminal
python manage.py makemigrations
python manage.py migrate

判断 用户之前 是否绑定过 微博,绑定过 就直接登录,没绑定过 重新 绑定一下。

class Get_WeiBo_Code(APIView):
    def post(self, request):
        code = request.data['code']
        print(code)
        # 通过 code 获取授权的 access_token
        access_token = get_weibo_accesstoken(code)
        uid = get_weibo_userinfo(access_token)
        # 从数据库 取 数据判断 用户是否 注册过
        try:
            s = models.Social.objects.get(
                third_party=3,  # 直接绑定微博
                uid=uid
            )
        except:
            # 当 抛出异常就代表没有(微博用户)
            # 给出对应的 数据
            return Response({
                'code': '10030',
                'message': '用户不存在,请绑定本站',
                'uid': uid
            })
        else:
            # 没有 异常 代表曾经注册过
            # 生成 token 保存登录状态
            data = {
                'id': s.user.id,
                'email': s.user.email
            }
            jwt_token = create_JWT_(data)
            # 发送到前台
            return Response({
                'code': 200,
                'message': '您是本站的老用户',
                'token': jwt_token,
                'email': s.user.email
            })

补充 这个 jwt 是我 给用户加密的方法 自己生成的 伪随机数。

# 生成 JWT 加密
def create_JWT_(data):
    jwt_ = Risk_Serializer(SECRET_KEY, JWT_EXPIRE)
    return jwt_.dumps(data).decode()


# 解除 JWT 加密
def remove_JWT_(jwt_):
    try:
        jwt_ = Risk_Serializer(SECRET_KEY, JWT_EXPIRE)
    except SignatureExpired:
        return None
    return jwt_.loads(jwt_)
    

写完 逻辑之后 要给 前台 一个响应了。

	axios({
      'url': 'api/user/get_weibo_code/',
      'method': 'post',
      'data': form_data
	  }).then((res)=>{
	  // 判断用户是否 登陆成功, 如果成功 保持用户的 token 状态,然后直接 跳转到 首页(在后台 批判断过是否 绑定过 微博)
	      if(res.data.code == 200){
	          var token = res.data.token
	          window.localStorage.setItem('token', token)
	          this.$router.push({
	              name : 'course_index'
	          })
	      }else {
	      // 如没成功 就代表着 没 绑定过微博。跳转 然后携带我们的 唯一标识
	          this.message = res.data.message
	          this.$router.push({
	              name: 'bind_weibo',
	              query: {'uid': res.data.uid}
	          })
	      }
	  })
// 绑定微博的页面
<template>
<div id="bind_weibo">
    <form @submit.prevent="register_">
          <input type="email" v-model="register_email" @blur="check_email" class="form-control" placeholder="请输入邮箱">
                   
          <input type="password" v-model="register_pwd" class="form-control" placeholder="请输入密码">
                
    </form>
      <button @click="bind_weibo">绑定</button>
          <h3>{{ message }}</h3>
      </div>
</template>

定义点击事件,请求接口。


<script>
  import axios from 'axios'
    export default {
        name: "bind_weibo",
        data() {
            return {
                register_email: '',
                register_pwd: '',
                Can_be_bound: false,
                message: ''
            }
        },
        methods: {
            check_email() {
                var form_data = new FormData()
                form_data.append('email', this.register_email)
                axios({
                    'url': 'api/user/check_email/',
                    'method': 'post',
                    'data': form_data
                }).then((res) => {
                    if (res.data.code == 200) {
                        this.Can_be_bound = true
                    }
                    this.message = res.data.message
                })
            },
            

很简单吧,请求个接口,并同时判断 密码长度是否 足够长。看着代码很臃肿,多 且 乱 一步一步看 都是简单的。我们 用到了 @blur。

@blur 是什么?

     @blur 是当元素失去焦点时所触发的事件

当页面失去焦点的时候,触发 验证一下 邮箱是否可以被使用


# 检查邮箱是否被使用 过
class Check_email(APIView):
    def post(self, request):
        email = request.data['email']
        try:
            models.User.objects.get(email=email)
        except:
            return Response({
                'code': 200,
                'message': '邮箱可以被使用'
            })
        else:
            return Response({
                'code': 10200,
                'message': '邮箱被占用'
            })

点击绑定微博 触发的事件,

bind_weibo() {
                if (!this.Can_be_bound) {
                    this.message = '邮箱不可以被绑定'
                } else {
                    var register_pwd = this.register_pwd.length
                    console.log(register_pwd)
                    if (register_pwd < 6) {
                        this.message = '邮箱密码不得小于7位'
                    } else {
                        var form_data = new FormData()
                        form_data.append('email', this.register_email)
                        form_data.append('pwd', this.register_pwd)
                        form_data.append('uid', this.$route.query.uid);
                        axios({
                            'url': 'api/user/bind_weibo/',
                            'method': 'post',
                            'data': form_data

                        }).then((res) => {
                            if (res.data.code == 200) {
                                window.localStorage.setItem('token', res.data.token)
                                this.$router.push({
                                    name: 'course_index'
                                })
                            }
                        })
                    }
                }
            },
            mounted() {
                this.uid = this.$route.query.uid
            }

    }
</script>

看注释


# 当是新用户的时候 绑定 邮箱
class Bind_weibo(APIView):
    def post(self, request):
        # 获取前台数据
        email = request.data['email']
        pwd = request.data['pwd']
        uid = request.data['uid']

        # 生成新的用户
        user = models.User.objects.create(
            email=email,
            pwd=pwd
        )

        # 由接口 获取 判断是 weibo 的用户
        data = {
            'third_party': 3,
            'uid': uid,
        }
        # 反序列化 数据 存入 Social 表
        s = SocialSerializer(data=data, context={'user': user})
        if s.is_valid():
            s.save()
            # 生成 需要加密的数据
            data = {
                'id': user.id,
                'email': user.email
            }
            # 加密 发送到前台 保存登录状态
            jwt_token = create_JWT_(data)
            return Response({
                'code': 200,
                'token': jwt_token,
                'email': user.email
            })
        else:
            return Response({
                'code': 10300,
                'message': '数据填写不完整'
            })

# 序列化器
class SocialSerializer(Serializer):
    uid = serializers.CharField(max_length=190)
    third_party = serializers.IntegerField()

    def create(self, data):
        print(self.context)
        s = models.Social.objects.create(
            user=self.context['user'],
            **data
        )
        return s

呼 这样的话 就都完成了 收工。

你可能感兴趣的:(#,Vue.js,#,Django,微博,第三方登录)