form组件注册ajax登录auth认证及验证码
本项目采用django自带的数据库
项目文件
models.py
1 from django.db import models 2 from django.contrib.auth.models import AbstractUser 3 # Create your models here. 4 5 class Userinfo(AbstractUser): 6 email = models.EmailField()
settings.py
1 AUTH_USER_MODEL='app01.Userinfo'#自定义认证表 2 LOGIN_URL='/login/'#指定认证失败的跳转页面 3 4 STATIC_URL = '/static/' 5 STATICFILES_DIRS=[ 6 os.path.join(BASE_DIR,'static_files') 7 ]
views.py
1 from django.shortcuts import render, HttpResponse, redirect 2 from django import forms#django的form组件 3 from django.core.validators import RegexValidator#form组件中的validators自带校验器 4 from django.core.exceptions import ValidationError#错误 5 import re 6 from app01 import models 7 from django.http import JsonResponse#Json响应数据类型 8 from django.urls import reverse#url反向解析 9 from django.contrib import auth#django内置认证系统 10 from django.contrib.auth.decorators import login_required#认证装饰器 11 12 13 # Create your views here. 14 #自定义校验函数 15 def name_valid(value): 16 name_re = re.compile(r'^[\d]+') 17 ret = name_re.match(value) 18 if ret: 19 raise ValidationError('用户名不能以数字开头!') 20 21 #注册form组件 22 class RegisterForm(forms.Form): 23 name = forms.CharField( 24 required=True, 25 label='用户名:', 26 min_length=6, 27 max_length=32, 28 help_text='只能有字母数字下划线组成,且不能以数字开头,长度6到32位!', 29 # initial='admin123_', 30 error_messages={ 31 'required': '用户名不能为空!', 32 'min_length': '长度不能少于6位!', 33 'max_length': '长度不能超过32位!', 34 }, 35 validators=[RegexValidator(r'^[a-zA-Z0-9_]+$', '用户名只能包含字母数字下划线!'), name_valid], 36 ) 37 password = forms.CharField( 38 required=True, 39 label='密码:', 40 min_length=6, 41 max_length=32, 42 help_text='长度6到32位!', 43 initial='', 44 error_messages={ 45 'required': '密码不能为空!', 46 'min_length': '长度不能少于6位!', 47 'max_length': '长度不能超过32位!', 48 }, 49 widget=forms.PasswordInput(render_value=True), 50 ) 51 r_password = forms.CharField( 52 required=True, 53 label='确认密码:', 54 min_length=6, 55 max_length=32, 56 help_text='长度6到32位!', 57 initial='', 58 error_messages={ 59 'required': '密码不能为空!', 60 'min_length': '长度不能少于6位!', 61 'max_length': '长度不能超过32位!', 62 }, 63 widget=forms.PasswordInput(render_value=True), 64 ) 65 email = forms.EmailField( 66 required=True, 67 label='邮箱', 68 error_messages={ 69 'required': '邮箱不能为空!', 70 'invalid':'邮箱格式不正确!' 71 } 72 ) 73 #全部字段添加样式 74 def __init__(self, *args, **kwargs): 75 super(RegisterForm, self).__init__(*args, **kwargs) 76 for field in self.fields: 77 self.fields[field].widget.attrs.update({'class': 'form-control'}) 78 #局部钩子 79 def clean_name(self): 80 pass 81 return self.cleaned_data.get('name') 82 83 def clean_password(self): 84 pass 85 return self.cleaned_data.get('password') 86 87 def clean_r_password(self): 88 pass 89 return self.cleaned_data.get('r_password') 90 91 def clean_email(self): 92 pass 93 return self.cleaned_data.get('email') 94 #全局钩子 95 def clean(self): 96 password = self.cleaned_data.get('password') 97 r_password = self.cleaned_data.get('r_password') 98 if password != r_password: 99 self.add_error('r_password', '两次密码不一致!') 100 raise ValidationError('两次密码不一致!') 101 else: 102 return self.cleaned_data 103 104 105 # 注册 106 def register(request): 107 if request.method == 'GET': 108 register_obj = RegisterForm() 109 return render(request, 'register.html', {'register_obj': register_obj}) 110 elif request.method == 'POST': 111 data = request.POST 112 # print(data) 113 register_obj = RegisterForm(data) 114 if register_obj.is_valid(): 115 user_obj = register_obj.cleaned_data 116 print(user_obj) 117 username = user_obj.get('name') 118 password = user_obj.get('password') 119 email = user_obj.get('email') 120 121 if not models.Userinfo.objects.filter(username=username).exists(): 122 new_obj = models.Userinfo.objects.create_user(username=username, password=password, email=email) 123 print(f'新用户{username}注册成功!') 124 return redirect('login') 125 else: 126 register_obj.add_error('name', '用户名已存在!') 127 return render(request, 'register.html', {'register_obj': register_obj}) 128 129 else: 130 return render(request, 'register.html', {'register_obj': register_obj}) 131 132 #登录页面的ModelForm组件(本次未使用) 133 ''' 134 # class LoginForm(forms.ModelForm): 135 # class Meta: 136 # model = models.Userinfo # 指定类 137 # # fields='__all__' 138 # # exclude=[] #排除字段 139 # fields = ['username', 'password'] # 设置的字段 140 # labels = { # 标签名 141 # 'username': '用户名:', 142 # 'password': '密码:'} 143 # error_messages = { # 错误信息 144 # 'username': {'required': '用户名不能为空!'}, 145 # 'password': {'required': '密码不能为空!'}, 146 # } 147 # widgets = { # 插件 148 # 'username': forms.TextInput({"class": "form-control"}), 149 # 'password': forms.TextInput({"class": "form-control"}), 150 # 151 # } 152 # # def clean_username(self): 153 # # pass 154 # # return self.cleaned_data.get('username') 155 # # def clean(self): 156 # # pass 157 # # return self.cleaned_data 158 # # def __init__(self,*args,**kwargs): 159 # # super().__init__(*args,**kwargs) 160 # # for field in self.fields: 161 # # self.fields[field].widget.attrs.update({'class':'form-control'}) 162 163 ''' 164 165 166 167 168 169 #随机验证码 170 def get_cverification_code(request): 171 import os 172 from crmtest import settings 173 import random 174 def get_random_color(): 175 ''' 176 随机颜色 177 :return: rgb颜色 178 ''' 179 return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) 180 181 from PIL import Image, ImageDraw, ImageFont#要先安装pillow模块:pip install pillow 182 img_obj = Image.new('RGB', (120, 40), get_random_color())#实例化图片对象 183 draw_obj = ImageDraw.Draw(img_obj)#创建图片 184 185 font_path = os.path.join(settings.BASE_DIR, r'static_files\fonts\BRUX.otf')#字体路径(字体自己下载) 186 print('>>>>', font_path) 187 # font_obj = ImageFont.truetype(font_path, 26)#路径拼接注意不能有中文,否则报错 188 font_obj = ImageFont.truetype(r'static_files/fonts/BRUX.otf', 26) #相对路径r'static_files/fonts/BRUX.otf' 189 # font_obj = ImageFont.load_default().font#系统默认字体 190 sum_str = '' 191 for i in range(6):#生成随机的字母数字组合 192 a = random.choice([str(random.randint(0, 9)), chr(random.randint(97, 122)), 193 chr(random.randint(65, 90))]) # 4 a 5 D 6 S 194 sum_str += a 195 print(sum_str) 196 draw_obj.text((12, 2), sum_str, fill=get_random_color(), font=font_obj) 197 198 width = 120 199 height = 40 200 # 添加噪线 201 for i in range(5):#循环一次就是一条线:两点确定一条 202 x1 = random.randint(0, width) 203 x2 = random.randint(0, width) 204 y1 = random.randint(0, height) 205 y2 = random.randint(0, height) 206 draw_obj.line((x1, y1, x2, y2), fill=get_random_color()) 207 # # 添加噪点 208 for i in range(10): 209 # 这是添加点,50个点 210 draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) 211 # 下面是添加很小的弧线,看上去类似于一个点,50个小弧线 212 x = random.randint(0, width) 213 y = random.randint(0, height) 214 draw_obj.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) 215 216 from io import BytesIO#生成的图片格式指定和存在位置(缓存) 217 f = BytesIO() 218 img_obj.save(f, 'png') 219 data = f.getvalue() 220 221 # 验证码对应的数据保存到session里面 222 request.session['valid_str'] = sum_str 223 224 return HttpResponse(data) 225 226 # 登录 227 def login(request): 228 if request.method == 'GET': 229 return render(request, 'login.html') 230 elif request.method == 'POST': 231 # print(request.POST) 232 username = request.POST.get('username') 233 password = request.POST.get('password') 234 cverification_code=request.POST.get('cverification_code') 235 236 if cverification_code.upper()==request.session.get('valid_str').upper(): 237 238 user_obj = auth.authenticate(username=username, password=password) 239 print(user_obj) 240 if user_obj: 241 auth.login(request, user_obj) 242 return JsonResponse({'status': 1, 'url': reverse('index')}) 243 else: 244 return JsonResponse({'status': 0, 'url': '账号或密码有误!'}) 245 else: 246 return JsonResponse({'status': 0, 'url': '验证码输入有误!'}) 247 248 249 #状态认证的首页访问(使用装饰器认证状态失败,会自动跳转一个路径,可以在settings中配置指定LOGIN_URL='/login/') 250 #同时在页面的请求路径会自动加上'?next=/index/'(当前页面路径), 251 # 借此可以在前端通过location.search获取后slice切边获取路径,登录成功之后在success回调函数location.href指向该路径,自动跳转访问的页面 252 @login_required 253 def index(request): 254 # if request.user.is_authenticated: 255 print(request.user) 256 if request.method == 'GET': 257 return render(request, 'index.html') 258 # else: 259 # return redirect('login') 260 261 262 #不加装饰器的方法判断状态 263 ''' 264 def index(request): 265 if request.user.is_authenticated: 266 print(request.user) 267 if request.method == 'GET': 268 return render(request, 'index.html') 269 else: 270 return redirect('login') 271 ''' 272 273 274 #注销 275 def logout(request): 276 auth.logout(request) 277 return redirect('login') 278 279 #修改密码 280 def reset_psd(request): 281 if request.user.is_authenticated: 282 if request.method == 'GET': 283 return render(request, 'reset_psd.html') 284 elif request.method == 'POST': 285 old_password = request.POST.get('old_password') 286 new_password = request.POST.get('new_password') 287 r_new_password = request.POST.get('r_new_password') 288 # ret=request.user.check_password(old_password) 289 # print(ret) 290 if request.user.check_password(old_password): 291 if new_password == r_new_password: 292 request.user.set_password(new_password) 293 request.user.save() 294 return JsonResponse({'status': True, 'info': '操作成功!', 'url': reverse('index')}) 295 else: 296 return JsonResponse({'status': False, 'info': '两次新密码不一致!', 'url': ''}) 297 else: 298 return JsonResponse({'status': False, 'info': '操作失败:原密码输入有误!', 'url': ''}) 299 return JsonResponse({'status': False, 'info': '操作失败!', 'url': ''}) 300 301 else: 302 return redirect('login')
urls.py
1 from django.conf.urls import url 2 from django.contrib import admin 3 from app01 import views 4 5 urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^register/', views.register, name='register'), 8 url(r'^login/', views.login, name='login'), 9 url(r'^index/', views.index, name='index'), 10 url(r'^logout/', views.logout, name='logout'), 11 url(r'^reset_psd/', views.reset_psd, name='reset_psd'), 12 url(r'^get_cverification_code/', views.get_cverification_code, name='get_cverification_code'),#随机验证码 13 14 ]
templates
register.html
1 {% load static %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}"> 7 8 <title>用户注册</title> 9 </head> 10 <body style="background-image: url('{% static 'images/register_bg.gif' %}');background-size: cover"> 11 <div class="container"> 12 <div class="row"> 13 <div class="col-xs-6 col-xs-offset-3" style="margin-top: 12%;"> 14 <div class="container-fluid" style="background-color: rgba(255,255,255,0.2);border-radius: 5%"> 15 <div class="row"> 16 <h2 class="text-left col-xs-8 text-primary">新用户注册:</h2> 17 </div> 18 <div class="row" > 19 <form action="{% url 'register' %}" method="post" novalidate class="form-horizontal"> 20 {% csrf_token %} 21 {% for field in register_obj %} 22 <div class="form-group" > 23 <label for="{{ field.id_for_label }}" 24 class="col-xs-3 control-label" >{{ field.label }}</label> 25 <div class="col-xs-7"> 26 {{ field }} 27 <div style="height: 10px;" class="text-danger">{{ field.errors.0 }}</div> 28 </div> 29 </div> 30 {% endfor %} 31 <div class="form-group"> 32 <div class="col-sm-7 col-xs-offset-3"> 33 <span class="col-xs-10 text-success text-center" 34 style="line-height: 200%">已有账号,请<a href="{% url 'login' %}">登录</a>!</span> 35 <input type="submit" class="btn btn-success btn-sm col-xs-2 pull-right"> 36 37 </div> 38 </div> 39 </form> 40 </div> 41 </div> 42 </div> 43 </div> 44 </div> 45 46 </body> 47 <script src="{% static 'jquery-3.4.1.js' %}"></script> 48 <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> 49 </html>
login.html
1 {% load static %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}"> 7 <title>用户登录</title> 8 </head> 9 <body style="background-image: url('{% static "images/login_bg.jpg" %}');background-size: cover"> 10 <div> 11 <div class="container"> 12 <div class="row"> 13 <div class='col-xs-4 col-xs-offset-4'> 14 <div class="row" style="margin-top: 50%;background-color: rgba(255,255,255,0.2 );border-radius: 3%"> 15 <div class="row c1"> 16 <h2 class=" col-xs-6 text-primary" style="margin-bottom: 30px">用户登录</h2> 17 </div> 18 <div class="row"> 19 <div class="form-group" style="height: 60px;"> 20 <label for="username" class="col-xs-3 control-label text-right">用户名:</label> 21 <div class="col-xs-8"> 22 <input type="text" class="form-control" id="username"> 23 <div class="text-danger"></div> 24 </div> 25 </div> 26 <div class="form-group" style="height: 60px;"> 27 <label for="password" class="col-xs-3 control-label text-right">密码:</label> 28 <div class="col-xs-8"> 29 <input type="password" class="form-control" id="password"> 30 <div class="text-danger"></div> 31 </div> 32 </div> 33 34 <div class="form-group" style="height: 60px;"> 35 <label for="code" class="col-xs-3 control-label text-right">验证码:</label> 36 <div class="col-xs-4"> 37 <input type="text" class="form-control" id="code"> 38 <div class="text-danger"></div> 39 </div> 40 <div class="col-xs-5" style="padding-left: 0"><img src="{% url 'get_cverification_code' %}" 41 alt="" id="cverification_code"></div> 42 </div> 43 44 <div class="form-group" style="height: 60px;"> 45 {% csrf_token %} 46 <div class="col-xs-8 col-xs-offset-3"> 47 <a href="{% url 'register' %}"> 48 <button class="btn btn-primary col-xs-offset-2" id="register">注册</button> 49 </a> 50 <button class="btn btn-success col-xs-offset-2" id="submit">登录</button> 51 <div class=" text-danger"></div> 52 </div> 53 </div> 54 </div> 55 </div> 56 </div> 57 </div> 58 </div> 59 </div> 60 61 </div> 62 </div> 63 </body> 64 <script src="{% static 'jquery-3.4.1.js' %}"></script> 65 <script src="{% static 'jquery-cookie-1.4.1.js' %}"></script> 66 <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> 67 <script> 68 $(function () { 69 70 $('#cverification_code').on('click',function () { 71 var src='{% url "get_cverification_code" %}?temp='+Math.random(); 72 console.log(src); 73 $('#cverification_code').attr('src',src); 74 }); 75 76 77 78 $('#username').blur(function () { 79 if (username !== '') { 80 $('#username').next().text(''); 81 } 82 }); 83 $('#password').blur(function () { 84 if (username !== '') { 85 $('#password').next().text(''); 86 } 87 }); 88 $('#code').blur(function () { 89 if (username !== '') { 90 $('#code').next().text(''); 91 } 92 }); 93 $('#username').focus(function () { 94 if (username !== '') { 95 $('#submit').next().text(''); 96 } 97 }); 98 $('#password').focus(function () { 99 if (username !== '') { 100 $('#submit').next().text(''); 101 } 102 }); 103 $('#code').focus(function () { 104 if (username !== '') { 105 $('#code').next().text(''); 106 } 107 }); 108 109 110 $('#submit').click(function () { 111 112 var username = $('#username').val().trim(); 113 var password = $('#password').val().trim(); 114 var cverification_code = $('#code').val().trim(); 115 console.log(cverification_code); 116 console.log(cverification_code.length); 117 118 119 if (username === '' || password === '' || cverification_code.length !== 6) { 120 if (username === '') { 121 $('#username').next().text('用户名不能为空!'); 122 } 123 ; 124 if (password === '') { 125 $('#password').next().text('密码不能为空!') 126 } 127 ; 128 if (cverification_code.length !== 6) { 129 $('#code').next().text('验证码为6位!'); 130 } 131 ; 132 console.log(cverification_code.length); 133 if (cverification_code.length !== 6) { 134 $('#code').next().text('验证码为6位!'); 135 return false 136 } 137 ; 138 139 } else { 140 $.ajax({ 141 url: '{% url 'login' %}', 142 type: 'POST', 143 headers: {'X-CSRFToken': $.cookie('csrftoken')}, 144 data: { 145 'username': username, 146 'password': password, 147 'cverification_code': cverification_code, 148 149 }, 150 success: function (request) { 151 console.log(request); 152 if (request.status === 1) { 153 location.href = request.url; 154 } else { 155 $('#submit').next().text(request.url); 156 } 157 } 158 }) 159 } 160 161 }) 162 }) 163 </script> 164 </html>
index. Html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>首页</title> 6 </head> 7 <body> 8 <h1>index</h1> 9 <a href="{% url 'logout' %}"><h6>注销</h6></a> 10 <a href="{% url 'reset_psd' %}">修改密码</a> 11 </body> 12 </html>
reset_psd.html
1 {% load static %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}"> 7 <title>修改密码</title> 8 </head> 9 <body> 10 <div><h3>当前用户:{{ username }}</h3> 11 12 <p>请输入原密码:<input type="password" id="old_password"></p> 13 <P>请输入新密码:<input type="password" id="new_password"></P> 14 <p>请确认新密码:<input type="password" id="r_new_password"></p> 15 {% csrf_token %} 16 <p><button class="btn btn-success" id="submit">保存</button></p> 17 18 </div> 19 </body> 20 <script src="{% static 'jquery-3.4.1.js' %}"></script> 21 <script src="{% static 'jquery-cookie-1.4.1.js' %}"></script> 22 <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> 23 <script> 24 $(function () { 25 $('#submit').click(function () { 26 var old_password=$('#old_password').val().trim(); 27 var new_password=$('#new_password').val().trim(); 28 var r_new_password=$('#r_new_password').val().trim(); 29 30 if(old_password===''||new_password==='' || r_new_password===''){ 31 alert('不允许有空!'); 32 return false 33 }; 34 35 if(new_password!==r_new_password){ 36 alert('两次输入的新密码不一致,请重新确认输入!'); 37 return false 38 }; 39 40 41 $.ajax({ 42 url:'{% url "reset_psd" %}', 43 type:'post', 44 headers:{'X-CSRFToken':$.cookie('csrftoken')}, 45 data:{ 46 old_password:old_password, 47 new_password:new_password, 48 r_new_password:r_new_password, 49 }, 50 success:function (response) { 51 if(response.status===true){ 52 alert(response.info); 53 location.href=response.url; 54 } 55 else { 56 alert(response.info) 57 } 58 } 59 60 } 61 ) 62 }) 63 }) 64 </script> 65 </html>