登录流程

  • 前端获取认证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. 判断登录的 新老用户 是否绑定邮箱

登录微博开放平台,如果是新用户会审核一些东西,可以略过那些,选择性填写。。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注册好应用后我们不需要 填写那些 信息,我们主要用到的是这两个参数,是会用它去调取微博的各项参数最后换取 登录权
在这里插入图片描述
设置回调页,就是说登陆之后的跳转页面
在这里插入图片描述

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

(这里很重要)
在这里插入图片描述
在这里插入图片描述
我们只会用到这三个授权,看一下里面的文档。(将三个文档全部观看)
在这里插入图片描述

这些文档中的返回值字段,只要是 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。
可以点击扫描二维码,毕竟登陆快、哈哈

如果:

在这里插入图片描述
再多次尝试登陆之后会出现这样的页面代表着,浏览器已经存储了登录的Cookie Token 清楚浏览器数据缓存就 OK。像下面这样
在这里插入图片描述
当然你也可以直接授权,但是 后期的 保存登录状态就 不好设置了 会重叠 不利于 浏览。
好了现在写后台接口请求对应的 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
"><i class="fa fa-weibo"></i></a>

看一下 效果
在这里插入图片描述
我们点击扫码登陆后 (没写跳转后的页面肯定有报错)发现 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']

在这里插入图片描述

# 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
在这里插入图片描述


# 查询 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

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

Logo

前往低代码交流专区

更多推荐