之前的文章有写过通过jwt认证的文章,今天这一篇是通过自定义用户认证的; 使用场景:有些API需要用户登录成功之后,才能访问;有些无需登录就能访问 解决方法:创建两张表,一张用户表,一张token表,保存用户登录成功后生产的token; 然后需要认证的视图,前台每次请求需要在请求头中携带token,后端然后对token进行验证
缺点:每个用户登录一次就需要生成一条token记录保存在数据库里,当用户量大的时候,就会增加后台服务器压力!
模型类
from django.contrib.auth.hashers import make_password, check_password
from django.db import models
# Create your models here.
class User(models.Model):
"""
用户模型类
"""
username = models.CharField(max_length=32,verbose_name='用户名')
password = models.CharField(max_length=64,verbose_name='密码')
mobile = models.CharField(max_length=11,unique=True,verbose_name='手机号')
class Meta:
db_table = 'user'
verbose_name = "用户信息表"
verbose_name_plural = verbose_name
def __str__(self):
return self.username
def set_password(self,password):
self.password = make_password(password)
return None
def check_pwd(self,password):
return check_password(self.password,password)
class UserToken(models.Model):
"""用户token表"""
user = models.OneToOneField(User) # 与用户一对一关系
token = models.CharField(max_length=64,verbose_name='token')
class Meta:
db_table = 'token'
verbose_name = 'token表'
verbose_name_plural = verbose_name
一 views views.py 视图
from django.shortcuts import render
# Create your views here.
from rest_framework.generics import CreateAPIView, ListAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
from .utils import md5
from . import models
from . import ser
class UserLogin(APIView):
'用户登录视图类'
authentication_classes = []
# 登录不需要认证
def post(self,request):
username = request.POST.get('username').strip()
pwd = request.POST.get('password').strip()
if not all([username,pwd]):
return Response({'info':'参数不完整','code':400})
user = models.User.objects.get(username=username)
user.check_pwd(pwd)
# 登录成功后生成token
token =md5(username)
models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
res = {'info':'success','token':token,'code':200}
res['data'] = ser.UserInfoSer(user).data
return Response(res)
class UserRegister(CreateAPIView):
"""用户注册视图"""
authentication_classes = []
# 用户注册不需要认证
serializer_class = ser.CreateUserSer
class UserInfoList(ListAPIView):
"""用户详情页视图"""
serializer_class = ser.UserInfoSer
queryset = models.User.objects.all()
二.序列化器 新建ser.py文件
from rest_framework import serializers
from . import models
class CreateUserSer(serializers.ModelSerializer):
"""新增用户序列化器"""
password2 = serializers.CharField(max_length=64,write_only=True)
mobile = serializers.CharField(max_length=11,min_length=11,write_only=True)
def validate(self, attrs):
password = attrs['password']
password2 = attrs['password2']
if password != password2:
raise serializers.ValidationError('两次密码不一致,请重新输入!')
return attrs
def validate_mobile(self,value):
import re
if not re.match(r'1[3-9]\d{9}',value):
raise serializers.ValidationError('手机号格式不正确请重新输入!')
return value
def create(self, validated_data):
del validated_data['password2']
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = models.User
fields = '__all__'
class UserInfoSer(serializers.ModelSerializer):
"""用户详情信息序列化器"""
class Meta:
model = models.User
fields = ('id','username','mobile')
三 新建utils 文件,定义MD5加密方法
import hashlib
import time
def md5(user):
"""md5 加密token"""
ctime = str(time.time())
m = hashlib.md5(bytes(user, encoding='utf-8'))
m.update(bytes(ctime, encoding='utf-8'))
return m.hexdigest()
四 新建auth文件 重写认证类 自定义认证方法
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from .import models
class Authtication(BaseAuthentication):
def authenticate(self, request):
try:
token = request.META.get('HTTP_AUTHORIZATION',None)
except:
raise exceptions.AuthenticationFailed('用户认证失败')
token = token.split(' ')[1]
print(token)
token_query = models.UserToken.objects.filter(token = token)
if not token_query:
raise exceptions.AuthenticationFailed('无效的token')
# 返回(当前登录对象,token)
return (token_query.first().user,token_query.first())
def authenticate_header(self, request):
return 'Basic realm="user"'
五 settings 配置文件
REST_FRAMEWORK = {
# 全局使用的认证类
"DEFAULT_AUTHENTICATION_CLASSES": ['users.auth.Authtication', ],
"UNAUTHENTICATED_USER": None,
"UNAUTHENTICATED_TOKEN": None,
"DEFAULT_RENDERER_CLASSES": [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
]
}
总路由:
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^users/', include('users.urls')),
]
分路由:
from django.conf.urls import include, url
from django.contrib import admin
from . import views
urlpatterns = [
url(r'^register/$',views.UserRegister.as_view()),
url(r'^login/$',views.UserLogin.as_view()),
url(r'^list/$',views.UserInfoList.as_view()),
]
用户注册 测试:
用户登录测试:
用户详情页测试: