公司的一个Django项目用的是mongoDB,所以Django自带的那一套权限不能使用,我们就自定义了一套,在每个重写的地方都附带了源码地址,帮助理解
request.user
- 从setting中获取AUTHENTICATION_BACKENDS的元组, 默认情况下是django.contrib.auth.backends.ModelBackend.
- 遍历这整个元组,然后调用每一个Backend的authenticate方法,而每个Backend.authenticate会有一个登陆逻辑(自定义后面会提及),如ModelBackend.authenticate它拿传过来的账号密码的与数据库user model的username与password进行验证。
- 如果Backend.authenticate验证通过,就会返回一个user对象,然后auth.authenticate将不再进行遍历,return user对象。如果返回None,就继续调用下一个Backend.authenticate。如果全部都返回None,那么验证不通过。
- 一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户,存在于上下文,可以使用,配合LoginRequiredMixin使用
- 如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们
- request.user传给前端的时候,前端可以通过 {% if request.user.is_authenticated %}判断用户时候登录
通过authenticate()返回一个user对象
user = authenticate(email=email, password=secret_password)
#logi()接受一个 HttpRequest 对象和一个 User 对象作为参数并使用Django的会话( session )框架把用户的ID保存在该会话中
login(request, user)
AUTHENTICATION_BACKENDS
检查身份验证。当有人调用时 django.contrib.auth.authenticate()
- 如如何记录用户中所述 - Django尝试对其所有身份验证后端进行身份验证。如果第一个身份验证方法失败,Django会尝试第二个身份验证方法,依此类推,直到尝试了所有后端。
在settings.py 配置
AUTHENTICATION_BACKENDS = ['persona.apps.users.backends.DocumentBackend']
persona.apps.users.backends.DocumentBackend
https://docs.djangoproject.com/en/2.0/topics/auth/customizing/
from persona.apps.users.models import Users
# from django.contrib.auth.backends import ModelBackend
class DocumentBackend:
#从数据库中匹配用户
#get_by_id()自定义匹配用户的方法
def get_user(self, user_id):
return Users().get_by_id(user_id)
#自定义的authenticate
def authenticate(self, request, email=None, password=None):
#如果账号密码正确,返回user对象
#check_password(email, password),自定义的用户认证
if Users().check_password(email, password):
#get_by_email()自定义
return Users().get_by_email(email)
return None
from django.contrib import auth,重写了其中的login,logout,get_user
login()
- login()接受一个 HttpRequest 对象和一个 User 对象作为参数并使用Django的会话( session )框架把用户的ID保存在该会话中
- 生成一个新的session_id 放到request.session中(这个session在返回时会被session的中间件进行处理,将session_id放入到cookie当中)
- 将这个user放到request.user中
logout()
- 将request.session清除
- request.user = AnonymousUser()将用户设置与匿名用户
https://docs.djangoproject.com/en/2.1/topics/auth/default/#auth-web-requests
from django.contrib.auth import *
from .models import *
from persona.apps.users.backends import DocumentBackend
SESSION_KEY = '_auth_user_id'
BACKEND_SESSION_KEY = '_auth_user_backend'
HASH_SESSION_KEY = '_auth_user_hash'
REDIRECT_FIELD_NAME = 'next'
def login(request, user, backend=None):
"""
Persist a user id and a backend in the request. This way a user doesn't
have to reauthenticate on every request. Note that data set during
the anonymous session is retained when the user logs in.
"""
session_auth_hash = ''
if user is None:
user = request.user
if hasattr(user, 'get_session_auth_hash'):
session_auth_hash = user.get_session_auth_hash()
request.session[SESSION_KEY] = user.id_to_string()
request.session[HASH_SESSION_KEY] = session_auth_hash
request.user = user
rotate_token(request)
user_logged_in.send(sender=user.__class__, request=request, user=user)
def logout(request):
"""
Remove the authenticated user's ID from the request and flush their session
data.
"""
# Dispatch the signal before the user is logged out so the receivers have a
# chance to find out *who* logged out.
user = getattr(request, 'user', None)
if not getattr(user, 'is_authenticated', True):
user = None
user_logged_out.send(sender=user.__class__, request=request, user=user)
# remember language choice saved to session
language = request.session.get(LANGUAGE_SESSION_KEY)
request.session.flush()
if language is not None:
request.session[LANGUAGE_SESSION_KEY] = language
if hasattr(request, 'user'):
from django.contrib.auth.models import AnonymousUser
request.user = AnonymousUser()
#LoginRequiredMixin会调用这里
def get_user(request):
#如果是没有登录的用户,则设置为匿名用户
from django.contrib.auth.models import AnonymousUser
user = None
if request.session.__contains__(SESSION_KEY):
user_id = request.session[SESSION_KEY]
user = DocumentBackend().get_user(user_id)
#返回user 或者 匿名用户
return user or AnonymousUser()
from django.contrib.auth.middleware import AuthenticationMiddleware,重写了AuthenticationMiddleware,并且在setting中配置
用户每次发起请求的时候,被AuthenticationMiddleware中间件处理,会去调用auth模块,auth的get_user方法会返回当前user,如果是没有登录验证的用户,则会返回一个匿名用户,将user表示当前登录用户的属性添加到每个传入HttpRequest对象,也就是request.user
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
'persona.apps.users.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
persona.apps.users.middleware.AuthenticationMiddleware
from django.utils.deprecation import MiddlewareMixin
from persona.apps.users import auth
from django.contrib.auth import authenticate
# from django.contrib import auth
# from django.contrib.auth.middleware import AuthenticationMiddleware
#这一段源码在django.contrib.auth.middleware中
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
request.user = get_user(request)
https://docs.djangoproject.com/en/2.1/ref/middleware/
Django用户发起访问流程
用户发起访问流程,会先经过中间件,中间件调用auth.get_user(),调用auth则会调用AUTHENTICATION_BACKENDS验证方法,返回一个user对象(如果是没有登录的用户则返回一个匿名user对象,在类视图中继承了LoginRequiredMixin,如果是具体的user对象,则会分发请求,如果是匿名的user对象,则返回登录),将user
表示当前登录用户的属性添加到每个传入HttpRequest
对象,也就是request.user。
调用login,logout(django.contrib import auth中的方法)等都会调用到AUTHENTICATION_BACKENDS
另外,还自定义了一些验证方法
from datetime import datetime, timedelta
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.hashers import make_password
from django.db import models
from mongoengine import *
# from persona.apps.users.backends import DocumentBackend
from persona.settings import DBNAME
# Create your models here.
connect(DBNAME)
class Users(Document):
"""用户数据库"""
account = EmailField(max_length=40)
password = StringField(max_length=150)
stars = IntField(max_value=5)
signUpDate = DateTimeField()
lastSignIn = DateTimeField()
profile = DictField() # 基本资料
friends = ListField() # 好友
created = ListField() # 创建的
liked = ListField() # 收藏
labeled = DictField() # 标签'LABEL': [PERSONA_ID, PERSONA_ID, PERSONA_ID],
browsed = ListField() # 最近浏览的
point = IntField(default=0) # 积分
invcode = IntField(null=False) # 邀请码
vip = BooleanField(default=False) # 会员
duetime = StringField(null=True) # 到期时间
rookie = BooleanField(default=True) # 是否为新手
is_authenticated = True
def id_to_string(self):
'''session_key'''
result = 0
try:
result = str(self.id)
except:
pass
return result
# 通过id 匹配 user
def get_by_id(self, user_id):
try:
return Users.objects.get(id=user_id)
except DoesNotExist:
return None
# 通过email 匹配 user
def get_by_email(self, user_email):
try:
return Users.objects.get(account=user_email)
except DoesNotExist:
return None
#检查账户密码
def check_password(self, email, password):
try:
user = Users.objects.get(account=email, password=password)
if user is not None:
return True
return False
except DoesNotExist:
return False