项目之中进行用户登录验证的!

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
        )
View Code
使用了自定义的 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
View Code

 

posted @ 2018-09-13 09:59  十七楼的羊  阅读(191)  评论(0编辑  收藏  举报