zoukankan      html  css  js  c++  java
  • 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>
  • 相关阅读:
    一分钟认识:Cucumber框架(一)
    迭代=冲刺?
    Nresource服务之接口缓存化
    58集团支付网关设计
    服务治理在资源中心的实践
    资源中心——连接池调优
    4种常用的演讲结构: 黄金圈法则结构、PREP结构、时间轴结构、金字塔结构
    微服务时代,领域驱动设计在携程国际火车票的实践
    Sentinel -- FLOW SLOT核心原理篇
    管理篇-如何跨部门沟通?
  • 原文地址:https://www.cnblogs.com/znyyy/p/11367463.html
Copyright © 2011-2022 走看看