下面从 登录页面开始,完整梳理你现在这套流程。

你现在实现的是:

用户访问登录页
↓
输入用户名和密码
↓
Django 校验账号密码
↓
登录成功
↓
根据用户角色自动跳转
↓
admin  -> 管理员页面
editor -> 编辑员页面
viewer -> 查看者页面

一、最终访问地址

你的系统现在主要有这些地址:

/user/login/        登录页面
/user/logout/       退出登录

/user/admin-page/   管理员页面
/user/editor-page/  编辑员页面
/user/viewer-page/  查看者页面

/user/no-role/      没有角色时的提示页面
/admin/             Django 管理后台

二、登录页面文件

登录页面文件是:

user/templates/user/login.html

内容是:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>

<h2>用户登录</h2>

{% if error %}
    <p style="color: red;">{{ error }}</p>
{% endif %}

<form method="post">
    {% csrf_token %}

    <p>
        <label>用户名:</label>
        <input type="text" name="username">
    </p>

    <p>
        <label>密码:</label>
        <input type="password" name="password">
    </p>

    <button type="submit">登录</button>
</form>

</body>
</html>

这里最重要的是:

<input type="text" name="username">
<input type="password" name="password">

因为后端会通过这两个名字拿到用户名和密码。


三、登录地址对应的路由

文件是:

user/urls.py

完整内容:

from django.urls import path
from . import views


urlpatterns = [
    path("login/", views.user_login, name="login"),
    path("logout/", views.user_logout, name="logout"),

    path("no-role/", views.no_role_page, name="no_role"),

    path("admin-page/", views.admin_page, name="admin_page"),
    path("editor-page/", views.editor_page, name="editor_page"),
    path("viewer-page/", views.viewer_page, name="viewer_page"),
]

这一行表示:

path("login/", views.user_login, name="login")

当你访问:

/user/login/

就会执行:

views.user_login

四、项目主路由

文件是:

DjangoProject_RBAC/urls.py

内容是:

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path("admin/", admin.site.urls),
    path("user/", include("user.urls")),
]

这里的意思是:

/admin/ 交给 Django 后台处理
/user/  交给 user/urls.py 处理

所以:

/user/login/

实际匹配流程是:

DjangoProject_RBAC/urls.py
↓
path("user/", include("user.urls"))
↓
user/urls.py
↓
path("login/", views.user_login)

五、登录视图逻辑

核心文件是:

user/views.py

完整代码如下:

from functools import wraps

from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import redirect, render


def role_required(role_name):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            user = request.user

            if not user.is_authenticated:
                return redirect("/user/login/")

            if not user.role:
                raise PermissionDenied("你还没有分配角色")

            if user.role.name != role_name:
                raise PermissionDenied("你没有权限访问这个页面")

            return view_func(request, *args, **kwargs)

        return wrapper

    return decorator


def get_role_redirect_url(user):
    """
    根据用户角色返回跳转地址
    """

    if not user.role:
        return "/user/no-role/"

    if user.role.name == "admin":
        return "/user/admin-page/"

    if user.role.name == "editor":
        return "/user/editor-page/"

    if user.role.name == "viewer":
        return "/user/viewer-page/"

    return "/user/no-role/"


def user_login(request):
    """
    登录页面:
    GET  请求:显示登录页面
    POST 请求:校验用户名和密码
    """

    if request.user.is_authenticated:
        return redirect(get_role_redirect_url(request.user))

    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")

        user = authenticate(
            request,
            username=username,
            password=password,
        )

        if user is not None:
            login(request, user)
            return redirect(get_role_redirect_url(user))

        return render(
            request,
            "user/login.html",
            {
                "error": "用户名或密码错误",
            },
        )

    return render(request, "user/login.html")


def user_logout(request):
    logout(request)
    return redirect("/user/login/")


@login_required
def no_role_page(request):
    return HttpResponse("当前用户还没有分配角色,请联系管理员")


@login_required
@role_required("admin")
def admin_page(request):
    return render(request, "user/admin_page.html")


@login_required
@role_required("editor")
def editor_page(request):
    return render(request, "user/editor_page.html")


@login_required
@role_required("viewer")
def viewer_page(request):
    return render(request, "user/viewer_page.html")

六、登录函数的执行流程

登录页面第一次打开时,是 GET 请求:

访问 /user/login/
↓
执行 user_login(request)
↓
request.method 不是 POST
↓
返回 login.html

对应代码:

return render(request, "user/login.html")

当你输入用户名密码并点击登录时,是 POST 请求:

输入 username 和 password
↓
点击登录
↓
提交 POST 请求到 /user/login/
↓
后端拿到 username 和 password
↓
authenticate 校验账号密码

对应代码:

username = request.POST.get("username")
password = request.POST.get("password")

user = authenticate(
    request,
    username=username,
    password=password,
)

如果账号密码错误:

return render(
    request,
    "user/login.html",
    {
        "error": "用户名或密码错误",
    },
)

页面会显示:

用户名或密码错误

如果账号密码正确:

login(request, user)
return redirect(get_role_redirect_url(user))

意思是:

让用户登录
↓
根据角色跳转到对应页面

七、根据角色自动跳转

这个函数决定登录后去哪里:

def get_role_redirect_url(user):
    if not user.role:
        return "/user/no-role/"

    if user.role.name == "admin":
        return "/user/admin-page/"

    if user.role.name == "editor":
        return "/user/editor-page/"

    if user.role.name == "viewer":
        return "/user/viewer-page/"

    return "/user/no-role/"

所以跳转规则是:

用户角色 登录后跳转
admin /user/admin-page/
editor /user/editor-page/
viewer /user/viewer-page/
没有角色 /user/no-role/

例如你的 admin 用户角色是:

管理员 admin

所以登录成功后会跳到:

/user/admin-page/

八、权限装饰器的作用

这个函数是权限控制核心:

def role_required(role_name):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            user = request.user

            if not user.is_authenticated:
                return redirect("/user/login/")

            if not user.role:
                raise PermissionDenied("你还没有分配角色")

            if user.role.name != role_name:
                raise PermissionDenied("你没有权限访问这个页面")

            return view_func(request, *args, **kwargs)

        return wrapper

    return decorator

它的作用是:

检查当前用户是否登录
检查当前用户是否有角色
检查当前用户角色是否匹配页面要求

例如管理员页面:

@login_required
@role_required("admin")
def admin_page(request):
    return render(request, "user/admin_page.html")

意思是:

必须登录
并且角色必须是 admin
才能访问 admin_page

九、三个独立页面

1. 管理员页面

文件:

user/templates/user/admin_page.html

内容:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>管理员页面</title>
</head>
<body>

<h2>管理员页面</h2>

<p>当前用户:{{ request.user.username }}</p>
<p>当前角色:{{ request.user.role }}</p>

<p>这是管理员专属页面,只有 admin 可以访问。</p>

<a href="/user/logout/">退出登录</a>

</body>
</html>

对应视图:

@login_required
@role_required("admin")
def admin_page(request):
    return render(request, "user/admin_page.html")

2. 编辑员页面

文件:

user/templates/user/editor_page.html

内容:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>编辑员页面</title>
</head>
<body>

<h2>编辑员页面</h2>

<p>当前用户:{{ request.user.username }}</p>
<p>当前角色:{{ request.user.role }}</p>

<p>这是编辑员专属页面,只有 editor 可以访问。</p>

<a href="/user/logout/">退出登录</a>

</body>
</html>

对应视图:

@login_required
@role_required("editor")
def editor_page(request):
    return render(request, "user/editor_page.html")

3. 查看者页面

文件:

user/templates/user/viewer_page.html

内容:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>查看者页面</title>
</head>
<body>

<h2>查看者页面</h2>

<p>当前用户:{{ request.user.username }}</p>
<p>当前角色:{{ request.user.role }}</p>

<p>这是查看者专属页面,只有 viewer 可以访问。</p>

<a href="/user/logout/">退出登录</a>

</body>
</html>

对应视图:

@login_required
@role_required("viewer")
def viewer_page(request):
    return render(request, "user/viewer_page.html")

十、退出登录流程

退出地址是:

/user/logout/

路由是:

path("logout/", views.user_logout, name="logout")

视图是:

def user_logout(request):
    logout(request)
    return redirect("/user/login/")

流程是:

点击退出登录
↓
访问 /user/logout/
↓
Django 清除当前登录状态
↓
跳回 /user/login/

十一、用户和角色的数据库关系

你现在的模型是:

User 用户
Role 角色

一个用户对应一个角色。

数据库关系:

role 表
    id
    name
    desc

user 表
    id
    username
    password
    email
    phone
    role_id

关系是:

user.role_id -> role.id

例如:

role 表:
id    name
1     admin
2     editor
3     viewer

user 表:
id    username    role_id
1     admin       1

所以:

admin 用户 -> role_id = 1 -> admin 角色

十二、后台操作流程

你现在在 Django 后台做了这些事:

1. 创建超级管理员 admin
2. 创建三个角色:
   admin
   editor
   viewer
3. 给 admin 用户分配 admin 角色

后台地址:

/admin/

角色管理:

Role / 角色

用户管理:

User / 用户

十三、完整登录访问流程

以 admin 用户为例:

浏览器访问:
/user/login/

↓ 输入账号密码

username = admin
password = 你的密码

↓ 点击登录

POST /user/login/

↓ 后端执行

user_login(request)

↓ 校验账号密码

authenticate(request, username, password)

↓ 登录成功

login(request, user)

↓ 判断角色

user.role.name == "admin"

↓ 自动跳转

/user/admin-page/

↓ 进入管理员页面

admin_page(request)

↓ 权限判断

@role_required("admin")

↓ 判断通过

渲染 admin_page.html

↓ 页面显示

管理员页面
当前用户:admin
当前角色:管理员

十四、如果访问错误页面会怎样

如果当前用户是 admin,但是访问:

/user/editor-page/

流程是:

访问 editor_page
↓
@role_required("editor")
↓
当前用户角色是 admin
↓
admin != editor
↓
抛出 PermissionDenied
↓
显示 403 Forbidden

这说明权限控制生效了。


十五、当前项目核心文件

你现在这套登录和权限跳转,主要靠这些文件:

DjangoProject_RBAC/urls.py
DjangoProject_RBAC/settings.py

user/models.py
user/admin.py
user/views.py
user/urls.py

user/templates/user/login.html
user/templates/user/admin_page.html
user/templates/user/editor_page.html
user/templates/user/viewer_page.html

role/models.py
role/admin.py

十六、最终效果总结

你现在已经实现了:

1. 用户访问登录页面
2. 输入账号密码
3. Django 验证用户
4. 登录成功后判断用户角色
5. admin 自动进入管理员页面
6. editor 自动进入编辑员页面
7. viewer 自动进入查看者页面
8. 不同角色不能访问别人的页面
9. 点击退出登录返回登录页
10. 用户和角色数据存储在 MySQL

也就是一个完整的:

Django + MySQL + User + Role + 登录认证 + 角色跳转 + 页面权限控制

系统。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐