AngularJS前端POST请求Django接口返回403错误排查
问题定位
Postman可正常响应、Angular前端POST返回403,核心是配置错误、自定义中间件逻辑异常、前后端CSRF规则不匹配三类问题,和基础CORS开关是否打开无关。
具体错误点
- 存在无效配置项:
CSRF_WHITELIST_ORIGINS是Django不存在的配置参数,框架不会读取该配置,写了也不生效。 - CSRF配置混淆Cookie名和请求头名:将
CSRF_COOKIE_NAME(Django写入客户端的CSRF校验Cookie的名称,默认值为csrftoken)错误设置为X-CSRFToken——后者是前端发请求时携带CSRF值的请求头名称,二者搞混会导致前端无法读取到正确的CSRF校验值。 - 缺少跨域凭证配置:未开启
CORS_ALLOW_CREDENTIALS,跨域场景下浏览器不会向前端发送Cookie、也不会允许前端携带Cookie发起请求,直接导致CSRF校验、会话认证失败。 - 自定义会话中间件逻辑错误:
- 会话续期逻辑写反:注释说明要给已认证用户延长会话,但实际
set_expiry(86400)的续期逻辑写在了未认证用户的else分支,已登录用户根本不会触发续期。 - 非法调用CSRF中间件:在自定义中间件中手动实例化
CsrfViewMiddleware时传入字符串'get_response'作为参数,该参数要求是可调用的请求处理链对象,传字符串会直接触发运行异常,干扰Django默认的全局CSRF校验流程。Django默认已全局加载CSRF中间件,不需要在自定义业务中间件中重复调用。
- 会话续期逻辑写反:注释说明要给已认证用户延长会话,但实际
- 前后端CSRF规则不匹配:AngularJS默认读取的CSRF Cookie名为
XSRF-TOKEN,发起请求时默认携带的CSRF请求头名为X-XSRF-TOKEN,和Django默认的Cookie名、要求的请求头名完全不匹配,就算后端配置正确,Angular也不会自动携带正确的CSRF校验值。
Postman不受浏览器同源策略约束,不存在跨域Cookie拦截、自动请求头缺失的限制,所以可以正常响应,和浏览器环境下的前端请求逻辑有本质区别。
修正步骤
1. 修正settings.py配置
删除无效配置,修正错误参数,补全跨域必要配置:
# 跨域白名单配置 CORS_ORIGIN_WHITELIST = [ 'http://localhost:8000', 'http://localhost:8080', ] # 允许跨域请求携带Cookie、认证凭证 CORS_ALLOW_CREDENTIALS = True # 配置跨域允许的请求头,包含CSRF请求头 CORS_ALLOW_HEADERS = [ 'accept', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', ] # CSRF信任源配置,删除无效的CSRF_WHITELIST_ORIGINS参数 CSRF_TRUSTED_ORIGINS = [ 'http://localhost:8000', 'http://localhost:8080', ] # 会话基础配置 SESSION_COOKIE_PATH='/' SESSION_COOKIE_SECURE=False SESSION_COOKIE_DOMAIN=None SESSION_COOKIE_HTTPONLY=False SESSION_EXPIRE_AT_BROWSER_CLOSE = False SESSION_COOKIE_AGE = 86400 # 修正CSRF Cookie配置,Cookie名恢复为默认值csrftoken,不要与请求头名混淆 CSRF_COOKIE_NAME='csrftoken' CSRF_COOKIE_AGE=86400 CSRF_COOKIE_DOMAIN=None CSRF_COOKIE_HTTPONLY=False CSRF_COOKIE_PATH='/' CSRF_COOKIE_SECURE=False
2. 重写自定义会话中间件
删除错误的CSRF手动调用逻辑,修正会话续期的分支判断:
from django.utils.deprecation import MiddlewareMixin class ExtendUserSession(MiddlewareMixin): """ 延长已认证用户的会话有效期,避免用户频繁重新登录 会话有效期取86400秒(1天) """ def process_request(self, request): # 仅对已登录用户刷新会话过期时间 if request.user.is_authenticated: request.session.set_expiry(86400)
将该中间件添加到settings.py的MIDDLEWARE列表中,位置放在SessionMiddleware之后、CsrfViewMiddleware之前,不要打乱Django默认中间件的顺序。
3. 配置AngularJS侧CSRF适配
在AngularJS应用启动配置块中,对齐Django的CSRF规则,开启跨域凭证携带:
angular.module('yourApp', []) .config(['$httpProvider', function($httpProvider) { // 对齐Django侧的CSRF Cookie名 $httpProvider.defaults.xsrfCookieName = 'csrftoken'; // 对齐Django侧要求的CSRF请求头名 $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; // 全局开启跨域请求携带凭证 $httpProvider.defaults.withCredentials = true; }]);
验证操作
所有配置修改完成后,清空浏览器本地存储的当前站点所有Cookie(清除之前错配生成的无效CSRF Cookie、会话Cookie),重启Django服务后重新发起请求即可正常响应。
内容的提问来源于stack exchange,提问作者Roronoa_Z
相关产品推荐
相关产品推荐

