美文网首页
Python(五十七)Auth权限认证

Python(五十七)Auth权限认证

作者: Lonelyroots | 来源:发表于2022-02-13 22:50 被阅读0次

    从2021年9月2日发文至今,Python系列(包括代码在内)共计114020个字、五十七篇!

    1. Auth系统中的表

    在注册admin后台的账户,我们可以在数据库中查看auth_user这张表。

    auth_user
    可以通过格式化方式进行查看!SELECT * FROM auth_user\G

    1.1. Auth系统的数据表

    User:User是auth模块中维护用户信息的关系模式(继承了models.Model),数据库中该表被命名为auth_user。
    Group:User对象中有一个名为groups的多对多字段,多对多关系由auth_user_groups数据表维护。Group对象可以通过user_set反向查询用户组中的用户。
    Permission:Django的auth系统提供了模型级的权限控制, 即可以检查用户是否对某个数据表拥有增(add),改(change), 删(delete)权限。

    1.2. Auth认证系统功能

    create_user 创建用户
    authenticate 验证登录
    login 记住用户的登录状态
    logout 退出登录
    is_authenticated 判断用户是否登录
    login_required 判断用户是否登录的装饰器

    1.3. 使用Auth系统

    在auth系统当中,django已经为我们提供了一个用户身份验证,用户组和权限管理这些功能,那么就可以使用它来完善我们现有的这个项目。

    form_session/forms.py:

    from django import forms
    
    class RegisterForm(forms.Form):
        # 用户名,默认情况下是text文本框
        username = forms.CharField(max_length=30,min_length=4,
                                   widget=forms.TextInput(attrs={'placeholder':'请输入用户名'})
                                   )
        # 密码,这里要将文本框改成密码框
        password = forms.CharField(max_length=16,min_length=6,
                                   widget=forms.PasswordInput(attrs={'placeholder':'请输入密码'})
                                   )
        # 确认密码
        password_confirm = forms.CharField(max_length=16,min_length=6,
                                   widget=forms.PasswordInput(attrs={'placeholder':'请再次输入密码'})
                                   )
        # 邮箱
        email = forms.EmailField()
    
    class LoginForm(forms.Form):
        # 用户名,默认情况下是text文本框
        username = forms.CharField(max_length=30,min_length=4,
                                   widget=forms.TextInput(attrs={'placeholder':'请输入用户名'})
                                   )
        # 密码,这里要将文本框改成密码框
        password = forms.CharField(max_length=16,min_length=6,
                                   widget=forms.PasswordInput(attrs={'placeholder':'请输入密码'})
                                   )
    

    form_session/views.py:

    # (十三、Auth权限认证)
    
    from django.shortcuts import render
    from django.views import View
    from django.shortcuts import redirect,reverse
    from django.contrib.auth.models import User,Group,Permission
    from django.contrib.auth import login,logout,authenticate
    from django.http import HttpResponse
    from .forms import LoginForm,RegisterForm
    from django.contrib.auth.decorators import login_required
    
    class RegisterTest(View):
        def get(self,request):
            form = RegisterForm()
            return render(request,'form_session/register.html',context={'form':form})
    
        def post(self,request):
            form = RegisterForm(request.POST)      # 获取表单的所有数据
            if form.is_valid():     # 判断数据是否合法,True表示合法,这里的合不合法主要指邮箱@后的格式
                # 保存用户提交的数据
                username = form.cleaned_data.get('username')
                pwd = form.cleaned_data.get('password')
                pwd_confirm = form.cleaned_data.get('password_confirm')
                email = form.cleaned_data.get('email')
                if pwd == pwd_confirm:
                    # 两次密码保持一致,则保存
                    User.objects.create_user(username=username,password=pwd,email=email)
                    return HttpResponse('注册成功!欢迎您使用该网站')
                else:
                    return HttpResponse('注册失败!请确认两次密码输入一致!')
            else:
                return HttpResponse('请输入合法的数据!')
    
    class Db_Login(View):
        def get(self,request):
            form = LoginForm()
            return render(request,'form_session/db_login.html',context={
                'form':form
            })
    
        def post(self,request):
            form = LoginForm(request.POST)
            if form.is_valid():     # 判断数据是否合法,True表示合法
                # 保存用户提交的数据
                username = form.cleaned_data.get('username')
                pwd = form.cleaned_data.get('password')
                user = authenticate(username=username,password=pwd)
                if user:
                    # 如果有这个用户
                    login(request,user)     # 保持用户登录状态
                    return redirect(reverse('home'))
                else:
                    return HttpResponse('登录失败!用户名或密码输入有误')
            else:
                return HttpResponse('请输入合法的数据!')
    
    def home(request):
        return render(request,'form_session/home.html',context={
            'username':request.user
        })
    
    def logoutTest(request):
        logout(request)
        return redirect('home')
    

    templates/form_session/register.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户注册页面</title>
    </head>
    <body>
    
    <form action="{% url 'register' %}" method="post">
        {% csrf_token %}
        {#后台传输过来的数据需要在前端进行显示,使用的是模板变量#}
    {% comment %}    {# 默认是内联标签,都在同一行进行显示 #}
        {{ form }}{% endcomment %}
        {#  以p标签单独成行进行显示  #}
        {{ form.as_p }}
        <input type="submit" value="注册">
    </form>
    
    </body>
    </html>
    

    templates/form_session/db_login.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户登录页面</title>
    </head>
    <body>
    
    {# action里不写提交路径,默认提交到当前路径下。不写任何东西,提交到当前路径才会有next值 #}
    <form action="" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="登录">
    </form>
    
    </body>
    </html>
    

    templates/form_session/home.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    欢迎{{ username }}登录页面    {# 这里的username是views里context传的键 #}
    <a href="{% url 'logout' %}">注销</a>
    
    上下文处理器的用户名:{{ myuser1 }}     {#  (十二、中间件和上下文处理器)  #}
    
    </body>
    </html>
    

    form_session/urls.py:

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('home/',views.home,name='home'),
        path('logout/',views.logoutTest,name='logout'),
        path('register/',views.RegisterTest.as_view(),name='register'),
        path('dblogin/',views.Db_Login.as_view(),name='dblogin'),
    ]
    

    2. 登录注册实现

    Myblog15_16/settings.py:先在settings里注册登录路由

    LOGIN_URL = '/form_session/dblogin/'        # 用户未登录的情况下跳转到登录页面, (十三、Auth权限认证)
    

    2.1. next_url的使用

    从刚才的登录页跳转中,我们会看到一个next的参数,这个参数前面的需要登录的那个视图的url。

    form_session/forms.py:

    from django import forms
    
    class RegisterForm(forms.Form):
        # 用户名,默认情况下是text文本框
        username = forms.CharField(max_length=30,min_length=4,
                                   widget=forms.TextInput(attrs={'placeholder':'请输入用户名'})
                                   )
        # 密码,这里要将文本框改成密码框
        password = forms.CharField(max_length=16,min_length=6,
                                   widget=forms.PasswordInput(attrs={'placeholder':'请输入密码'})
                                   )
        # 确认密码
        password_confirm = forms.CharField(max_length=16,min_length=6,
                                   widget=forms.PasswordInput(attrs={'placeholder':'请再次输入密码'})
                                   )
        # 邮箱
        email = forms.EmailField()
    
    class LoginForm(forms.Form):
        # 用户名,默认情况下是text文本框
        username = forms.CharField(max_length=30,min_length=4,
                                   widget=forms.TextInput(attrs={'placeholder':'请输入用户名'})
                                   )
        # 密码,这里要将文本框改成密码框
        password = forms.CharField(max_length=16,min_length=6,
                                   widget=forms.PasswordInput(attrs={'placeholder':'请输入密码'})
                                   )
    

    form_session/views.py:

    # (十三、Auth权限认证)
    
    from django.shortcuts import render
    from django.views import View
    from django.shortcuts import redirect,reverse
    from django.contrib.auth.models import User,Group,Permission
    from django.contrib.auth import login,logout,authenticate
    from django.http import HttpResponse
    from .forms import LoginForm,RegisterForm
    from django.contrib.auth.decorators import login_required
    
    class RegisterTest(View):
        def get(self,request):
            form = RegisterForm()
            return render(request,'form_session/register.html',context={'form':form})
    
        def post(self,request):
            form = RegisterForm(request.POST)      # 获取表单的所有数据
            if form.is_valid():     # 判断数据是否合法,True表示合法,这里的合不合法主要指邮箱@后的格式
                # 保存用户提交的数据
                username = form.cleaned_data.get('username')
                pwd = form.cleaned_data.get('password')
                pwd_confirm = form.cleaned_data.get('password_confirm')
                email = form.cleaned_data.get('email')
                if pwd == pwd_confirm:
                    # 两次密码保持一致,则保存
                    User.objects.create_user(username=username,password=pwd,email=email)
                    return HttpResponse('注册成功!欢迎您使用该网站')
                else:
                    return HttpResponse('注册失败!请确认两次密码输入一致!')
            else:
                return HttpResponse('请输入合法的数据!')
    
    class Db_Login(View):
        def get(self,request):
            form = LoginForm()
            return render(request,'form_session/db_login.html',context={
                'form':form
            })
    
        def post(self,request):
            form = LoginForm(request.POST)
            if form.is_valid():     # 判断数据是否合法,True表示合法
                # 保存用户提交的数据
                username = form.cleaned_data.get('username')
                pwd = form.cleaned_data.get('password')
                user = authenticate(username=username,password=pwd)
                if user:
                    # 如果有这个用户
                    login(request,user)     # 保持用户登录状态
                    next_url = request.GET.get('next')
                    print('next_url:',next_url)
                    # 这里重定向:加reverse,则只能跟路由下的name值;若不加reverse,则既可以跟路由下的name值,也可以跟访问的路由。
                    return redirect(next_url)
                else:
                    return HttpResponse('登录失败!用户名或密码输入有误')
            else:
                return HttpResponse('请输入合法的数据!')
    
    @login_required()       # 通过装饰器控制登录权限,去判断用户是否登录
    def home(request):
        return render(request,'form_session/home.html',context={
            'username':request.user
        })
    
    def logoutTest(request):
        logout(request)
        return redirect('home')
    

    templates/form_session/register.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户注册页面</title>
    </head>
    <body>
    
    <form action="{% url 'register' %}" method="post">
        {% csrf_token %}
        {#后台传输过来的数据需要在前端进行显示,使用的是模板变量#}
    {% comment %}    {# 默认是内联标签,都在同一行进行显示 #}
        {{ form }}{% endcomment %}
        {#  以p标签单独成行进行显示  #}
        {{ form.as_p }}
        <input type="submit" value="注册">
    </form>
    
    </body>
    </html>
    

    templates/form_session/db_login.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户登录页面</title>
    </head>
    <body>
    
    {# action里不写提交路径,默认提交到当前路径下。不写任何东西,提交到当前路径才会有next值 #}
    <form action="" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="登录">
    </form>
    
    </body>
    </html>
    

    templates/form_session/home.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    欢迎{{ username }}登录页面    {# 这里的username是views里context传的键 #}
    <a href="{% url 'logout' %}">注销</a>
    
    上下文处理器的用户名:{{ myuser1 }}     {#  (十二、中间件和上下文处理器)  #}
    
    </body>
    </html>
    

    form_session/urls.py:

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('home/',views.home,name='home'),
        path('logout/',views.logoutTest,name='logout'),
        path('register/',views.RegisterTest.as_view(),name='register'),
        path('dblogin/',views.Db_Login.as_view(),name='dblogin'),
    ]
    
    运行结果
    运行结果
    运行结果

    3. 权限的实现

    查看数据库中auth_permission这张表,里面有所有表的一些操作权限,这些是在表创建的同时添加进来的数据。SELECT * FROM auth_permission;

    blog/models.py:

    from django.db import models
    
    # Create your models here.
    
    class BlogModel(models.Model):
        title = models.CharField(max_length=50)
        content = models.TextField()
    

    blog/views.py:

    # (十三、Auth权限认证)
    
    from django.views import View
    from .models import BlogModel
    from django.http import HttpResponse
    from django.shortcuts import render,redirect,reverse       # 重定向需导入模块
    from django.contrib.auth.decorators import permission_required      # (十三、Auth权限认证)
    # 给用户添加权限
    from django.contrib.auth.models import User,Group,Permission      # (十三、Auth权限认证)
    # 方法装饰器
    from django.utils.decorators import method_decorator      # (十三、Auth权限认证)
    
    def index(request):
        return render(request,'blog/blog_index.html')
    
    # 这样的话只有被设置拥有此权限的用户才可访问此视图函数对应的前端页面(注意:如果是超级用户则拥有所有权限的)。哪怕你登录了普通用户会发现也访问失败
    # ,它会自动给你跳转到你settings.py文件中设置的权限不足而跳转到的页面里!(十三、Auth权限认证)
    # 设置权限,appname.codename(权限名称)
    @permission_required('blog.add_blogmodel')
    def blog_add(request):
        # 如果是get请求,那就是访问页面
        if request.method == 'GET':
            return render(request,'blog/blog_add.html')
        # 如果是post请求,那就是向页面提交数据
        elif request.method == 'POST':
            # 获取文章标题
            title = request.POST.get('title')       # 这里的title需要和前端页面blog_add里的标题input中的name值保持一致.
            # 获取文章内容
            content = request.POST.get('content')       # 这里的content需要和前端页面blog_add里的内容input中的name值保持一致.
            # 将数据保存到数据库中
            BlogModel.objects.get_or_create(title=title,content=content)
            b_list = BlogModel.objects.all()        # 查询所有文章
            # 文章添加成功后,要转到文章列表页,展示所有文章.
            return render(request,'blog/blog_list.html',context={'b_list':b_list})
    
    def blog_list(request):
        b_list = BlogModel.objects.all()  # 查询所有文章
        return render(request,'blog/blog_list.html', context={'b_list': b_list})
    
    # 通过blog_id确定具体查看的是哪篇文章。
    def blog_detail(request,blog_id):
        blog = BlogModel.objects.get(id=blog_id)
        return render(request,'blog/blog_detail.html',context={'blog':blog})
    
    def blog_edit(request,blog_id):
        blog = BlogModel.objects.get(id=blog_id)
        if request.method == 'GET':
            return render(request,'blog/blog_edit.html',context={'blog':blog})      # 将原内容写入
        elif request.method == 'POST':
            # 获取文章标题
            new_title = request.POST.get('new_title')  # 这里的new_title需要和前端页面blog_edit里的标题input中的name值保持一致.
            # 获取文章内容
            new_content = request.POST.get('new_content')  # 这里的new_content需要和前端页面blog_edit里的内容input中的name值保持一致.
            BlogModel.objects.filter(id=blog_id).update(title=new_title,content=new_content)  # 这里的红变量名是数据库中的字段名称
            # 文章修改成功后,要转到文章列表页,展示所有文章.
            return redirect(reverse('blog_list'))
    
    # 通过blog_id确定具体需要删除的是哪篇文章
    def blog_delete(request,blog_id):
        blog = BlogModel.objects.filter(id=blog_id)
        # 如果有当前这篇文章就删除
        if blog:
            blog.delete()
            return redirect(reverse('blog_list'))       # 'blog_list'即需要重定向的路由name值.
        else:
            return HttpResponse('该文章不存在')
    
    # 使用类视图,可以不需要get和post判断
    class Blog_update(View):
        def get(self,request,blog_id):
            blog = BlogModel.objects.get(id=blog_id)
            return render(request,'blog/blog_edit.html',context={'blog':blog})
    
        # 在类视图里设置拥有此权限的用户才可访问此视图函数对应的前端页面,需要加method_decorator()
        @method_decorator(permission_required('blog.change_blogmodel'))
        def post(self,request,blog_id):
            # 获取文章标题
            new_title = request.POST.get('new_title')  # 这里的new_title需要和前端页面blog_edit里的标题input中的name值保持一致.
            # 获取文章内容
            new_content = request.POST.get('new_content')  # 这里的new_content需要和前端页面blog_edit里的内容input中的name值保持一致.
            BlogModel.objects.filter(id=blog_id).update(title=new_title,content=new_content)  # 这里的红变量名是数据库中的字段名称
            # 文章修改成功后,要转到文章列表页,展示所有文章.
            return redirect(reverse('blog_list'))
    
    # 添加权限
    def permission_add(request):
        user = User.objects.filter(username='chen').first()     # 获取要添加权限的用户
        # per = Permission.objects.filter(codename='add_blogmodel').first()     # 获取用户要添加的权限
        per = Permission.objects.filter(codename='change_blogmodel').first()     # 获取用户要修改的权限
        user.user_permissions.add(per)      # 正向访问,通过外键给用户添加权限
        return HttpResponse('用户权限添加成功!')
    

    blog/urls.py:

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('index/',views.index,name='blog_index'),
        path('add/',views.blog_add,name='blog_add'),
        path('list/',views.blog_list,name='blog_list'),
        path('detail/<blog_id>/',views.blog_detail,name='blog_detail'),
        path('edit/<blog_id>/',views.blog_edit,name='blog_edit'),
        path('delete/<blog_id>/',views.blog_delete,name='blog_delete'),
        path('cls_update/<blog_id>/',views.Blog_update.as_view(),name='cls_update'),      # 只要是使用类视图,就需要调用as_view()这个方法
        path('permission_add/',views.permission_add,name='permission_add')
    ]
    

    templates/blog/blog_base.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}模板的母版文件{% endblock %}</title>
    </head>
    <body>
    {% block body %}
    
    {% endblock %}
    </body>
    </html>
    

    templates/blog/blog_index.html:

    {% extends 'blog/blog_base.html' %}
    
    {% block title %}
        博客首页
    {% endblock %}
    
    {% block body %}
        <p><a href="{% url 'blog_add' %}">添加博客</a></p>
        <p><a href="{% url 'blog_list' %}">博客列表</a></p>
    {% endblock %}
    

    templates/blog/blog_add.html:

    {% extends 'blog/blog_base.html' %}
    
    {% block title %}
        添加博客
    {% endblock %}
    
    {% block body %}
        <h1>添加新博客</h1>
        <form action="{% url 'blog_add' %}" method="post">
            {#  csrf_token防止跨域请求  #}
            {% csrf_token %}
            标题 <input type="text" id="title" name="title" placeholder="请输入文章标题"><br>
            内容 <textarea name="content" id="content" cols="30" rows="10" placeholder="请输入文章内容"></textarea> {# textarea:文本域 #}
            <button type="submit">发布文章</button>
        </form>      {# 渲染页面用get方式,提交数据用post方式 #}
        <br>
        <a href="{% url 'blog_index' %}">返回首页</a>
    {% endblock %}
    

    templates/blog/blog_list.html:

    {% extends 'blog/blog_base.html' %}
    
    {% block title %}
        博客显示
    {% endblock %}
    
    {% block body %}
        <h1>靓文展示页</h1>
        <table>
            <tr>
                <th width="80px">标题</th>
                <th>功能</th>
            </tr>
            {% for b in b_list %}
                <tr>
                    <td style="text-align: center"><a href="{% url 'blog_detail' b.id %}">{{ b.title }}</a></td>
                    <td><a href="{% url 'cls_update' b.id %}">编辑</a>&emsp;<a href="{% url 'blog_delete' b.id %}">删除</a></td>        {# (十、cookie与session:类视图) #}
                </tr>
            {% endfor %}
        </table>
        <br>
        <a href="{% url 'blog_index' %}">返回首页</a>
    {% endblock %}
    

    templates/blog/blog_detail.html:

    {% extends 'blog/blog_base.html' %}
    
    {% block title %}
        文章详情
    {% endblock %}
    
    {% block body %}
        <h1>{{ blog.title }}</h1>
        {{ blog.content }}
        <br>
        <br>
        <a href="{% url 'blog_list' %}">返回列表页</a>
    {% endblock %}
    

    templates/blog/blog_edit.html:

    {# (十三、Auth权限认证) #}
    
    {% extends 'blog/blog_base.html' %}
    
    {% block title %}
        修改博客
    {% endblock %}
    
    {% block body %}
        <h1>修改博客</h1>
        <form action="{% url 'cls_update' blog.id %}" method="post">     {# (十、cookie与session:类视图) #}
            {#  csrf_token防止跨域请求  #}
            {% csrf_token %}
            新标题:<input type="text" id="new_title" name="new_title" value="{{ blog.title }}"><br>
            新内容:<textarea name="new_content" id="new_content" cols="20" rows="10">{{ blog.content }}</textarea>
            <button type="submit">确认修改</button>
        </form>
        <br>
        <a href="{% url 'blog_list' %}">返回列表页</a>
    {% endblock %}
    

    文章到这里就结束了!希望大家能多多支持Python(系列)!六个月带大家学会Python,私聊我,可以问关于本文章的问题!以后每天都会发布新的文章,喜欢的点点关注!一个陪伴你学习Python的新青年!不管多忙都会更新下去,一起加油!

    Editor:Lonelyroots

    相关文章

      网友评论

          本文标题:Python(五十七)Auth权限认证

          本文链接:https://www.haomeiwen.com/subject/oteqlrtx.html