ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

简单的Django auth + React 跨域cookie登录

2022-03-04 13:33:23  阅读:273  来源: 互联网

标签:axios 跨域 登录 request auth Django cookie login response


把原来的代码,用React后端分离来重构,踩了挺多坑的,特别是这个登录功能,坑太多了。

登录接口

首先是后端的登录接口,这个简单,用的是django自带的auth库。迁移数据库的时候,会自动生成一张用户表,可以通过admin后台来管理用户,这是django的基础,就不多说了。

def login_react(request):
    from django.contrib import auth
    data = json.loads(request.body)
    username = data.get('username', '')
    password = data.get('password', '')
    user_object = auth.authenticate(username=username, password=password)
    if user_object:
        # 登录成功为止
        auth.login(request, user_object)
        request.session['user'] = username
        return JsonResponse({'code': 0, 'msg': '登录成功'})
    return JsonResponse({'code': -1, 'msg': '用户名或密码错误'})

跨域解决

跨域一般后端解决,解决起来也简单,两种方式。

一、自定义中间件

第一种是在每个请求的响应头加上跨域字段,可以手写一个中间件,类似这种:

from django.utils.deprecation import MiddlewareMixin


class HttpResponseCustomHeader(MiddlewareMixin):
    # 全局修改响应header
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin'] = '*'
        if request.method == 'OPTIONS':
            response["Access-Control-Allow-Headers"] = "Content-Type"
            response["Access-Control-Allow-Methods"] = "PUT,PATCH,DELETE"
        return response

二、django-cors-headers第三方库

第二种可以用一个第三方库:

pip install django-cors-headers

后面配置一下settings.py文件,网上很多教程,一搜一大把。


axios登录请求

大概就是这样:

$axios.post(
  'http://127.0.0.1/login/',
  {
    username: xxxx,
    password: xxxx,
  },
).then((res) => {
  if (res.data.code === 0) {
    // 跳转到首页
  } else {
    // 登录失败的处理
  }
})

坑一、set-cookie黄色感叹号

第一个坑出现了,用Django自带的auth登录成功,返回的response header会自动写入一个set-cookie字段,浏览器接到响应之后,会将set-cookie中的值写入浏览器,这是cookie登录的原理。

抓包看一下,登录成功之后,响应头里明明有set-cookie字段,但是cookie却没有写入,提示SameSite=Lax时,set-cookie行为被禁止了。

image-20220304123240953

然后百度,中间踩了许多坑:

1、后端返回时,将SameSite字段设置为None,这个做法不成功。直接配置corsheaders中间件,将SESSION_COOKIE_SAMESITE = None CSRF_COOKIE_SAMESITE = None ,不生效,如果设置成'None',后端会报错,显示:SameSite字段只能是Lax或者Strict;

2、手写中间件,篡改响应头的SameSite字段,这个改成功了,返回的set-cookie字段里面确实没有SameSite字段了,但是还是标黄色感叹号,没写入成功;

3、浏览器输入chrome://flags/,将SameSite by default cookies 和 Cookies without SameSite must be secure设置为disable,实测失败;

正确的解决方式,有两种:

1、axios请求时,把127.0.0.1,替换成localhost;

2、使用corsheaders中间件,在settings.py中加入这两个字段:SESSION_COOKIE_SECURE = True SESSION_COOKIE_SAMESITE = 'None',同时登录接口使用https请求。


坑二、set-cookie仍无法写入

我在本地用的是第一种方式,直接将127.0.0.1,替换成localhost,但是这样还是不行,因为axios请求默认是不写入、不携带cookie的,需要在请求中加入一个字段withCredentials: true,因为之后所有的请求都需要携带cookie,所以这里要封装一下axios:

import axios from 'axios'

const service = axios.create({
  baseURL: 'http://localhost:8000',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json; charset=UTF-8;',
  },
  withCredentials: true
})
export default service

在登录组件中调用:

import $axios from './request/index'

$axios.post(
  '/login/',
  {
    username: values.username,
    password: values.password,
  }
).then((res) => {
  if (res.data.code === 0) {
    // 跳转到首页
  } else {
    // 登录失败的处理
  }
})

大功告成,可以看到set-cookie后面的黄色感叹号消失,sessionId也成功写入:

image-20220304130310900

image-20220304130325714


其他携带cookie的请求

这个就比较简单了,后端在接到请求之后,如果校验cookie不正确,就给重定向到一个接口,这个接口返回一个登录不成功的信息,然后axios接受到这个信息,就给重定向到登录页。

一、后端处理

新开一个接口:no_login

def no_login(request):
    return JsonResponse({'code': -1, 'msg': '未登录'})

在需要登录的接口上加一个装饰器:@login_required(login_url='/no_login'),如:

@login_required(login_url='/no_login')
def login_out(request):
    from django.contrib import auth
    auth.logout(request)
    return JsonResponse({'code': 0, 'msg': '退出登录成功'})

这样,如果前端请求login_out接口时,django会自动校验cookie,如果校验失败,就重定向到no_login接口,由no_login接口返回错误信息给前端

另:上面示例的是退出登录接口,在执行完auth.logout(request)后的响应,django会自动构造响应头,将set-cookie字段中的sessionId设为空,这样浏览器接到请求后,就会将sessionId从网站的cookie中移除了。


二、前端处理

前端不可能每次发请求都判断一下后端的msg是不是“未登录”,所以还得去封装axios:

import axios from 'axios'

const service = axios.create({
  baseURL: 'http://localhost:8000',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json; charset=UTF-8;',
  },
  withCredentials: true
})

// 添加对响应的处理
service.interceptors.response.use((response) => {
  if (response.data.msg === '未登录') {
    window.location.href = '/login/'
  }else {
    return response
  }
})

export default service

这样就ok了。


个人踩坑过来的,不知道是否是最佳实践,酌情使用哈,有错误之处欢迎大家批评指正。

标签:axios,跨域,登录,request,auth,Django,cookie,login,response
来源: https://www.cnblogs.com/bigllxx/p/15963891.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有