# -*- coding: utf-8 -*-
import logging
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.models import AnonymousUser
from jsonschema import validate
from rest_framework.exceptions import AuthenticationFailed, PermissionDenied, ValidationError
from rest_framework.response import Response
from rest_framework.views import APIView
from six import raise_from
from .exceptions import InvalidJsonException
from ..models import User
from .exceptions import LoginFail
logger = logging.getLogger(__name__)
def json_schema_validate(schema):
def valieated_func(func):
def _func(self, request, *args, **kwargs):
try:
validate(request.data, schema)
except ValidationError as e:
# 自定义异常类
raise InvalidJsonException(e.message)
else:
return func(self, request, *args, **kwargs)
return _func
return valieated_func
class SessionView(APIView):
permission_classes = ()
def get(self, request):
user = request.user
# 如果没有登录的话,是 AnonymousUser 实例,如果登录了,那么是 User 实例!
#jango 使用会话和中间件把身份验证系统插入 request 对象,为每个请求提供 request.user 属性,表示当用户。如果未登陆,这个属性的值是一个 AnonymousUser 实例,否则是是一个 User 实例
if isinstance(user, AnonymousUser):
raise AuthenticationFailed(
detail='Incorrect authentication credentials.'
)
role = request.GET.get('role')
# 没有使用 django 自带的 User类,自定义的user类
if role is not None and not user.check_role(role):
raise PermissionDenied(
detail='Incorrect user role.'
)
return Response()
# 装饰器起到一个简单的字段类型的验证的功能!
@json_schema_validate({
'type': 'object',
'properties': {
'user': {
'type': 'string',
'minLength': 1
},
'pass': {
'type': 'string',
'minLength': 1
},
},
})
def post(self, request):
'''get auth token
this api can be used in two method:
request contains username/password in body, return token
request contains exists valid token, return a new token
'''
if isinstance(request.user, AnonymousUser):
# 没有登录的用户进行登录!
return self.login(request)
else:
# 此时用户登录!也为用户设置一个新的session
return self.renew_token(request)
def login(self, request):
'''密码校验成功,登录操作!'''
try:
username = request.data['user']
password = request.data['pass']
user = User.objects.get(username=username)
except User.DoesNotExist as e:
logger.exception('User[ %s ] is not exists', username)
raise_from(
LoginFail, e
)
else:
if not user.is_activate():
raise LoginFail(user)
# authentication 需要在 setting指定自定义的校验类
# AUTHENTICATION_BACKENDS = ('schoool.user.plugins.AuthBackend',)
success = authenticate(user=user, password=password)
if not success:
user.login_fail()
logger.info('User[ %s ] authenticated failed', username)
raise LoginFail()
# 更新一下登录时间!
user.login_success()
return Response({'token': self.build_token(user)})
def renew_token(self, request):
user = request.user
# 判断登录时间是否过期!
if not user.is_activate():
raise LoginFail(user)
return Response({'token': self.build_token(user)})
def build_token(self, user):
import jwt
from cryptography.fernet import Fernet
from django.utils.timezone import now
now = now()
return jwt.encode(
{
'id': user.pk,
'iss': 'antilles-user',
'sub': user.username,
'role': user.get_role_display(),
'iat': now,
'nbf': now,
'exp': now + settings.TOKEN_EXPIRE,
'jti': Fernet.generate_key(),
},
settings.SECRET_KEY,
algorithm=settings.TOKEN_ALGORITHMS
)