Django/Apache2部署后登录成功无法访问@login_required视图跳转首页
问题描述
现有一套采用token-based auth的Django/Vue应用,此前在AWS上部署运行无异常,改用Apache2提供服务后,出现登录成功后无法打开login required保护页面的问题。
输入用户名密码点击登录后,登录接口正常请求完成,但页面重定向至携带?next=memory参数的首页,服务端未识别用户为已登录状态。登录成功后已将token以apitoken为键存入cookie。
相关代码如下:
views.py
def index(request): print('Access to page "home"') return render(request, 'index.html') @login_required(login_url='/') def memory(request): print('Access to page "memory"') return render(request, 'memory.html')
user_api.py
@api_view(["POST"]) @permission_classes((AllowAny,)) @csrf_exempt def doLogin(request): body_unicode = request.body.decode('utf-8') user = json.loads(body_unicode) dbUser = User.objects.filter(username=user['username']) if not dbUser: return HttpResponse(status=404) authenticatedUser = authenticate(username=user['username'], password=user['password']) if authenticatedUser: login(request, authenticatedUser) token, _ = Token.objects.get_or_create(user=authenticatedUser) return Response({'token': token.key}, status=HTTP_200_OK) else: return Response(status=401)
LoginModal.vue
login() { if (!this.user.username || this.user.username == '' || !this.user.password || this.user.password == '') { this.warningText = 'All fields must be filled.' return } axios.post(this.backendAddress + 'api/user/login', this.user) .then((response) => { sessionStorage.setItem('token', response.data.token) document.cookie = 'apitoken=' + response.data.token window.location.href = 'memory' }, function (err) { printError(err) }) }
问题根因&修复方案
核心问题是认证逻辑混用+部署配置不匹配,具体点如下:
- 装饰器认证逻辑不匹配:
@login_required是Django原生session认证的装饰器,它只会读取请求携带的sessionidcookie,从服务端session里读取用户登录态,完全不会识别你手动存在cookie里的apitoken字段,也不会读你存在sessionStorage里的token。 - 整页跳转的请求特性:你用
window.location.href = 'memory'触发的是浏览器原生整页跳转,这类请求由浏览器直接发起,不会自动携带sessionStorage里存储的token,只会自动携带匹配当前域名、路径、安全策略的cookie。 - Apache部署配置问题:之前AWS部署能正常运行,大概率是因为当时服务端返回的
sessionidcookie能被浏览器正常存储;换成Apache后常见问题是WSGI配置缺失、cookie属性不匹配导致sessionid没被浏览器保存:- Apache默认会剥离请求的
Authorization头不传给WSGI应用,如果后续要走token认证需要加对应配置 - 若站点是HTTPS部署,Django的
SESSION_COOKIE_SECURE、CSRF_COOKIE_SECURE配置和实际协议不匹配、cookie的Domain/Path/SameSite属性配置错误,都会导致浏览器不存储登录接口返回的sessionid,后续跳转请求不带session,自然被判定为未登录。
- Apache默认会剥离请求的
- 你代码里手动写
apitokencookie的操作对当前的@login_required鉴权完全无效,属于冗余操作。
修复方案
根据你的认证选型二选一即可:
方案1:保留页面路由用session鉴权(适配现有代码改动最小)
- 给Apache的虚拟主机配置加上
WSGIPassAuthorization On,避免认证头被Apache拦截 - 打开浏览器开发者工具-应用面板,检查登录接口返回的
Set-Cookie头,确认sessionid的Domain、Path、SameSite、Secure属性和当前访问的站点匹配:如果是HTTP站点不要开SESSION_COOKIE_SECURE = True,cookie域要配置为当前访问的域名,不要出现跨域、跨路径写cookie失败的问题 - 删掉前端手动写
apitokencookie的冗余代码,只要sessionid能正常被浏览器存储,@login_required就能正常识别登录态
方案2:全链路使用Token认证
- 不要给返回模板页面的视图用Django原生
@login_required装饰器,自定义鉴权装饰器,从请求的cookie或者头里读取token做校验 - 注意:原生整页跳转的请求无法自定义添加
Authorization请求头,如果要靠cookie传token,需要修改DRF的TokenAuthentication逻辑,让它主动读取你存在cookie里的apitoken字段做校验,否则token不会被识别
内容的提问来源于stack exchange,提问作者cuneyttyler
相关产品推荐
相关产品推荐

