DRF小程序授权登录demo结合uni-app

本demo后端采用DRF框架(djangorestframeworf)框架,有对DRF不熟悉的可以先看看我之前的文章 分类 Django Restframework 下的文章

前端使用的是uni-app ,因为发现uni-app是在太好用了。就随便写了登录的例子
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-95ml1M7q-1611724545430)(/media/editor/20200824093026_20200824093037022174.jpg)]

创建项目数据

安装DRF pip install djangorestframework
创建新项目并新建app为users
这里我们直接继承django自带的user模型

from django.db import models
from django.contrib.auth.models import AbstractUser
from shortuuidfield import ShortUUIDField
# Create your models here.

class UserProfile(AbstractUser):

    uid = ShortUUIDField(primary_key=True, verbose_name='用户表主键')
    openid = models.CharField(unique=True,max_length=200, blank=True, null=True, verbose_name='开放id')
    telephone = models.CharField(unique=True, max_length=11, null=True, verbose_name="手机号码")
    username = models.CharField(unique=True, max_length=20, null=True, verbose_name='用户名')
    nickname = models.CharField(max_length=128, null=True, verbose_name='昵称')
    avatar = models.CharField(max_length=200, verbose_name='头像链接', default='https://dmall.wouldmissyou.com/20200822112506.jpg')
    gender = models.CharField(max_length=20, default='未知', verbose_name="性别")
    province = models.CharField(max_length=128, verbose_name='所在地区', null=True)
    address = models.CharField(max_length=128, verbose_name='详细地址', null=True)
    is_active = models.BooleanField(default=True, verbose_name="是否可用")
    is_staff = models.BooleanField(default=False, verbose_name="是否是员工")
    date_joined = models.DateTimeField(auto_now_add=True, verbose_name='加入时间')

然后在setting.py文件制定我们的模型

#setting.py
AUTH_USER_MODEL = 'users.UserProfile'

然后进行数据迁移

序列化用户模型

在users app中,新建serializers.py文件进行模型序列化,我们要想前端返回我们的user数据,除了密码之外的我们都要返回,具体返回的内容还是看自己实际项目中的需求,我们就直接把密码排除点,其他的都返回

from rest_framework import serializers
from .models import UserProfile


class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        exclude = ['password']

使用jwt进行认证

DRF中内置了好几种认证方法,但都不是我们所需要的,我们这里要使用JWT的方式进行认证,所以先安装一个包

pip install pyjwt

然后在users APP中新建一个py文件作为我们处理认证的文件,authentications.py

#authentications.py
import jwt
import time
from django.conf import settings
from rest_framework.authentication import BaseAuthentication, get_authorization_header
from rest_framework import exceptions
from django.contrib.auth import get_user_model
from jwt.exceptions import ExpiredSignatureError

User = get_user_model()


def generate_jwt(user):
    expire_time = time.time() + 60 * 60 * 24 * 7
    return jwt.encode({"userid": user.pk, "exp": expire_time}, key=settings.SECRET_KEY).decode('utf-8')


class JWTAuthentication(BaseAuthentication):
    keyword = 'JWT'

    def authenticate(self, request):
        auth = get_authorization_header(request).split()

        if not auth or auth[0].lower() != self.keyword.lower().encode():
            return None

        if len(auth) == 1:
            msg = "不可用的JWT请求头!"
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = '不可用的JWT请求头!JWT Token中间不应该有空格!'
            raise exceptions.AuthenticationFailed(msg)

        try:
            jwt_token = auth[1]
            jwt_info = jwt.decode(jwt_token, settings.SECRET_KEY)
            userid = jwt_info.get('userid')
            try:
                # 绑定当前user到request对象上
                user = User.objects.get(pk=userid)
                return user, jwt_token
            except:
                msg = '用户不存在!'
                raise exceptions.AuthenticationFailed(msg)
        except ExpiredSignatureError:
            msg = "JWT Token已过期!"
            raise exceptions.AuthenticationFailed(msg)

这个认证方式也是修改于DRF的内置方法BaseAuthentication,可以进源码看一下。

视图

关于微信认证的方式大家可以去看一下官方文档,这里就不阐述了
首先到微信小程序后台获取自己的appkey和appid
在setting.py中配置上

#setting.py
APP_ID = '填写你自己的小程序appid'
APP_KEY = '填写你自己的小程序appkey'

# 配置使用JWT方式认证
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ['users.authentications.JWTAuthentication']
}
#users/views.py
from rest_framework.views import APIView
from .serializers import UserProfileSerializer
from .authentications import generate_jwt
from rest_framework import status
from rest_framework.response import Response
from django.conf import settings
import requests, json
from django.contrib.auth import get_user_model
User = get_user_model()
from django.utils.timezone import now

# Create your views here.

class LoginView(APIView):
    def post(self, request):
        """进行code验证"""
        code = request.data.get('code')
        if not code:
            return Response({'message': "缺少code"}, status=status.HTTP_400_BAD_REQUEST)

        url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code" \
            .format(settings.APP_ID, settings.APP_KEY, code)

        r = requests.get(url)
        res = json.loads(r.text)
        openid = res['openid'] if 'openid' in res else None

        if not openid:
            return Response({"message":"微信调用失败"}, status=status.HTTP_503_SERVICE_UNAVAILABLE)

        # 判断是否是第一次登陆
        try:
            user = User.objects.get(openid=openid)
            user.last_login = now()
            # print(user.last_login)
            user.save()

        except Exception:
            # 微信用户第一次登陆,新建用户
            nickname = request.data.get('nickName')
            gender = request.data.get('gender')
            avatar = request.data.get('avatarUrl')
            province = request.data.get('province')+request.data.get('city')
            user = User.objects.create(nickname=nickname, province=province, gender=gender, avatar=avatar, openid=openid,
                                       password=openid)

        serializer = UserProfileSerializer(user)
        token = generate_jwt(user)
        return Response({"user": serializer.data, "token": token, 'status':status.HTTP_202_ACCEPTED})

然后在配置路由

path('login/', views.LoginView.as_view(), name='login'),

至此,后台部分配置完毕

前端部分

我这边为了后期方便开发,直接封装了一个简单的request.js

const baseURL = 'http://127.0.0.1:8000/';
const http = (options) => {
    return new Promise((resolve, reject) => {
        uni.showLoading({
            title: '加载中...',
            mask: options.load || false // 默认遮罩出现可以继续操作
        });
        try{
            uni.request({
                url: (options.baseURL || baseURL) + options.url,
                method: options.method || 'POST', // 默认为POST请求
                data: options.data, //请求超时在manifest.json配置
                header: {
                    'token': uni.getStorageSync('token'),
                    'Content-Type': options.header == 'form' ? 'application/x-www-form-urlencoded' : 'application/json'
                },
                success: res => {
                    resolve(res.data)
                },
                fail: (err) => {
                    reject(err.data);
                    console.log(err);
                    uni.showToast({
                        title: '请检查网络连接',
                        icon: 'none'
                    })
                    /*错误码处理
                    let code = err.data.code; 
                    switch (code) {
                        case 1000:
                            break;
                        default:
                            break;
                    } */
                },
                complete: () => {
                    uni.hideLoading();
                }
            });
        }catch(e){
            uni.hideLoading();
            uni.showToast({
                title: '服务端异常',
                icon: 'none'
            })
        }
        
    })
}

export default http

然后在main.js中声明一下

import http from "@/utils/http.js"
Vue.prototype.$HTTP = http

然后编写Login.vue

<template>
	<view>
		<!-- #ifdef MP-WEIXIN -->
		<view>
			<view>
				<view class='header'>
					<image :src='userInfo.avatar'></image>
					<text>{{userInfo.nickname}}</text>
				</view>
				<view class='content'>
					<view>申请获取以下权限</view>
					<text>获得你的公开信息(昵称,头像、地区等)</text>
				</view>

				<button class='bottom' type='primary' open-type="getUserInfo" withCredentials="true" lang="zh_CN" @getuserinfo="wxGetUserInfo">
					授权登录
				</button>
				<button class='bottom' type='warn' @click="logoutBtn">退出登录</button>

			</view>
		</view>
		<!-- #endif -->
	</view>
</template>

<script>
	export default {
		data() {
			return {
				userInfo: {},
				isLogin: false //默认为true
			};
		},
		mounted(){
			const getuserInfo = uni.getStorageSync("userInfo")
			this.userInfo = getuserInfo
		},
		methods: {
			wxGetUserInfo() {
				let _this = this;
				uni.getUserInfo({
					provider: 'weixin',
					lang:"zh_CN",
					success: function(infoRes) {
						_this.data = infoRes.userInfo
						// console.log(_this.data)
						uni.showLoading({
							title: "登录中"
						})
						uni.login({
							provider: "weixin",
							success: (loginres) => {
								if (!loginres.code) {
									console.log("登录失败,请再次点击~~");
									return;
								}
								_this.data['code'] = loginres.code;
								const params = _this.data
								// console.log(params)

								_this.$HTTP({
									method: "POST",
									url: "login/",
									data: params
								}).then((res) => {
									console.log(res)
									if (res.status=='202') {
										uni.setStorageSync('token', 'JWT '+res.token)
										uni.setStorageSync('userInfo', res.user)
										_this.userInfo = uni.getStorageSync('userInfo')
						
									}
								})

							}
						})

					}
				})
			},
			logoutBtn(){
				// 先判断用户是否登录
				const userStatus = uni.getStorageSync('token')
				if(!userStatus){
					uni.showToast({
						icon:"none",
						title:"您未登录"
					})
				}else{
					uni.removeStorageSync('token')
					this.userInfo = {}
				}
			}
		},
	}
</script>

<style>
	.header {
		padding: 90rpx;
		border-bottom: 1px solid #ccc;
		text-align: center;
		width: 100%;
		line-height: 60rpx;
		box-sizing: border-box;
		display: flex;
		align-items: center;
		justify-content: center;
		flex-direction: column;
	}

	.header image {
		width: 200rpx;
		height: 200rpx;
	}
	

	.content {
		margin-left: 50rpx;
		margin-bottom: 90rpx;
	}

	.content text {
		display: block;
		color: #9d9d9d;
		margin-top: 40rpx;
	}

	.bottom {
		border-radius: 80rpx;
		margin: 70rpx 50rpx;
		font-size: 35rpx;
	}
</style>

最后生成小程序看看效果吧

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
DRFDjango Rest Framework)是一个用于构建 Web API 的强大框架,而微信小程序是一种基于微信平台的应用程序。下面是一个 DRF 微信小程序开发示例: 1. 创建 Django 项目和应用程序 首先,我们需要使用 Django 创建项目和应用程序。在终端中输入以下命令: ``` django-admin startproject myproject cd myproject python manage.py startapp myapp ``` 2. 创建 DRF 视图 创建一个名为 views.py 的文件,并添加以下代码: ```python from rest_framework.views import APIView from rest_framework.response import Response from django.http import HttpResponse class MyView(APIView): def get(self, request): return Response({'message': 'Hello World!'}) def post(self, request): return Response({'message': 'Received POST request!'}) ``` 这里我们创建了一个名为 MyView 的 DRF 视图,该视图包含 get 和 post 方法。 3. 配置 URL 在应用程序的 urls.py 文件中,添加以下 URL 配置: ```python from django.urls import path from myapp.views import MyView urlpatterns = [ path('api/', MyView.as_view()), ] ``` 这里我们配置了一个名为 api 的 URL,该 URL 将映射到 MyView 视图。 4. 创建微信小程序 在微信开发者工具中创建一个新的小程序项目,设置相关配置并获取 AppID。 5. 编写小程序代码 在小程序app.js 文件中,添加以下代码: ```javascript App({ onLaunch: function () { console.log('App launched') }, }) ``` 这里我们定义了一个名为 onLaunch 的函数,该函数在小程序启动时被调用。 在小程序的 index.js 文件中,添加以下代码: ```javascript Page({ onLoad: function () { wx.request({ url: 'https://example.com/api/', method: 'GET', success: function (res) { console.log(res.data) } }) }, }) ``` 这里我们定义了一个名为 onLoad 的函数,该函数在小程序加载时被调用,并使用 wx.request 方法向我们创建的 DRF 视图发送 GET 请求。 6. 运行 DRF 服务器和小程序 在终端中输入以下命令以启动 DRF 服务器: ``` python manage.py runserver ``` 在微信开发者工具中运行小程序并查看控制台输出。如果一切正常,你应该能够看到来自 DRF 视图的响应。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘刘刘刘露

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值