16 django用户认证组件

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