1,用户进行session判断(自带的),是否是登录用户
2,如果是登录用户那么就会重新设置一下session值,非登录用户进行重新登录!
4,重写了用户类,不用django自带的用户类,也就是不使用django自带的权限校验.
# -*- 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 )
使用了自定义的 user 类
from django.db.models import ( BigIntegerField, CharField, DateTimeField, EmailField, FloatField, ForeignKey, IntegerField, Model, TextField, ) class User(Model): ROLES = {r[1]: r[0] for r in USER_ROLES} USERNAME_FIELD = 'username' REQUIRED_FIELDS = [] username = CharField( unique=True, max_length=32, error_messages={'username': "User already exists"} ) first_name = CharField(max_length=30, null=True) last_name = CharField(max_length=30, null=True) email = EmailField(null=True) role = IntegerField(choices=USER_ROLES, default=USER_ROLES[-1][0]) last_login = DateTimeField(null=True) date_joined = DateTimeField(default=timezone.now) last_operation_time = DateTimeField(null=True) bill_group = ForeignKey('BillGroup', null=True, related_name="bill_members", on_delete=models.PROTECT) fail_chances = IntegerField(default=0, null=False) effective_time = DateTimeField(auto_now_add=True, null=False) def __init__(self, *args, **kwargs): super(User, self).__init__(*args, **kwargs) try: self._passwd = pwd.getpwnam(self.username) except KeyError: self._passwd = None def login_success(self): self.last_login = timezone.now() self.fail_chances = 0 self.save() def login_fail(self): self.fail_chances += 1 if self.fail_chances >= settings.LOGIN_FAIL_MAX_CHANCE: self.fail_chances = 0 self.effective_time = \ timezone.now() + settings.LOGIN_FAIL_LOCKED_DURATION self.save() @property def remain_chances(self): return settings.LOGIN_FAIL_MAX_CHANCE - self.fail_chances @property def remain_time(self): now = timezone.now() return timedelta() \ if now >= self.effective_time \ else self.effective_time-now def is_activate(self): return timezone.now() >= self.effective_time def is_authenticated(self): return True def is_anonymous(self): return False @property def is_admin(self): return self.role >= ROLE_ADMIN @property def workspace(self): return self._passwd.pw_dir if self._passwd else None @property def uid(self): return self._passwd.pw_uid if self._passwd else None @property def gid(self): return self._passwd.pw_gid if self._passwd else None @property def group(self): if self._passwd is not None: try: return grp.getgrgid(self._passwd.pw_gid) except KeyError: return None else: return None @classmethod def get_role_value(cls, role): return cls.ROLES[role] @property def freeze_time(self): import time return time.mktime( self.effective_time.timetuple()) if not \ self.is_activate() else None def as_dict(self, **karg): data = model_to_dict(self) data.update( role=self.get_role_display(), workspace=self.workspace, is_freezed=not self.is_activate(), effective_time=self.freeze_time ) data['bill_group'] = model_to_dict( self.bill_group, ) if self.bill_group else None data['os_group'] = grp_to_dict(self.group) if self.group else None return data def require_role(self, cls): if self.role < cls.floor: raise PermissionDenied def check_role(self, role): floor = self.ROLES.get(role, 0) return self.role >= floor