Django用户认证系统 Web请求中的认证解析


Posted in Python onAugust 02, 2019

在每个Web请求中都提供一个 request.user 属性来表示当前用户。如果当前用户未登录,则该属性为AnonymousUser的一个实例,反之,则是一个User实例。

你可以通过is_authenticated()来区分,例如:

if request.user.is_authenticated():
  # Do something for authenticated users.
else:
  # Do something for anonymous users.

登陆login

login()

登陆函数,需要一个HttpRequest对象和一个User对象作参数。login()使用django的session框架,将User的id存储在session中。

同时使用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)
      # Redirect to a success page.
    else:
      # Return a 'disabled account' error message
  else:
    # Return an 'invalid login' error message.

如果不知道密码,login方法:

user=MyUser.objects.get(...)
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request,user)

退出登录logout

logout()

使用HttpRequest对象为参数,无返回值。例如:

from django.contrib.auth import logout
def logout_view(request):
  logout(request)
  # Redirect to a success page.

限制访问

The raw way

使用 request.user.is_authenticated()

再定向:

from django.shortcuts import redirect 
def my_view(request):
  if not request.user.is_authenticated():
    return redirect('/login/?next=%s' % request.path)
  # ...

或者:

from django.shortcuts import render
def my_view(request):
  if not request.user.is_authenticated():
    return render(request, 'myapp/login_error.html')
  # ...

使用装饰器login_required

login_required([redirect_field_name=REDIRECT_FIELD_NAME, login_url=None]

from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
  ...

如果用户未登录,则重定向到 settings.LOGIN_URL,并将现在的url相对路径构成一个next做key的查询字符对附加到 settings.LOGIN_URL后面去:

/accounts/login/?next=/polls/3/.

query字符对的key默认为next,也可以自己命名:

from django.contrib.auth.decorators import login_required
@login_required(redirect_field_name='my_redirect_field')
def my_view(request):
  ...

也可以自己定义login_url:

from django.contrib.auth.decorators import login_required
@login_required(login_url='/accounts/login/')
def my_view(request):
  ...

urls.py中需定义:

(r'^accounts/login/$', 'django.contrib.auth.views.login'),

测试登录用户

例如,要检测用户的email:

def my_view(request):
  if not '@example.com' in request.user.email:
    return HttpResponse("You can't vote in this poll.")
  # ...

可以用装饰器:

from django.contrib.auth.decorators import user_passes_test
def email_check(user):
  return '@example.com' in user.email
 
@user_passes_test(email_check)
def my_view(request):
  ...

也可以改变login_url:

@user_passes_test(email_check, login_url='/login/')
def my_view(request):
  ...

认证Views

当然,我们可以自己定义一些登陆,登出,密码管理的view 函数,而且也更加方便。

不过也可以学习下Django内置的views。

Django没有为认证views提供缺省模板,however the template context is documented for each view below.

所有内置views都返回一个TemplateResponse实例,可以让你很方便的定制response数据。

https://github.com/django/django/blob/master/django/contrib/auth/views.py

多数的内置认证views都提供一个URL名称以便使用。

login(request[, template_name, redirect_field_name, authentication_form,current_app,extra_context])

源码:

def login(request, template_name='registration/login.html',
     redirect_field_name=REDIRECT_FIELD_NAME,
     authentication_form=AuthenticationForm,
     current_app=None, extra_context=None):
  """
  Displays the login form and handles the login action.
  """
  redirect_to = request.POST.get(redirect_field_name,
                  request.GET.get(redirect_field_name, ''))
 
  if request.method == "POST":
    form = authentication_form(request, data=request.POST)
    if form.is_valid():
 
      # Ensure the user-originating redirection url is safe.
      if not is_safe_url(url=redirect_to, host=request.get_host()):
        redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
 
      # Okay, security check complete. Log the user in.
      auth_login(request, form.get_user())
 
      return HttpResponseRedirect(redirect_to)
  else:
    form = authentication_form(request)
 
  current_site = get_current_site(request)
 
  context = {
    'form': form,
    redirect_field_name: redirect_to,
    'site': current_site,
    'site_name': current_site.name,
  }
  if extra_context is not None:
    context.update(extra_context)
  return TemplateResponse(request, template_name, context,
              current_app=current_app)

URL name: login

参数:

template_name: 默认的登陆模板.默认为registration/login.html.

redirect_field_name: 重定向的name,默认为next.

authentication_form: 默认Form. Defaults to AuthenticationForm.

current_app: A hint indicating which application contains the current view. See the namespaced URL resolution strategy for more information.

extra_context: 添加到默认context data中的额外数据,为字典。

django.contrib.auth.views.login does:

如果通过GET访问, 将显示登录表单,可以将其内容POST到相同的URL上。

如果通过POST访问,它首先会尝试登录,如果成功,view就重定向到next指定的的链接。如果next 未设置,则重定向到settings.LOGIN_REDIRECT_URL(一般缺省值为accounts/profile/)。如果登录失败,则再次显示登录表单。

需要用户自己来提供login的html模板,缺省是registration/login.html 。这个模板将传递4个模板上下文变量:

form: 一个表单对象AuthenticationForm.
next: 登录成功后的重定向链接,可以包含一个query string中。
site: 当前网站,根据 SITE_ID 设置。如果你并没有安装site框架,这个变量将设定为一个 RequestSite实例,它从当前 HttpRequest中取得站点名和域名。
site_name: 是 site.name的一个别名。如果你没有安装site框架,它将会被设为 request.META['SERVER_NAME']的值。

如果你不想调用registration/login.html模板,你可以在URLconf中设定特定的view参数来传递template_name参数。

(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),

你也可以自己指定重定向链接字段名,通过redirect_field_name 参数。默认的字段名为next.

下面是registration/login.html 模板的原始状态,它假定你有一个base.html模板(其中有content block的定义。

{% extends "base.html" %}
 
{% block content %}
 
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
 
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<table>
<tr>
  <td>{{ form.username.label_tag }}</td>
  <td>{{ form.username }}</td>
</tr>
<tr>
  <td>{{ form.password.label_tag }}</td>
  <td>{{ form.password }}</td>
</tr>
</table>
 
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}

如果自己定制认证系统,可以通过 authentication_form 参数把自定义的认证表单传给login view。该表单的__init__ 方法应该有一个request 的参数,并提供一个 get_user 方法来返回认证后的User对象。

logout(request[, next_page, template_name, redirect_field_name, current_app,extra_context])

登出用户.

URL name: logout

可选参数:

next_page: 注销后的重定向链接.

logout_then_login(request[, login_url, current_app, extra_context])

注销用户然后重定向到登录链接.

可选参数:

login_url: 登陆页面的重定向链接,缺省值为 settings.LOGIN_URL。

password_change(request[, template_name, post_change_redirect,password_change_form,current_app, extra_context])

允许用户修改密码.

URL name: password_change

可选参数 Optional arguments:

template_name: 模板名,缺省值为registration/password_change_form.html 。

post_change_redirect: 重定向链接。

password_change_form: 自定义的密码修改表单,包括一个user 参数。缺省值为PasswordChangeForm.

password_change_done(request[, template_name,current_app, extra_context])

用户修改密码后的页面.

URL name: password_change_done

可选参数 Optional arguments:

template_name: 模板名称,缺省为registration/password_change_done.html.

password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect, from_email, current_app, extra_context, html_email_template_name])

向用户发送邮件,内含一个一次性链接,来让用户重设密码。

如果提供的邮箱不存在,则不会发送。

URL name: password_reset

可选参数 Optional arguments:

template_name: 模板名称,缺省值为registration/password_reset_form.html。

email_template_name: 用来生成带充值链接email的模板名称,缺省值为registration/password_reset_email.html。

subject_template_name: 用来生成邮件主题的模板名称,缺省值为 registration/password_reset_subject.txt。

password_reset_form: 重设密码的表单,缺省值为 PasswordResetForm.

token_generator: 检查一次性链接的类实例,缺省值为default_token_generator, 其类为django.contrib.auth.tokens.PasswordResetTokenGenerator.

post_reset_redirect: 密码重置后的重定向链接.

from_email: 邮件地址,缺省值为DEFAULT_FROM_EMAIL.

current_app: A hint indicating which application contains the current view. See the namespaced URL resolution strategy for more information.

extra_context: A dictionary of context data that will be added to the default context data passed to the template.

html_email_template_name: The full name of a template to use for generating a text/html multipart email with the password reset link. By default, HTML email is not sent.

示例: registration/password_reset_email.html (email内容模板):

Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

password_reset_done(request[, template_name])

显示用户选择发送密码重置邮件后的页面。如果 password_reset() view中没有显式指定post_reset_redirect链接时,则直接调用本view。

password_reset_confirm(request[, uidb36, token, template_name, token_generator,set_password_form, post_reset_redirect,current_app, extra_context])

显示输入新密码的表单.

password_reset_complete(request[, template_name, current_app, extra_context])

重置密码成功后的表单。

Helper functions
redirect_to_login(next[, login_url, redirect_field_name])

重定向到登录页面,登录成功后的在重定向到另一链接。.

参数:

next: 登录成功后的链接.

login_url: 登陆页面链接,默认缺省值:settings.LOGIN_URL。

redirect_field_name: 重定向字段名称,缺省值为next。

内置表单 Built-in forms

class AdminPasswordChangeForm

admin后台的用户密码修改表单

class AuthenticationForm

登录表单。

方法confirm_login_allowed(user)

例如,允许所有的users登陆,不管is_active属性:

from django.contrib.auth.forms import AuthenticationForm
 
class AuthenticationFormWithInactiveUsersOkay(AuthenticationForm):
  def confirm_login_allowed(self, user):
    pass

或者只允许活跃用户登陆:

class PickyAuthenticationForm(AuthenticationForm):
  def confirm_login_allowed(self, user):
    if not user.is_active:
      raise forms.ValidationError(
        _("This account is inactive."),
        code='inactive',
      )
    if user.username.startswith('b'):
      raise forms.ValidationError(
        _("Sorry, accounts starting with 'b' aren't welcome here."),
        code='no_b_users',
      )

class PasswordChangeForm

修改密码表单.

class PasswordResetForm

密码重置表单.

class SetPasswordForm

密码设置表单.

class UserChangeForm

admin后台的用户信息和权限修改表单.

class UserCreationForm

用户创建表单.

模板中的认证信息 Authentication data in templates

当前登录用户及其权限可以在模板变量中取得,通过使用RequestContext.

Users

在渲染模板 RequestContext时,当前的登录用户无论是User实例还是AnonymousUser 实例均保存在模板变量 {{ user }}:

{% if user.is_authenticated %}
  <p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
  <p>Welcome, new user. Please log in.</p>
{% endif %}

如果未使用 RequestContext,则此变量不存在。

使用RequestContext:

from django.shortcuts import render_to_response
from django.template import RequestContext
 
def some_view(request):
  # ...
  return render_to_response('my_template.html',
               my_data_dictionary,
               context_instance=RequestContext(request))

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python处理文本文件并生成指定格式的文件
Jul 31 Python
Python迭代用法实例教程
Sep 08 Python
python 多线程实现检测服务器在线情况
Nov 25 Python
python 实现网上商城,转账,存取款等功能的信用卡系统
Jul 15 Python
Python编程之列表操作实例详解【创建、使用、更新、删除】
Jul 22 Python
Tornado高并发处理方法实例代码
Jan 15 Python
numpy给array增加维度np.newaxis的实例
Nov 01 Python
在mac下查找python包存放路径site-packages的实现方法
Nov 06 Python
Django REST Framework之频率限制的使用
Sep 29 Python
Python: 传递列表副本方式
Dec 19 Python
python实现门限回归方式
Feb 29 Python
Pytorch如何切换 cpu和gpu的使用详解
Mar 01 Python
Django用户认证系统 User对象解析
Aug 02 #Python
浅谈python3中input输入的使用
Aug 02 #Python
Pycharm连接远程服务器并实现远程调试的实现
Aug 02 #Python
在PyCharm的 Terminal(终端)切换Python版本的方法
Aug 02 #Python
Django单元测试工具test client使用详解
Aug 02 #Python
Django使用unittest模块进行单元测试过程解析
Aug 02 #Python
pip安装python库的方法总结
Aug 02 #Python
You might like
编写PHP脚本过滤用户上传的图片
2015/07/03 PHP
PHP设计模式之原型设计模式原理与用法分析
2018/04/25 PHP
分析php://output和php://stdout的区别
2018/05/06 PHP
JavaScript脚本性能的优化方法
2007/02/02 Javascript
基于jquery的jqDnR拖拽溢出的修改
2011/02/12 Javascript
Ajax异步提交表单数据的说明及方法实例
2013/06/22 Javascript
高效率JavaScript编写技巧整理
2013/08/23 Javascript
javascript去除字符串中所有标点符号和提取纯文本的正则
2014/06/07 Javascript
javascript动态修改Li节点值的方法
2015/01/20 Javascript
基于javascript实现全屏漂浮广告
2016/03/31 Javascript
原生js制作日历控件实例分享
2016/04/06 Javascript
微信小程序实现打开内置地图功能【附源码下载】
2017/12/07 Javascript
详解webpack-dev-server使用方法
2018/09/14 Javascript
vue-cli整合vuex的时候,修改actions和mutations,实现热部署的方法
2018/09/19 Javascript
原生JavaScript实现滑动拖动验证的示例代码
2019/12/06 Javascript
在实例中重学JavaScript事件循环
2020/12/03 Javascript
Python实现获取操作系统版本信息方法
2015/04/08 Python
详解Python中的Cookie模块使用
2015/07/06 Python
详解Python的Django框架中的模版相关知识
2015/07/15 Python
Python3 Random模块代码详解
2017/12/04 Python
使用Filter过滤python中的日志输出的实现方法
2019/07/17 Python
Python3安装pip工具的详细步骤
2019/10/14 Python
python判断链表是否有环的实例代码
2020/01/31 Python
Python使用enumerate获取迭代元素下标
2020/02/03 Python
Python3 Click模块的使用方法详解
2020/02/12 Python
opencv python 图片读取与显示图片窗口未响应问题的解决
2020/04/24 Python
在Sublime Editor中配置Python环境的详细教程
2020/05/03 Python
html5版canvas自由拼图实例
2014/10/15 HTML / CSS
HTML5单页面手势滑屏切换原理
2016/03/21 HTML / CSS
Html5写一个简单的俄罗斯方块小游戏
2019/12/03 HTML / CSS
超市营业员求职简历的自我评价
2013/10/17 职场文书
高中毕业生的个人自我评价
2014/02/21 职场文书
大学生国家助学金感谢信
2015/01/23 职场文书
Mysql中有关Datetime和Timestamp的使用总结
2021/12/06 MySQL
Python图像处理库PIL详细使用说明
2022/04/06 Python
Spring Security动态权限的实现方法详解
2022/06/16 Java/Android