本文介绍如何使用 Django 的 @login_required 装饰器,强制所有敏感页面(如书籍详情、书单列表等)仅对已登录用户开放,彻底防止通过 URL 直接访问导致的权限绕过问题。
本文介绍如何使用
django
的 `@login_required` 装饰器,强制所有敏感页面(如书籍详情、书单列表等)仅对已登录用户开放,彻底防止通过 url 直接访问导致的权限绕过问题。
在当前的 Django 项目中,虽然已实现登录逻辑(signin 视图处理认证),但所有视图函数(如 index、get_book、get_books 等)均未进行访问控制校验。这导致用户无需登录即可通过直接输入 URL(例如 /books 或 /book/5)访问全部内容,严重违反权限最小化原则。
解决该问题最标准、最可靠的方式是使用 Django 内置的 @login_required 装饰器。它会自动拦截未认证请求,并重定向至登录页(默认为 /accounts/login/)。你需要首先确保 settings.py 中已正确配置登录重定向路径:
接着,在所有需登录才能访问的视图函数上方添加装饰器。注意:
index 视图作为首页必须受保护
(当前却被设为登录入口,逻辑矛盾),而 signin、registration、signout 等认证相关视图则
不应加装饰器
(否则将陷入重定向死循环)。
✅ 正确改造示例(修改 views.py):
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
⚠️ 重要注意事项:
@login_required 默认重定向到 /accounts/login/,若你的登录 URL 是 store:signin,务必在 settings.py 中显式设置 LOGIN_URL = 'store:signin';
装饰器
必须放在 def 语句正上方
,且不能与 @csrf_protect 等其他装饰器顺序冲突(Django 会自动处理 CSRF,通常无需手动加);
若需更细粒度控制(如区分管理员/普通用户),应结合 User.is_staff、自定义权限或 @user_passes_test;
前端导航栏中,建议动态渲染登录/登出链接(利用 request.user.is_authenticated),提升用户体验。
通过以上改造,任何未登录用户尝试访问 /books、/book/1 等路径时,将被自动重定向至登录页,从根本上堵住 URL 直接访问的安全漏洞,符合 Django 官方推荐的最佳实践。
# settings.py
LOGIN_URL = 'store:signin' # 指向你的登录视图名称
LOGIN_REDIRECT_URL = 'store:index' # 登录后跳转首页from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from .models import Category, Writer, Book, Review, Slider
from .forms import RegistrationForm, ReviewForm
# ✅ 登录页:不加装饰器(允许匿名访问)
def signin(request):
if request.user.is_authenticated:
return redirect('store:index')
if request.method == "POST":
user = request.POST.get('user')
password = request.POST.get('pass')
auth = authenticate(request, username=user, password=password)
if auth is not None:
login(request, auth)
return redirect('store:index')
else:
messages.error(request, '用户名或密码错误')
return render(request, "store/login.html")
# ✅ 注册页:不加装饰器
def registration(request):
form = RegistrationForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('store:signin')
return render(request, 'store/signup.html', {"form": form})
# ✅ 退出登录:不加装饰器(但建议 POST 验证 CSRF)
def signout(request):
logout(request)
return redirect('store:index')
# ✅ 首页(原 index):必须加 @login_required!
@login_required
def index(request):
newpublished = Book.objects.order_by('-created')[:15]
slide = Slider.objects.order_by('-created')[:3]
context = {
"newbooks": newpublished,
"slide": slide
}
return render(request, 'store/index.html', context)
# ✅ 所有业务页面均需保护
@login_required
def get_book(request, id):
# ...(原有逻辑保持不变)
book = get_object_or_404(Book, id=id)
# 注意:此处可进一步校验用户权限(如仅作者可编辑),但基础认证已由装饰器保障
# ...
@login_required
def get_books(request):
books_ = Book.objects.all().order_by('-created')
paginator = Paginator(books_, 10)
page = request.GET.get('page')
books = paginator.get_page(page)
return render(request, "store/category.html", {"book": books})
@login_required
def get_book_category(request, id):
book_ = Book.objects.filter(category_id=id)
paginator = Paginator(book_, 10)
page = request.GET.get('page')
book = paginator.get_page(page)
return render(request, "store/category.html", {"book": book})
@login_required
def get_writer(request, id):
wrt = get_object_or_404(Writer, id=id)
book = Book.objects.filter(writer_id=wrt.id)
return render(request, "store/writer.html", {"wrt": wrt, "book": book})