RESTframework提供了许多用于身份验证方案,也允许自定义方案。主要有BasicAuthentication(通过 HTTP Basic Authentication方案根据用户的用户名和密码进行签名认证)、SessionAuthentication(通过Django默认session进行后端认证)、TokenAuthentication(通过令牌进行 HTTP 认证)、RemoteUserAuthentication(托给 Web 服务器认证)。
RESTframework提供了认证和权限两个不同的功能,很多人可能会混淆,用户认证(Authentication)和用户授权(Authorization)是两个不同的概念,用具简单的话来说你是会员(是否为会员认证了用户是否存在)但不是vip,所以无权限(虽然都是用户,可是我只授权了VIP用户可使用该功能)操作。
下面我用BasicAuthentication来实现用户认证,BaseAuthentication 中有两个方法 authenticate 和 authenticate_header, 其中 authenticate是必须实现的方法,而且无论是自定义的认证类还是RESTframework自带的认证类都应该继承BaseAuthentication,自定义的话只需要重写authenticate方法即可。
新增一个模型类,用来存储用户的token信息,用外键一对一关联用户,每次用户登录的时候更新或者新增token,这里我的token打算用md5来生成,以此来保证token的唯一性和时效性(新增之后别忘了同步数据库)。
class UserToken(models.Model):
user = models.OneToOneField('User', models.CASCADE)
token = models.CharField(max_length=64)
#同步数据库
python manage.py makemigrations
python manage.py migrate
在views.py文件中新增登录视图,主要功能为获取前端提交的数据,然后去匹配数据库中是否存在这个用户,如果存在我们就生成一个token并将数据库中原有的token更新,同时给前端返回成功的提示,如果不存在则返回给前端错误。
def md5(user):
ctime = str(time.time())
m = hashlib.md5(bytes(user, encoding='utf-8'))
m.update(bytes(ctime, encoding='utf-8'))
return m.hexdigest()
class Login(APIView):
def post(self, request):
res = {'code': 403, 'msg': ''}
try:
user = request.POST.get('username')
pwd = request.POST.get('password')
print(user, pwd)
userObj = User.objects.filter(username=user, password=pwd).first()
if not userObj:
res['msg'] = "用户名或者密码错误"
return Response(res)
token = md5(user)
print('123456789')
UserToken.objects.update_or_create(user=userObj, defaults={'token': token})
res['code'] = 200
res['msg'] = '登录成功'
res['token'] = token
except Exception as e:
res['msg'] = str(e)
return Response(res)
在views.py 同级的目录创建 auth.py 文件,新增一个token认证类,规定获取Token的方式为请求头传递,用来认证用户是否为合法用户,当用户登录之后访问其他页面时将token携带发给后台,后台拿到这个token之后进行认证,如果通过则说明该用户为合法用户,允许发送相关数据给该用户。
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from user.models import *
class TokenAuth(BaseAuthentication):
def authenticate(self, request):
# 规定token用请求头传递
token = request.META.get('HTTP_AUTHORIZATION')
token_obj = UserToken.objects.filter(token=token).first()
if not token_obj:
raise AuthenticationFailed('用户认证失败')
return (token_obj.user, token_obj)
比如登录之后访问用户个人信息页面,在跳转至个人信息页面时向服务器发送请求获取用户个人信息,请求时附上token信息,当服务器监听到请求时对token进行认证,如果token存在则说明该用户是合法用户,允许获取对应的数据。 调用我们刚才写的认证类我们只要子视图中引入对应的类然后在需要认证的的类中标注认证类即可。
class UserInfo(APIView):
authentication_classes = [TokenAuth] # 在需要认证的类中标注认证类
# 通过认证后,用request.user拿到当前登录的用户
def get(self, request):
print(request.user)
serializer = UserSerializer(request.user)
res = {'code': 200, 'msg': '验证成功'}
res['userInfo'] = serializer.data
return Response(res)
在一个项目中,基本所有有业务需求的api都需要进行视图认证,如上的方法需要在每个需要认证的视图中标注认证类,这样操作就会变得繁琐而且也会出现代码冗余,增加维护难度,所以我们来定义一个全局视图认证,这样所有的认证视图都不需要添加认证类属性,如果不分视图不需要进行视图认证(如登录视图)我们可以在局部设置authentication_classes = [],这样就可以屏蔽视图认证了。全局认证我们需要在settings.py中添加如下配置:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["user.auth.TokenAuth"]
}
欢迎关注本人的公众号:编程手札,文章也会在公众号更新