【Django】认证系统

目录

  • #. auth模块
    • 1. 认证 authenticate()
    • 2. 登陆 login(HttpRequest, user)
    • 3. 注销 logout(request)
    • 4. 认证判断 is_authenticated()
    • 5. 登陆校验 login_requierd()
    • 6. 创建普通用户 create_user()
    • 7. 创建超级用户 create_superuser()
    • 8. 密码校验 check_password(password)
    • 9. 修改密码 set_password(new_password)
  • User对象的属性
  • 扩展默认的auth_user表
  • auth实现

@
我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。

Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。

补充:Django内置的生成当前时间的方法

from django.utils.timezone import now
now_time = now()  # 生成当前时间
# 时间格式示例:2018-11-22 05:17:09.538525+00:00
# 它生成的时间格式能够与数据库中的auto_now/auto_now_add生成的时间做计算.


#. auth模块

# 导入auth模块
from django.contrib import auth

1. 认证 authenticate()

提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数。

如果认证成功(用户名和密码正确有效),便会返回一个 User 对象。

authenticate()会在该 User 对象上设置一个属性来标识后端已经认证了该用户,且该信息在后续的登录过程中是需要的。

用法:

# user = auth.authenticate(request, **form_obj.cleaned_data)
user = auth.authenticate(request, username="用户名", password="密码")

2. 登陆 login(HttpRequest, user)

该函数接受一个HttpRequest对象,以及一个经过认证的User对象。

该函数实现一个用户登录的功能。它本质上会在后端为该用户生成相关session数据。

session数据表名为:django_session,一个浏览器对应一条记录。

示例:
在这里插入图片描述
***

3. 注销 logout(request)

该函数接受一个HttpRequest对象,无返回值。

当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。

示例:
在这里插入图片描述
***

4. 认证判断 is_authenticated()

用来判断当前请求是否通过了认证(用户是否登陆)。

示例:
在这里插入图片描述
***

5. 登陆校验 login_requierd()

auth 给我们提供的一个装饰器工具,用来快捷的给某个视图添加登录校验。

若用户没有登录,则会跳转到django默认的登录URL '/accounts/login/ ' ,并传递当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。

==如果需要自定义登录的URL,则需要在settings.py文件中通过LOGIN_URL进行修改,如下:==

# 修改默认的登陆URL为'/login/':
LOGIN_URL = '/login/'

用法:

# 用于验证是否登陆的装饰器
from django.contrib.auth.decorators import login_required  @login_required
def home(request):pass

6. 创建普通用户 create_user()

auth 提供的一个创建新用户的方法,需要提供必要参数(username、password)等。

示例:
在这里插入图片描述
***

7. 创建超级用户 create_superuser()

auth 提供的一个创建新的超级用户的方法,需要提供必要参数(email, username, password)。

示例:
在这里插入图片描述
***

8. 密码校验 check_password(password)

auth 提供的一个检查密码是否正确的方法,需要提供当前请求用户的密码。

密码正确返回True,否则返回False。

示例:
在这里插入图片描述
***

9. 修改密码 set_password(new_password)

auth 提供的一个修改密码的方法,接收 要设置的新密码 作为参数。

==注意:设置完一定要调用用户对象的save方法!!!==

示例:
在这里插入图片描述

User对象的属性

重要属性:

  • username:用户名
  • password:密文密码
  • is_staff:用户是否拥有网站的管理权限(是否可登陆admin后台).
  • is_active:是否允许用户登陆,设置为False后,可以在不删除用户的前提下禁止用户登陆.
    对禁用的用户调用auth.authenticate()方法将返回None

==request.user.xx :返回xx字段的值(xx可以为auth_user表中的所有字段名).
request.user :默认返回username字段的值.==

扩展默认的auth_user表

这内置的认证系统这么好用,但是auth_user表字段都是固定的那几个,我在项目中没法拿来直接使用啊!

比如,我想要加一个存储用户手机号的字段,怎么办?

聪明的你可能会想到新建另外一张表然后通过一对一和内置的auth_user表关联,这样虽然能满足要求但是有没有更好的实现方式呢?

答案是当然有了。

我们可以通过继承内置的 AbstractUser 类,来定义一个自己的Model类。

这样既能根据项目需求灵活的设计用户表,又能使用Django强大的认证系统了。

from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):"""用户信息表"""nid = models.AutoField(primary_key=True)phone = models.CharField(max_length=11, null=True, unique=True)def __str__(self):return self.username

注意:
按上面的方式扩展了内置的auth_user表之后,一定要在settings.py中告诉Django,我现在使用我新定义的UserInfo表来做用户认证。写法如下:

# 引用Django自带的User表,继承使用时需要设置
AUTH_USER_MODEL = app名.UserInfo'

再次注意:
==一旦我们指定了新的认证系统所使用的表,我们就需要重新在数据库中创建该表,而不能继续使用原来默认的auth_user表了。==

auth实现

实现功能:注册用户、登陆、注销、修改密码、登陆校验

==settings.py文件增加配置项:==

# 这里配置项目登陆页面的路由
LOGIN_URL = '/login/'

==urls.py文件:==

from django.conf.urls import url
from django.contrib import admin
from blog import viewsurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^register/$', views.register),url(r'^login/$', views.login),url(r'^index01/$', views.index01),url(r'^index02/$', views.index02),url(r'^logout/$', views.logout),url(r'^change_password/$', views.change_password),url(r'^ajax_register/$', views.ajax_register),
]

==forms.py文件:==

from django import forms
from django.forms import Form
from django.forms import widgets
from django.core.exceptions import ValidationError  # 用于抛出错误信息# 用户登陆验证
class LoginForm(Form):username = forms.CharField(label="用户名",min_length=2,max_length=6,error_messages={'required': "用户名不能为空",'invalid': "用户名格式错误",'min_length': "用户名最短2位",},  # 自定义错误提示)password = forms.CharField(label="密码",min_length=6,max_length=12,error_messages={'required': "密码不能为空",'invalid': "密码格式错误",'min_length': "密码最短6位",},widget=widgets.PasswordInput(),  # 指定input框的type类型password)# 注册验证
class RegForm(Form):username = forms.CharField(label="用户名",min_length=2,max_length=6,error_messages={'required': "用户名不能为空",'invalid': "用户名格式错误",'min_length': "用户名最短2位",},  # 自定义错误提示)password = forms.CharField(label="密码",min_length=6,max_length=12,error_messages={'required': "密码不能为空",'invalid': "密码格式错误",'min_length': "密码最短6位",},widget=widgets.PasswordInput(),)re_password = forms.CharField(label="确认密码",min_length=6,max_length=12,error_messages={'required': "验证密码不能为空",'invalid': "密码格式错误",'min_length': "密码最短6位",},widget=widgets.PasswordInput(),)def clean_re_password(self, *args, **kwargs):password = self.cleaned_data.get('password')re_password = self.cleaned_data.get('re_password')if password == re_password:return passwordraise ValidationError("密码不一致")# 单选按钮level = forms.fields.ChoiceField(label="用户级别",choices=((0, "普通用户"), (1, "超级用户"),),initial=0,  # 默认选择普通用户widget=forms.widgets.RadioSelect(),)# 修改密码
class ChangePwd(Form):old_password = forms.CharField(label="旧密码",min_length=6,max_length=12,error_messages={'required': "旧密码不能为空",'invalid': "密码格式错误",'min_length': "密码最短6位",},widget=widgets.PasswordInput(),)password = forms.CharField(label="新密码",min_length=6,max_length=12,error_messages={'required': "新密码不能为空",'invalid': "密码格式错误",'min_length': "密码最短6位",},widget=widgets.PasswordInput(),)re_password = forms.CharField(label="确认新密码",min_length=6,max_length=12,error_messages={'required': "验证密码不能为空",'invalid': "密码格式错误",'min_length': "密码最短6位",},widget=widgets.PasswordInput(),)def clean_re_password(self, *args, **kwargs):password = self.cleaned_data.get('password')re_password = self.cleaned_data.get('re_password')if password == re_password:return passwordraise ValidationError("密码不一致")

==views.py文件:==

from django.shortcuts import render, redirect, HttpResponse
from blog.forms import *  # 导入自定义的Form组件
from django.contrib import auth  # 导入认证模块
from django.contrib.auth.models import User  # 导入User表
from django.contrib.auth.decorators import login_required  # 用于验证是否登陆的装饰器
from blog072 import settings# 注册
def register(request):form_obj = RegForm()if request.method == 'POST':form_obj = RegForm(request.POST)if form_obj.is_valid():form_obj.cleaned_data.pop('re_password')level = form_obj.cleaned_data.pop('level')# 判断要注册的用户类型if not int(level):# 使用create()方法创建的用户为明文密码,无法登陆(不可使用此方法)# User.objects.create(**form_obj.cleaned_data)# create_user():创建普通用户User.objects.create_user(is_staff=1, **form_obj.cleaned_data)# is_staff=1:允许登陆后台(默认不允许)else:# create_superuser():创建超级用户User.objects.create_superuser(email='', **form_obj.cleaned_data)return redirect('/login/')return render(request, 'register.html', {'form': form_obj})# 登陆
def login(request):form_obj = LoginForm()if request.method == 'POST':form_obj = LoginForm(request.POST)if form_obj.is_valid():# 只有执行了is_valid之后,才可执行cleaned_data方法,且cleaned_data内的数据是经过校验的user = auth.authenticate(request, **form_obj.cleaned_data)# 如果校验成功,user是当前登陆用户对象,否则为None(用户被禁用[is_active=0]后,也会返回None)if user:# 生成session数据(表名:django_session  一个浏览器对应一条session数据)auth.login(request, user)# 如果是跳转过来的,则登陆成功后返回至原页面:next = request.GET.get('next')ret = next if next else '/index01/'return redirect(ret)print(form_obj.cleaned_data)return HttpResponse("用户名或密码错误")return render(request, 'login.html', {'form': form_obj})@login_required
def index01(request):print("用户%s进入index01页面" % request.user)# request.user.xx  :xx可以为auth_user表中的所有字段名# request.user 默认返回username字段return render(request, 'index01.html')def index02(request):if not request.user.is_authenticated():# is_authenticated():判断当前请求是否通过了认证ret = '%s?next=%s' % (settings.LOGIN_URL, request.path)return redirect(ret)print("用户%s进入index02页面" % request.user)return render(request, 'index02.html')# 注销
@login_required
def logout(request):# 注销,清除session数据auth.logout(request)return redirect('/login/')# 修改密码
@login_required
def change_password(request):form_obj = ChangePwd()if request.method == 'POST':form_obj = ChangePwd(request.POST)if form_obj.is_valid():old_password = form_obj.cleaned_data.get('old_password')password = form_obj.cleaned_data.get('password')# 判断旧密码是否正确if request.user.check_password(old_password):# 修改密码request.user.set_password(password)request.user.save()  # 同步到数据库return redirect('/login/')return HttpResponse("旧密码错误!")return render(request, 'change_password.html', {'form': form_obj})# ajax判断用户名是否存在
def ajax_register(request):username = request.GET.get('username')print(username)print(User.objects.filter(username=username).exists())if User.objects.filter(username=username).exists():return HttpResponse("0")return HttpResponse()

==HTML文件:==
==注册页面(register.html):==

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>我是注册页面</title>
</head>
<body>
<form action="" method="post" novalidate>{% csrf_token %}<p id="sign01">{{ form.username.label }}{{ form.username }}<span style="color: red">{{ form.username.errors.0 }}</span><span id="sign02" style="color: red"></span></p><p>{{ form.password.label }}{{ form.password }}<span style="color: red">{{ form.password.errors.0 }}</span></p><p>{{ form.re_password.label }}{{ form.re_password }}<span style="color: red">{{ form.re_password.errors.0 }}</span></p><p>{{ form.level.label }}{{ form.level }}</p><p><button>注册</button><span style="font-size: 50%;">注册成功将返回登陆页面</span></p>
</form>
{% load static %}
<script src="{% static 'jquery-3.3.1.js' %}"></script>
<script>var $UserName = $('#sign01 input');var $Button = $('button');$UserName.on('input', function () {$.ajax({url: '/ajax_register/',type: 'GET',data: {username: $UserName.val(),csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),},success: function (data) {if (data === '0') {$Button.attr('disabled', 'disabled');$('#sign02').text("用户名已存在");} else {$Button.removeAttr('disabled', 'disabled');$('#sign02').text('');}},});});
</script>
</body>
</html>

==登陆页面(login.html):==

{# bootstrap版登陆页面 #}
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1">{# 需要下载bootstrap样式文件 #}<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>我是登陆页面</title>
</head>
<body>
<form class="form-horizontal" action="" method="post" novalidate>{% csrf_token %}<div class="form-group"><label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">用户名</label><div class="col-sm-10">{{ form.username }}{# has-error:Form组件要想展示红色错误信息,必须要加这个类 #}<div class="has-error"><span class="help-block">{{ form.username.errors.0 }}</span></div></div></div><div class="form-group"><label for="{{ form.password.id_for_label }}" class="col-sm-2 control-label">密码</label><div class="col-sm-10 has-error">{{ form.password }}<div class="has-error"><span class="help-block">{{ form.password.errors.0 }}</span></div></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="submit" class="btn btn-default">登陆</button><a href="/register/" class="btn btn-sm">注册用户</a></div></div>
</form>
</body>
</html>

==index01.py页面:==

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>index01</title>
</head>
<body>
<h1>我是 Index01 页面</h1>
<a href="/logout/">注销</a>
<a href="/change_password/">修改密码</a>
</body>
</html>

==index02.py页面:==

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>index02</title>
</head>
<body>
<h1>我是 Index02 页面</h1>
<a href="/logout/">注销</a>
<a href="/change_password/">修改密码</a>
</body>
</html>

==修改密码页面(change_password.html):==

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta http-equiv="content-Type" charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"><title>我是修改密码页面</title>
</head>
<body>
<form action="" method="post" novalidate>{% csrf_token %}<p>{{ form.old_password.label }}{{ form.old_password }}<span style="color: red">{{ form.odl_password.errors.0 }}</span></p><p>{{ form.password.label }}{{ form.password }}<span style="color: red">{{ form.password.errors.0 }}</span></p><p>{{ form.re_password.label }}{{ form.re_password }}<span style="color: red">{{ form.re_password.errors.0 }}</span></p><p><button>提交</button><span style="font-size: 50%;">修改成功后将返回登陆页面</span></p>
</form>
</body>
</html>

转载于:https://www.cnblogs.com/zyk01/p/10176340.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/249957.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

尝试修改源码需要用到git存一下

git reflog查看本地记录 git reset --hard 02a3260 转载于:https://www.cnblogs.com/smzd/p/8492065.html

poj3713 Transferring Sylla 枚举+tarjan判割点

其实就是判断是否为三连通图 三连通图指的是去掉3个点就不连通的图&#xff0c;但是并没有直接求三连通的算法。著名的Tarjan算法可以求解连通和割点&#xff0c;再枚举删除一个点就能达到三连通的目的。 先看用例2&#xff0c;是由用例1去掉一条边而变成非三连通图的&#xff…

Linux 安装Zookeeper单机版(使用Mac远程访问)

阅读本文需要先阅读安装Zookeeper<准备> 新建目录 mkdir /usr/local/zookeeper 解压 cd zookeeper压缩包所在目录 tar -xvf zookeeper-3.4.12.tar.gz -C /usr/local/zookeeper 新建目录 mkdir /usr/local/zookeeper/zookeeper-3.4.12/data 配置文件准备 cp /usr/local/zo…

深入vue

转载于:https://www.cnblogs.com/smzd/p/8547748.html

html--form表单常用操作

form表单 用于收集用户信息&#xff0c;如&#xff1a;登录、注册等场景&#xff1b;所有要提交的数据都必须放在form标签中<form action" " method" "> action&#xff1a;提交地址、动作&#xff0c;与input标签中type标签的submit属性相关联。 &…

MySQL触发器(转载)

触发器&#xff08;trigger&#xff09;是数据库中的一个很重要的、很实用的基于事件的处理器&#xff0c;在处理一些业务需求的时候&#xff0c;使用触发器会很方便。似乎在《高性能MySQL》中&#xff0c;对触发器作了一定的描述&#xff0c;也提到使用中的一些优势和局限性&a…

神级bug解决方法

真的是神级bug,util包中的Arrays类导入不了&#xff0c;一直报错。原因&#xff1a;JDK 1.8和Myeclipse 8.5不兼容&#xff0c;导致java.util.Arrays类无法被编译。所以报错。解决方法&#xff1a;1.降低jdk版本。2.升高Myeclipse版本转载于:https://www.cnblogs.com/yanlongw/…

es6注意点

补救方法&#xff1a; 详情&#xff1a;http://es6.ruanyifeng.com/#docs/array 取出文本内容 实现深拷贝 jq实现不完全深拷贝 jQuery.extend jQuery.fn.extend function () {var options, name, src, copy, copyIsArray, clone,target arguments[0] || {},i 1,length ar…

input标签用法解读

HTML5/HTML中标签用法解读 OK&#xff01;今天博主为小伙伴们介绍的内容是HTML5/HTML中标签的用法&#xff0c;&#xff0c;&#xff0c; &#xff0c;emmm图文并茂哦&#xff01; 下面正式开始内容的介绍&#xff1a;首先&#xff0c;直观上说标签规定了用户可以在其中输入数据…

软件开发文档整理(之)一张示意图 | 清晰明了

在整个软件开发周期&#xff0c;开发文档是必不可少的资料&#xff0c;它们贯穿于整个开发周期&#xff0c;用来评估计划、规划进度、项目管理、软件测试、软件发布&#xff0c;可以说至关重要。   开发文档必须归档&#xff0c;没有归档的文档作用大打折扣&#xff0c;时效性…

《SQL Server 2008从入门到精通》--20180717

目录 1.触发器1.1.DDL触发器1.2.DML触发器1.3.创建触发器1.3.1.创建DML触发器1.3.2.创建DDL触发器1.3.3.嵌套触发器1.3.4.递归触发器1.4.管理触发器1.触发器 触发器是一种特殊的存储过程&#xff0c;与表紧密关联。 1.1.DDL触发器 当服务器或数据库中发生数据定义语言&#xff…

主成分分析原理解释(能力工场小马哥)

主成分分析&#xff08;Principal components analysis&#xff09;-最大方差解释 在这一篇之前的内容是《Factor Analysis》&#xff0c;由于非常理论&#xff0c;打算学完整个课程后再写。在写这篇之前&#xff0c;我阅读了PCA、SVD和LDA。这几个模型相近&#xff0c;却都有自…

vue全面介绍--全家桶、项目实例

简介 “简单却不失优雅&#xff0c;小巧而不乏大匠”。 2016年最火的前端框架当属Vue.js了&#xff0c;很多使用过vue的程序员这样评价它&#xff0c;“vue.js兼具angular.js和react.js的优点&#xff0c;并剔除了它们的缺点”。授予了这么高的评价的vue.js&#xff0c;也是开…

吴恩达机器学习笔记 —— 9 神经网络学习

本章讲述了神经网络的起源与神经元模型&#xff0c;并且描述了前馈型神经网络的构造。 更多内容参考 机器学习&深度学习 在传统的线性回归或者逻辑回归中&#xff0c;如果特征很多&#xff0c;想要手动组合很多有效的特征是不现实的&#xff1b;而且处理这么大的特征数据量…

vue.js开发环境搭建

环境准备 Node.js Javascript的运行时环境npm Node.js下的包管理工具webpack 前端资源模块化管理和打包工具vue-cli 脚手架构建工具cnpm npm的淘宝镜像 Vue.js安装 Node.js的安装非常容易&#xff0c;首先从官网下载你所需操作系统的版本&#xff0c;然后一直下一步就ok&…

正则基础知识

创建正则表达式 1.使用new来创建 var exp new RegExp(box , gi );g 全局匹配 i 忽略大小写 m 多行匹配2.使用字面量 var exp /box/gi; 直接用2个 / ; 在俩个斜杠后加上模式修饰符&#xff1b; 俩种创建方式比较: 1.使用字面量方式创建用的更加广泛; 2.当要匹配的内容是变量时,…

Promise实践

var doSomething function() { return new Promise((resolve, reject) > { resolve(返回值); }); };let doSomethingElse function() { return 新的值; }doSomething().then(function () { return doSomethingElse(); }).then(resp > { console.warn(resp); console.wa…

JsonBuilder初出茅庐

互联网这股东风不久前刮到了甘凉国&#xff0c;国王老甘独具慧眼&#xff0c;想赶紧趁着东风未停大力发展移动互联网&#xff0c;因为他笃信布斯雷的理论&#xff1a;“站在风口上&#xff0c;猪都能飞起来”。无奈地方偏僻落后&#xff0c;国内无可用之才啊。老甘一筹莫展的低…

Nexus介绍

转自&#xff1a;https://www.cnblogs.com/wincai/p/5599282.html 开始在使用Maven时&#xff0c;总是会听到nexus这个词&#xff0c;一会儿maven&#xff0c;一会儿nexus&#xff0c;当时很是困惑&#xff0c;nexus是什么呢&#xff0c;为什么它总是和maven一起被提到呢&#…

vue-cli 打包

一项目打包 1 打包的配置在 build/webpack.base.conf.js文件下 常量config在vue/config/index.js 文件下配置&#xff0c;__dirname是定义在项目的全局变量&#xff0c;是当前文件所在项目的文件夹的绝对路径。 2 需要修改vue/config/index.js 文件下的将build对象下的assets…