django1.8认证系统(3)

Authentication in Web requests

django使用会话(sessions)和中间件(middleware)来连接认证系统(authentication system)和请求对象(request objects)
每个请求都有一个request.user属性来表示当前用户.如果当前用户没有登录,这个属性是一个AnonymounsUser实例,否则,是一个User实例.
你可以使用is_authenticated(),例如:

if request.user.is_authenticated():
    # 对认证用户do something
    ...
else:
    # 对匿名用户do something
    ...

How to log a user in

如果你想把一个登录用户加入会话,使用login()方法
- login()
在view里,是用户登录,使用login().它有2个参数,HttpRequest对象和User对象.login()会保存用户ID到会话,使用django的会话框架.
注意:用户在登录之后,匿名访问期间产生的会话数据不会清除
这个例子展示了如何使用authenticate()和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():
    当你手动的登录用户时,在调用login()之前,必须先用authenticate()方法成功的验证用户.authenticate()方法会给User设置一个属性来标记认证后台已经成功的验证了该用户,而且
    登录过程需要这个信息.如果你直接将user对象和数据库里的数据进行比对,会抛出异常.

How to log a user out

  • logout()
    对于一个通过django.contrib.auth.login()登录的用户,可以在view里使用django.contrib.auth.logout()来注销.它有一个参数,HttpRequest对象,无返回值.例如:
from django.contrib.auth import logout

def logout_view(request):
    logout(request)
    # 重定向到成功页面

注意:如果用户没有登录,logout()方法不会抛出异常
当你调用logout()后,当前请求的会话里的数据会完全清除.所有存在的数据会清除.这是为了安全而防止以下事情发生:同一个浏览器的下一个使用者访问上一个使用者的会话数据.如果你想在用户注销时在会话里存入信息,请在调用django.contrib.auth.logout()之后再做.

Limiting access to logged-in users(登录用户的限制访问)

最直接的方法

实现限制访问最简单粗暴的方法就是检查request.user.is_authenticated(),重定向到登录页面

from django.conf import settings
from django.shortcuts import redirect

def my_view(request):
    if not request.user.is_authenticated():
        return redirct('%s?next=%s' % (settings.LOGIN_URL, request.path))
    # ...

…或者显示错误信息:

from django.shortcuts import render

def my_view(request):
    if not request.user.is_authenticated():
        return render(request, 'myapp/login_error.html')
    # ...
The login_require decorator
  • login_required([redirect_field_name=REDIRECT_FIELD_NAME, login_url=None])
    懒人有懒方法,使用login_required()装饰器:
from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

login_required()方法做了这些事:
- 如果用户没有登录,重定向到settings.LOGIN_URL,也就是query string中的当前绝对路径.例如:/accounts/login/?next=/polls/3/.
- 如果用具已经登录,正常执行view

默认情况下,路径验证成功后,用户会被重定向的地址,是查询字符串参数”next”的值.如果你想使用其他参数名,login_required()有一个可选参数,redirect_field_name.

from django.contrib.auth.decorators import login_required

@login_required(redirect_field_name='my_redirect_field')
def my_view(request):
    ...

注意:如果你提供了redirect_field_name的值,你很可能需要定制你的登录模板,因为默认情况下,模板内容中,存储跳转路径的变量的参数是”next”.
login_required()还有一个可选参数:login_url.例如:

from django.contrib.auth.decorators import login_required

@login_required(login_url='/accounts/login/'
def my_view(request):
    ...

注意:如果你没有指定login_url,你需要确保settings.LOGIN_URL存在并且login view能正确链接.例如,默认情况下,将下面的代码添加到你的URLconf:

from django.contrib.auth import views as auth_views

url(r'^accounts/login/$', auth_views.login),

settings.LOGIN_URL也能接受试图函数名和named URL patterns作参数.这样就能灵活的映射登录视图和URLconf.

注意:login_required装饰器不会检查用户的is_active状态

Limiting access to logged-in users that pass a test

使用一些测试来做限制访问,你要做的基本上就是上一节介绍的方法
最简单粗暴的方法是直接在view里对request.user里进行测试.例如,这个view会做测试来确保用户有所需域的电子邮箱,否则跳转到登录页面:

from django.shortcuts import redirect

def my_view(request):
    if not request.user.email.endswith('@example.com'):
        return redirect('/login/?next=%s' % request.path)
  • user_passes_test(func[,login_url=None, redirect_field_name=REDIRECT_FIELD_NAME])
    快捷方式是,你可以使用方便的user_passes_test装饰器,当函数返回False时执行跳转:
from django.contrib.auth.decorators import user_passes_test

def email_check(user):
    return user.email.endswith('@example.com')

@user_passes_test(email_check)
def my_view(request):
    ...

user_passes_test()方法有一个必须参数:一个函数(参数是User对象,返回布尔值).注意,user_passes_test()不会自动的检查用户是否是匿名用户.
user_passes_test()有2个可选参数:
log_url
可以指定未通过测试的用户跳转的地址.可以是一个登录界面,如果不指定,默认是settings.LOGIN_URL
redirect_field_name
和login_required()相同.例如:

@user_passes_test(email_check, login_url='/login/'):
def my_view(request):
    ...
The permission_required decorator
  • permission_required(perm[, login_url=None, raise_exception=False])
    这是一种比较常用的任务,检查用户是否有特定的权限.所以,django提供了实现这种功能的快捷方式:permission_required()装饰器:
from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote')
def my_view(request):
    ...

就像has_perm()方法一样,参数是”.”(例如,polls应用里的模型的权限,polls.can_vote)
**注意:**permission_required()也有一个可选参数login_url,例如:

from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote', login_url='/loginpage/')
def my_view(request):
    ...

就像在login_required()装饰器里一样,login_url默认是settings.LOGIN_URL.
如果指定了raise_exception参数的值,装饰器会抛出异常PermissionDenied,也就是403(HTTP Forbidden),而不是重定向到登录页.
django1.7新增:
permission_required()装饰器可以接受permissions列表作为参数.

Applying permissions to generic views

To apply a permission to a class-based generic view, decorate the View.dispatch method on the class. See Decorating the class for details. Another approach is to write a mixin that wraps as_view().

Session invalidation on password change(密码更改后会话失效)

django1.7新增
警告:
这个保护措施只在以下情况生效:MIDDLEWARE_CLASSES里有SessionAuthenticationMiddleware.如果django>=1.7,在startproject时就在settings.py里自动生成了.
django2.0将采用强制会话验证不管SessionAuthenticationMiddleware是否可用.如果你使用的django版本<1.7,或者你使用的模板中没有SessionAuthenticationMiddleware,在越多下面的注意事项前考虑激活它.
如果你的AUTH_USER_MODEL继承自AbstractBaseUser或实现了自己的get_session_auth_hash()方法,authenticated sessions会包含这个方法返回的哈希值.如果是AbstractBaseUser,会用密码字段的HMAC(哈希的消息验证代码).如果SessionAuthenticationMiddleware可用,django会在request中的哈希值是否与服务端已计算好的哈希值匹配.这就使得用户改密码时注

django有默认的更改密码视图,django.contrib.auth.views.password_change(),django.contrib.auth的admin里也有user_change_password,会在用户更改密码的时候更新会话里的新密码哈希值,用户就不会注销.如果你有一个自定义的更改密码视图,希望有同样的效果,使用这个方法:

update_session_auth_hash(request, user)
这个方法有2个参数,现在的请求和更新的User对象,新的会话哈希会生成并替换就得会话.实例用法:

from django.contrib.auth import update_session_auth_hash

def password_change(request):
    if request.method == 'POST':
        form = PasswordChangeForm(user=request.user, data=request.POST)
        if form.is_valid():
            form.save()
            update_session_auth_hash(request, form.user)
    else:
        ...

如果你正在升级的的网站并且希望激活这个中间件,并无需用户重新登录的话,你应该先升级到django1.7并且,运行一段时间,那样的话,会话自然就重新创建了,就像用户登录了一样,会话中存有上面讲的哈希值.一旦你运行你的网站,使用了SessionAuthenticationMiddleware,任何未登录用户或者更新了会话的用户,现有会话失效,需要重新登录.
注意:
因为get_session_auth_hash()使用了SECRET_KEY,更新网站时使用了新的secret会使现有所有会话失效.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值