Django中内置的权限控制3-Login Logout
上面两篇文章,讲述的Django的Authentication系统的核心模型对象User API和相关的使用,本文继续深入,讨论如何在Web中使用Authentication系统。
前面说了,Django的这套权限系统有三个核心,User,Permission,Group。
而在Web应用中,任何的权限系统要做的第一步就是用户识别,也就是我们常说的登陆(login)。只有正确的登陆校验,知道用户是谁了,才能够知道用户能干什么,那就是许可(Permission)需要负责解决的事情,而Group则是批量设置许可的时候的一个便利手段了。
Web请求的认证:
django有一套方法,可以在每个view方法能够接收到的request对象中增加权限验证相关的方法。要做到这一点,首先需要:
- 安装SessionMiddleware和AuthenticationMiddleware。安装方法在settings文件中对MIDDLEWARE_CLASSES变量增加上述两个Middleware类。Middleware的设计几乎在任何web框架中都有体现,只不过叫法和具体实现的手段不尽相同,例如在struts2中叫做interceptor拦截器,采用函数递归Stack的方式实现(其它框架没见过有这么干的)。设计思路都是在request进入最后的处理方法之前经过一个个前处理,在处理方法处理完之后再经过一个个后处理,前后的次序恰好相反。有点点走题,请看例子:
1 MIDDLEWARE_CLASSES = ( 2 'django.contrib.sessions.middleware.SessionMiddleware', 3 'django.middleware.locale.LocaleMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 'django.middleware.csrf.CsrfViewMiddleware', 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.transaction.TransactionMiddleware', 9 )
一旦安装好了之后,在view中,我们就可以使用request.user获取当前的登陆用户User对象。如果当前用户没有登陆,那么request.user将会是我们之前所说的AnonymousUser对象。我们可以用User对象的is_authenticated()方法将这两者区分开来:
if request.user.is_authenticated(): # 做一些事情针对验证用户. else: # 做一些事情对于匿名未登录用户.
那么如何登陆一个用户呢?
需要两个函数:authenticate(username,password)和login(request,user),位于django.contrib.auth模块中;
这两个方法需要结合使用,
1.authenticate(username,password)函数需要两个参数username,password,如果校验通过则返回User对象,如果校验不通过返回None,例如:
from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: if user.is_active: print "You provided a correct username and password!" else: print "Your account has been disabled!" else: print "Your username and password were incorrect."
2.login接受两个参数,第一个是request对象,第二个是user对象。login方法使用SessionMiddleware将userID存入session当中。注意,在用户还未登录的时候,也存在着匿名用户的Session,在其登陆之后,之前在匿名Session中保留的信息,都会保留下来。这两个方法要结合使用,而且必须要先调用authenticate(),因为该方法会User的一个属性上纪录该用户已经通过校验了,这个属性会被随后的login过程所使用,例如:
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: if user.is_active: login(request, user) # 跳转到成功页面. else: # 返回一个无效帐户的错误 else: # 返回登录失败页面。
我们也可以不用authenticate()进行特定于一个用户的身份校验,直接使用和User无关的几个函数进行密码相关的校验,在Django1.4中以及新版本中提供以下方法,位于模块django.contrib.auth.hashers:
- check_password(password,encoded):第一个参数是明文密码,第二个参数是加密过的密码。如果通过校验返回True,不通过返回False;
- make_password(password[,salt,hashers]):根据给定的明文密码,salt,和Django支持的加密算法,返回一个加密的密码。如果password提供的值为None,那么该返回值将永远通不过check_password()方法。这个返回值是一个特定的约定值,目前是'!';
- is_password_usable(encoded_password):判断是否给定字符串是一个hashed密码,有机会通过check_password()函数的校验。
接下来,如何登出(log out)一个用户?
我们使用django.contrib.auth.logout函数来登出用django.contrib.auth.login函数登入的用户。
logout(requet)
函数只有一个参数,就是request。没有返回值,而且即使当前用户没有登陆也不会抛出任何异常。
例子:
from django.contrib.auth import logout def logout_view(request): logout(request) # 重定向到成功登出界面
这个方法,会将存储在用户session的数据全部清空,这样避免有人用当前用户的浏览器登陆然后就可以查看当前用户的数据了,回想一下login会保留anonymous用户的session数据。如果需要将一些东西加入到登出之后的用户session,那么需要在logout方法调用之后再进行。
接下来介绍Login和Logout的两个signals
Django的signal体系是一套简单实用的事件定义、事件产生、事件监听、事件处理框架,具体可以参看Django关于signal的文档。在登陆和登出这两个重要的点上,提供了两个signal:
- django.contrib.auth.signals.user_logged_in
- django.contrib.auth.signals.user_logged_out
有三个参数会随singal传过来:
- sender:user的class,如果是logout事件该值有可能是None如果用户根本就没有验证通过。
- request:HttpRequest对象
- user:user对象,如果是logout事件该值有可能是None如果用户根本就没有验证通过。
一个经常性的简单需求就是控制某些view(struts中叫做action方法)只对登陆用户开放,如果未登录用户请求该view则跳转到登录界面让其登陆。要做到这一点,我们可以这样做:
from django.http import HttpResponseRedirect def my_view(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/login/?next=%s' % request.path) # ...
也可以这样做,返回一个错误的页面:
def my_view(request): if not request.user.is_authenticated(): return render_to_response('myapp/login_error.html') # ...
更为优雅的方式是用decorator:
django.contrib.auth.decorators.login_required([redirect_field_name=REDIRECT_FIELD_NAME,login_url=None])
login_required()装饰器函数做了以下事情:
- 如果当前用户没有登陆,跳转到settings.LOGIN_URL,并传递当前的绝对路径到URL请求参数中,例如:/accounts/login/?next=/polls/3/
- 如果当前用户已经登陆了,执行view方法。在view中的方法可以认为当前用户已经登陆了。
login_required方法接受两个参数:
- redirect_field_name:默认值是next。用来定义登陆成功之后的跳回之前访问界面的url。
- login_url:默认值是settings.LOGIN_URL。用来指定登陆界面的url。如果不传入改参数,就需要确保settings.LOGIN_URL的值是正确设置的。
没有参数的login_required装饰器使用方法:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
传递参数的方法:
from django.contrib.auth.decorators import login_required @login_required(redirect_field_name='my_redirect_field') def my_view(request): ...
from django.contrib.auth.decorators import login_required @login_required(login_url='/accounts/login/') def my_view(request): ...
以上就是Django提供的用于完成login和logout相关的一些API支持,使用他们可以很好的对用户进行认证了,也就是说用户是谁系统已经搞清楚了,接下来就是更细粒度的判断,判断此人究竟能做些什么,也就是Permission许可的使用了。请看 Django中的权限控制-authentication-内置版本-4。