django用户认证组件
思考:
前一篇提到的seesion,当用户登录后会保存session。
浏览器再次访问会带上服务器返回的session:sessionid ---> 登录时随机生成的key
服务器拿到sessionid后去数据库匹配,匹配到后拿到一个字典数据:{a:b,c:d}
当数据有变化时,会更新某个值,如 c:d --> c:e ,那么直接更新数据库 {a:b,c:e},sessionid并不会变化,然后将原来的sessionid继续回传给浏览器。
这样的处理机制存在什么样的问题呢?
问题:
A在浏览器登录,登录信息是A用户的,比如服务器的一些权限控制,角色控制信息,这个时候保存在seesion库里,当B又用浏览器登录,因为用一个浏览器,传给服务器的sessionid是一样的,那么在更新B的数据的时候,可能部分A的信息,并不会删掉,服务器会在A的基础上做更新,那么就存在数据错乱的风险,B获得A的敏感信息。
解决:
判断是不是同一个用户,不同用户,同一个sessionid,先清空A的信息,再写入B的信息。
我们当然可以自己做一套,但是django已经帮我们实现了,我们直接用就好,这就是用户认证组件。
先settings配置好数据库,然后运行以下命令进行数据库初始化,因为要用到其中的session表。
1 """ 2 Django settings for auther project. 3 4 Generated by ‘django-admin startproject‘ using Django 2.2.3. 5 6 For more information on this file, see 7 https://docs.djangoproject.com/en/2.2/topics/settings/ 8 9 For the full list of settings and their values, see 10 https://docs.djangoproject.com/en/2.2/ref/settings/ 11 """ 12 13 import os 14 15 # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 18 19 # Quick-start development settings - unsuitable for production 20 # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ 21 22 # SECURITY WARNING: keep the secret key used in production secret! 23 SECRET_KEY = ‘6y-h4%y^%+2^@_2y7xyz0fc98faozkvh(xp!)h4)q8n0hsx+ee‘ 24 25 # SECURITY WARNING: don‘t run with debug turned on in production! 26 DEBUG = True 27 28 ALLOWED_HOSTS = [] 29 30 31 # Application definition 32 33 INSTALLED_APPS = [ 34 ‘django.contrib.admin‘, 35 ‘django.contrib.auth‘, 36 ‘django.contrib.contenttypes‘, 37 ‘django.contrib.sessions‘, 38 ‘django.contrib.messages‘, 39 ‘django.contrib.staticfiles‘, 40 ‘auth_app01‘, 41 ] 42 43 MIDDLEWARE = [ 44 ‘django.middleware.security.SecurityMiddleware‘, 45 ‘django.contrib.sessions.middleware.SessionMiddleware‘, 46 ‘django.middleware.common.CommonMiddleware‘, 47 ‘django.middleware.csrf.CsrfViewMiddleware‘, 48 ‘django.contrib.auth.middleware.AuthenticationMiddleware‘, 49 ‘django.contrib.messages.middleware.MessageMiddleware‘, 50 ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘, 51 ] 52 53 ROOT_URLCONF = ‘auther.urls‘ 54 55 TEMPLATES = [ 56 { 57 ‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘, 58 ‘DIRS‘: [os.path.join(BASE_DIR, ‘templates‘)], 59 ‘APP_DIRS‘: True, 60 ‘OPTIONS‘: { 61 ‘context_processors‘: [ 62 ‘django.template.context_processors.debug‘, 63 ‘django.template.context_processors.request‘, 64 ‘django.contrib.auth.context_processors.auth‘, 65 ‘django.contrib.messages.context_processors.messages‘, 66 ], 67 }, 68 }, 69 ] 70 71 WSGI_APPLICATION = ‘auther.wsgi.application‘ 72 73 74 # Database 75 # https://docs.djangoproject.com/en/2.2/ref/settings/#databases 76 77 # DATABASES = { 78 # ‘default‘: { 79 # ‘ENGINE‘: ‘django.db.backends.sqlite3‘, 80 # ‘NAME‘: os.path.join(BASE_DIR, ‘db.sqlite3‘), 81 # } 82 # } 83 84 85 # Password validation 86 # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators 87 88 AUTH_PASSWORD_VALIDATORS = [ 89 { 90 ‘NAME‘: ‘django.contrib.auth.password_validation.UserAttributeSimilarityValidator‘, 91 }, 92 { 93 ‘NAME‘: ‘django.contrib.auth.password_validation.MinimumLengthValidator‘, 94 }, 95 { 96 ‘NAME‘: ‘django.contrib.auth.password_validation.CommonPasswordValidator‘, 97 }, 98 { 99 ‘NAME‘: ‘django.contrib.auth.password_validation.NumericPasswordValidator‘, 100 }, 101 ] 102 103 104 # Internationalization 105 # https://docs.djangoproject.com/en/2.2/topics/i18n/ 106 107 LANGUAGE_CODE = ‘en-us‘ 108 109 TIME_ZONE = ‘UTC‘ 110 111 USE_I18N = True 112 113 USE_L10N = True 114 115 USE_TZ = True 116 117 118 # Static files (CSS, JavaScript, Images) 119 # https://docs.djangoproject.com/en/2.2/howto/static-files/ 120 121 STATIC_URL = ‘/static/‘ 122 STATICFILES_DIRS = [ 123 os.path.join(BASE_DIR, ‘statics‘) 124 ] 125 126 LOGIN_URL = ‘/app01/login‘ 127 128 DATABASES = { 129 ‘default‘: { 130 ‘ENGINE‘: ‘django.db.backends.mysql‘, 131 ‘NAME‘:‘auth‘,# 要连接的数据库,连接前需要创建好 132 ‘USER‘:‘root‘,# 连接数据库的用户名 133 ‘PASSWORD‘:‘‘,# 连接数据库的密码 134 ‘HOST‘:‘127.0.0.1‘,# 连接主机,默认本级 135 ‘PORT‘:3308 # 端口 默认3306 136 } 137 } 138 139 LOGGING = { 140 ‘version‘: 1, 141 ‘disable_existing_loggers‘: False, 142 ‘handlers‘: { 143 ‘console‘:{ 144 ‘level‘:‘DEBUG‘, 145 ‘class‘:‘logging.StreamHandler‘, 146 }, 147 }, 148 ‘loggers‘: { 149 ‘django.db.backends‘: { 150 ‘handlers‘: [‘console‘], 151 ‘propagate‘: True, 152 ‘level‘:‘DEBUG‘, 153 }, 154 } 155 }
settings
python manage.py makemigrations
python manage.py migrate
视图函数
1 from django.shortcuts import render, redirect, HttpResponse 2 from auth_app01.myforms import Form, LoginForm 3 from django.contrib import auth 4 from django.contrib.auth.models import User 5 from django.contrib.auth import authenticate, login, logout 6 from django.contrib.auth.decorators import login_required 7 from auther import settings 8 9 10 def index(request): 11 if not request.user.is_authenticated: 12 print(request.user) 13 else: 14 print(‘no user‘) 15 return render(request, ‘index.html‘) 16 17 18 def regist(request): 19 if request.method == ‘POST‘: 20 form = Form(request.POST) 21 if form.is_valid(): 22 print(form.cleaned_data) 23 username = form.cleaned_data[‘name‘] 24 email = form.cleaned_data[‘email‘] 25 pwd = form.cleaned_data[‘pwd‘] 26 user = None 27 28 # 注意创建用户的时候要用create_user方法 用create的话不会对密码进行加密 29 res = User.objects.create_user(username=username,email=email,password=pwd) 30 31 # 注册完成之后 调用 authenticate() 进行认证得到一个 User 对象 32 if res: 33 user = authenticate(username=username, password=pwd) 34 35 # login函数接受一个HttpRequest对象,以及一个认证了的User对象 36 # login函数使用django的session框架给某个已认证的用户附加上sessionid等信息 37 # 链接跳转到index后 会带上sessionid信息,且 request.user 定义成了一个全局变量任何地方都可以用,包括模板 38 if user: 39 login(request, user) 40 41 return redirect(‘/app01/index/‘) 42 else: 43 print(form.cleaned_data) 44 print(form.errors) 45 return render(request, ‘regist.html‘, locals()) 46 else: 47 form = Form() 48 return render(request, ‘regist.html‘, locals()) 49 50 51 def mylogin(request): 52 if request.method == ‘POST‘: 53 form = LoginForm(request.POST) 54 if form.is_valid(): 55 print(form.cleaned_data) 56 username = form.cleaned_data.get(‘name‘) 57 pwd = form.cleaned_data.get(‘pwd‘) 58 user = auth.authenticate(username=username,password=pwd) 59 if user: 60 login(request, user) 61 # next_url 用户请求可能是其他请求重定向过来的,登录完成后,根据next值返回到相应的url 62 next_url = request.GET.get(‘next‘, ‘/app01/index‘) 63 return redirect(next_url) 64 else: 65 error = ‘用户名或密码错误!‘ 66 return render(request, ‘login.html‘, locals()) 67 else: 68 form = LoginForm() 69 return render(request, ‘login.html‘, locals()) 70 71 72 def mylogout(request): 73 # 接受一个HttpRequest对象,无返回值。调用时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。 74 logout(request) 75 return redirect( ‘/app01/index/‘) 76 77 78 # 上面通过auth已经实现了简单的登录注册以及首页之间的跳转 但是实际场景存在这样的情况: 79 # 1 用户登陆后才能访问某些页面, 80 # 2 如果用户没有登录就访问该页面的话直接跳到登录页面 81 # 3 用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址 82 83 # 方式一 84 def secret(request): 85 if request.user.is_authenticated: 86 my_secret = ‘secret‘ 87 return render(request, ‘secret.html‘,locals()) 88 else: 89 return redirect("%s?next=%s" % (settings.LOGIN_URL, request.path)) 90 91 # 方式二 login_required 装饰器 92 #若用户没有登录,则会跳转到django默认的 登录URL ‘/accounts/login/ ‘ (这个值可以在settings文件中通过LOGIN_URL进行修改)。 93 # 并传递 当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。 94 @login_required 95 def mimi(request): 96 my_secret = ‘mimi‘ 97 return render(request, ‘secret.html‘,locals()) 98 99 100 # 修改密码 101 @login_required 102 def set_password(request): 103 user = request.user 104 state = None 105 if request.method == ‘POST‘: 106 old_password = request.POST.get(‘old_password‘, ‘‘) 107 new_password = request.POST.get(‘new_password‘, ‘‘) 108 repeat_password = request.POST.get(‘repeat_password‘, ‘‘) 109 if user.check_password(old_password): 110 if not new_password: 111 state = ‘empty‘ 112 elif new_password != repeat_password: 113 state = ‘repeat_error‘ 114 else: 115 user.set_password(new_password) 116 user.save() 117 return redirect("/app01/login/") 118 else: 119 state = ‘password_error‘ 120 content = { 121 ‘user‘: user, 122 ‘state‘: state, 123 } 124 return render(request, ‘password.html‘, content)
forms组件
1 from django import forms 2 from django.forms import widgets 3 from django.core.exceptions import ValidationError 4 from django.contrib.auth.models import User 5 6 7 name_widget = widgets.TextInput(attrs={‘class‘:‘form-control‘}) 8 pwd_widget = widgets.PasswordInput(attrs={‘class‘:‘form-control‘}) 9 10 11 class Form(forms.Form): 12 name = forms.CharField(min_length=4, max_length=16, widget=name_widget, label=‘用户名‘) 13 pwd = forms.CharField(min_length=4, max_length=16, widget=pwd_widget, label=‘密码‘) 14 email = forms.EmailField(widget=name_widget, label=‘邮箱‘) 15 16 def clean_name(self): 17 val = self.cleaned_data.get(‘name‘) 18 res = User.objects.filter(username=val).exists() 19 if not res: 20 return val 21 else: 22 raise ValidationError(‘用户名已存在!‘) 23 24 25 class LoginForm(forms.Form): 26 name = forms.CharField(min_length=4, max_length=16, widget=name_widget, label=‘用户名‘) 27 pwd = forms.CharField(min_length=4, max_length=16, widget=pwd_widget, label=‘密码‘)
url分发器
from django.urls import path, re_path from auth_app01 import views urlpatterns = [ path(‘index/‘, views.index), path(‘regist/‘, views.regist), path(‘login/‘, views.mylogin), path(‘logout/‘, views.mylogout), path(‘secret/‘, views.secret), path(‘mimi/‘, views.mimi), ]
index.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>index</title> 6 <link rel="stylesheet" href="/static/bootstrap.min.css"> 7 </head> 8 <body> 9 <div class="container"> 10 <div class="row"> 11 <div class="col-md-6 col-md-offset-3"> 12 <h3>欢迎进入首页</h3> 13 {% if not request.user.is_authenticated %} 14 <a href="/app01/login" class="btn btn-success">登录</a> 15 <a href="/app01/regist" class="btn btn-success">注册</a> 16 {% else %} 17 <h4>hi,{{ request.user }}</h4> 18 <a href="/app01/logout" class="btn btn-success">注销</a> 19 {% endif %} 20 </div> 21 </div> 22 </div> 23 </body> 24 </html>
login.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>login</title> 6 <link rel="stylesheet" href="/static/bootstrap.min.css"> 7 </head> 8 <body> 9 10 <div class="container"> 11 <div class="row"> 12 <div class="col-md-6 col-md-offset-3"> 13 <h4>登录</h4> 14 <form action="" method="post"> 15 {% csrf_token %} 16 {% for field in form %} 17 <div class="form-group"> 18 <label for="">{{ field.label }}</label> 19 {{ field }} 20 <span class="pull-right" style="color: red">{{ field.errors.0 }}</span> 21 </div> 22 {% endfor %} 23 <input type="submit" class="btn btn-success"> 24 <span class="pull-right" style="color: red">{{ error }}</span> 25 </form> 26 </div> 27 </div> 28 </div> 29 30 </body> 31 </html>
regist.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>regist</title> <link rel="stylesheet" href="/static/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h4>注册</h4> <form action="" method="post"> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="">{{ field.label }}</label> {{ field }} <span class="pull-right" style="color: red">{{ field.errors.0 }}</span> </div> {% endfor %} <input type="submit" class="btn btn-success"> </form> </div> </div> </div> </body> </html>
secret.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>secret</title> </head> <body> {{ my_secret }} <h3>你很帅!</h3> </body> </html>
原文地址:https://www.cnblogs.com/znyyy/p/11367463.html
时间: 2024-09-30 10:41:56