Django @login_required未重定向指定页面,用户编辑页权限异常排查
问题分析与修复方案
我来帮你梳理下代码里的核心问题,以及对应的修复方法,解决你的权限控制失效问题:
1. 最关键的漏洞:未校验登录用户与编辑页的user_id一致性
你用了@login_required装饰器,这只能确保访问页面的用户已经登录,但完全没限制用户只能访问自己的编辑页。所以登录后的用户只要改一下URL里的user_id,就能随便访问别人的页面,这是最核心的问题。
2. 路由与视图的小漏洞:无效URL能访问
你提到像http://127.0.0.1:8000/user//edit这种无效URL也能打开编辑页,大概率是因为:
- Django默认会把URL里的多个斜杠合并成一个,所以
/user//edit等价于/user/edit,而你的路由正则r'^(?P<user_id>\d+)/edit$要求user_id是至少一个数字,理论上这个URL应该返回404,但实际能打开的话,要么是你写错了正则(比如用了\d*而不是\d+),要么是视图里没校验user_id的有效性,空值也能渲染模板。
具体修复步骤
第一步:给user_edit视图加身份校验逻辑
在user_edit里加上代码,确保当前登录用户的ID和URL里的user_id完全一致,不一致就拒绝访问或者跳转到自己的编辑页:
from django.contrib.auth.decorators import login_required from django.shortcuts import redirect, get_object_or_404 from django.contrib.auth.models import User from django.http import HttpResponseForbidden @login_required(login_url='/user/login/') def user_edit(request, user_id): # 先把user_id转成整数,避免字符串匹配的坑 try: target_user_id = int(user_id) except ValueError: # 如果user_id不是有效数字,直接返回404或者跳转到登录页 return HttpResponseForbidden("无效的用户ID") # 核心校验:当前登录用户只能访问自己的编辑页 if request.user.id != target_user_id: # 两种处理方式选其一: # 1. 跳转到当前用户自己的编辑页 return redirect('user-edit', user_id=request.user.id) # 2. 返回403禁止访问页面 # return HttpResponseForbidden("你没有权限访问此页面") # 如果需要操作用户实例,用get_object_or_404确保用户存在 target_user = get_object_or_404(User, id=target_user_id) print(request, request.user) print("next is {}".format(request.GET.get('next'))) return render(request, 'user_edit.html', {'userid': target_user_id, 'user': request.user})
第二步:修复路由正则,严格匹配有效ID
确保你的路由正则只匹配数字ID,并且和实际访问的URL格式一致(比如你的示例URL是/user/7/edit,路由里要对应好斜杠):
# urls.py from django.conf.urls import url from . import views urlpatterns = [ url(r'^login/$', views.user_login, name='user-login'), # 严格匹配数字ID,结尾加上斜杠和$确保没有多余字符 url(r'^(?P<user_id>\d+)/edit/$', views.user_edit, name='user-edit'), ]
第三步:优化登录后的跳转逻辑(可选但更友好)
你的user_login视图现在登录成功后直接跳转到用户自己的编辑页,但如果用户是从其他需要登录的页面跳转过来的,应该优先回到原页面。修改一下:
from django.urls import reverse def user_login(request): login_form = AuthenticationForm(request.POST or None) if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user: login(request, user) # 先取next参数,没有的话再跳转到自己的编辑页 next_url = request.GET.get('next') or reverse('user-edit', args=[user.id]) return redirect(next_url) else: # 登录失败别直接重定向,返回登录页并提示错误 return render(request, 'user_login.html', {'form': login_form, 'error': '用户名或密码错误'}) return render(request, 'user_login.html', {'form': login_form})
额外推荐:用Django类视图更规范地实现
如果想减少手动编写的代码,推荐用Django的通用类视图,自带权限控制的Mixin,代码更简洁不易出错:
# views.py from django.views.generic.edit import UpdateView from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.contrib.auth.models import User from django.urls import reverse_lazy class UserEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): model = User # 这里放你要让用户编辑的字段,比如用户名、邮箱 fields = ['username', 'email'] template_name = 'user_edit.html' # 编辑成功后跳转的页面,比如用户自己的编辑页 def get_success_url(self): return reverse_lazy('user-edit', kwargs={'pk': self.request.user.id}) def test_func(self): # 校验当前用户是否是要编辑的用户 return self.request.user == self.get_object() def get_login_url(self): return '/user/login/'
对应的路由改成:
url(r'^(?P<pk>\d+)/edit/$', views.UserEditView.as_view(), name='user-edit'),
这样就不用自己写校验逻辑,Django会帮你处理好登录校验和用户权限校验。
内容的提问来源于stack exchange,提问作者Aleksey Sidorov
相关产品推荐
相关产品推荐

