前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Django跨域验证及OPTIONS请求

Django跨域验证及OPTIONS请求

作者头像
星星在线
发布2020-05-22 14:58:55
2.9K0
发布2020-05-22 14:58:55
举报
文章被收录于专栏:python爬虫实战之路

最近做的一个需求是:有两个后端服务器,一个是老项目(django),一个是新项目(djangorestframework),老项目不能做大的改动,只能在新项目进行修改,并且前端只能使用老项目的。老项目的登录认证是最简单的方式:数据库保存账号密码,登录时发送账号密码,检测是否正常,即算是登录成功。新后端是使用jwt认证方式,使用Django用户模块保存用户信息。以上是需求的前提,现在要做的是在老项目登录成功后,在前端请求新项目接口也能通过用户认证。

使用cookies保存jwt认证token

在老项目的登录接口中,使用requests方式向新后端发送一个登录请求,将返回的token设置到cookies中

代码语言:javascript
复制
def login(request):
    if request.method == 'POST':
        user = User.objects.filter(username=username, passowrd=password)
        ......
        res = requests.get('http://www.abc.com/login', data=data)
        response = HttpResponse({'result': 'success', 'message': '登录成功'})
        if res:
               response.set_cookie('access-token', res.get('token'))
        return response

前端将token通过header传递到新后端

前端跨域请求

代码语言:javascript
复制
$.ajax({
    type: 'GET',
    url: 'http://www.abc.com/user/info',
    success: function (data) {
        console.log(data);
    }
});

在进行跨域请求的时候,我们必须将jwt生成的token传递到后端,这里我使用$.ajaxSetup进行全局拦截,给所有进行跨域请求的header上增加access-token,并在后端获取后,使用jwt进行验证。

代码语言:javascript
复制
$.ajaxSetup({
    beforeSend: function (xhr, settings) {
        if (settings.url.indexOf('www.abc.com') != -1) {
            xhr.setRequestHeader('access-token', 'Token1111111')
        }
   }
});

但是请求的时候报错了,报错如下:

跨域验证失败

这里错误的意思是token在Access-Control-Allow-Headers中不识别,我们在使用Django跨域验证时,使用的是django-cors-headers库,其中有一个配置项:CORS_ALLOW_HEADERS

这里的配置是允许跨域验证的headers,我们在前端请求拦截里给headers中增加了token这个项,因此要在这个配置增加一下

获取数据成功,并可以在前端显示

后端JWT认证

代码语言:javascript
复制
class JwtAuthentication(BaseAuthentication):
    def authenticate(self, request):
        access_token = request.META.get('HTTP_TOKEN', None)
        if access_token:
            jwt = JwtUtil()
            data = jwt.check_jwt_token(access_token)
            if data:
                username = data.get('username')
                telephone = data.get('telephone')
                exp = data.get('exp')
                if time.time() > exp:
                    raise AuthenticationFailed('authentication time out')

                try:
                    user = UserInfo.objects.get(Q(username=username) | Q(telephone=telephone))
                    _thread_local.user = user
                except (UserInfo.DoesNotExist, UserInfo.MultipleObjectsReturned) as e:
                    return (None, None)
                else:
                    return (user, None)

        raise AuthenticationFailed('authentication failed')

获取headers中的HTTP_TOKEN信息,进行jwt认证处理即可

在进行以上处理的时候,我们发现了以下问题 为什么会多了一个OPTIONS请求

代码语言:javascript
复制
Request URL: http://127.0.0.1:8000/info
Request method: OPTIONS
Status Code: 200 OK
Remote Address: 127.0.0.1:8000
Referrer Policy: no-referrer-when-downgrade

看到这里的时候一脸懵逼,为什么捏?于是乎各种搜索 AJAX中出现OPTIONS请求 最全的Ajax跨域详解 跨域资源共享CORS详解 通过以上几篇文章,我知道为什么会变成OPTIONS请求?因为我们增加了自定义的header,所以请求变成了非简单请求。非简单请求和CORS请求会在证实通信之前,增加一次HTTP查询请求,成为“预检”请求(preflight request)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP方法和头信息字段,只有得到肯定答复,浏览器才会发出真实的XMLHttpRequest请求,否则就报错。 OPTIONS请求的Response信息如下:

代码语言:javascript
复制
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with
Access-Control-Allow-Methods: DELETE, GET, OPTIONS, PATCH, POST, PUT
Access-Control-Allow-Origin: 127.0.0.1:8080
Access-Control-Max-Age: 86400
Content-Length: 0
Content-Type: text/html; charset=utr-8

Access-Control-Allow-Headers显示了后端支持的所有头信息 Access-Control-Allow-Methods显示了后端支持的所有请求类型 而且前端也报了如下错误:

代码语言:javascript
复制
Access to XMLHttpRequest at 'http://127.0.0.1:8000/info' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Request header field access-token is not allowed by Access-Control-Allow-Headers in preflight response.

错误的意思是Access-Control-Allow-Headers不支持access-token头部字段。再看上面OPTIONS请求的返回值,可以知道确实是不支持access-token。只要在后端增加配置即可:

代码语言:javascript
复制
CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'access-token'
)

再次请求发现出现两个请求,第一个为OPTIONS请求,第二个为正常访问的请求。第二个请求头出现了我们发送的access-token信息。

代码语言:javascript
复制
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN, zh; q=0.9,en;q=0.8
access-token: token11111111111,
Cache-Control: no-cache

在后端获取头部信息,进行JWT校验

代码语言:javascript
复制
class JwtAuthentication(BaseAuthentication):
    def authenticate(self, request):
        access_token = request.META.get('HTTP_ACCESS_TOKEN')
        if access_token and access_token != 'null':
            try:
                jwt = JwtUtils() # 这是自己写的Jwt验证类
                token_info = jwt.check_jwt(access_token)
                username = token_info.get('username')
                try:
                    user = TblUser.objects.get(username=username)
                except TblUser.DoesNotExist as e:
                    LOG.exception(e)
                else:
                    return (user, None)
            except Exception as e:
                LOG.exception(e)
        raise APIException('Authentication failed')

PS: Django对于header的处理,处理特殊的header项,一般的都使用一下方式处理:

  • 所有字符转大写
  • 中划线-变为下划线_
  • 前面增加HTTP_ 因此access-token在后端变成HTTP_ACCESS_TOKEN来获取信息
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 python爬虫实战之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用cookies保存jwt认证token
  • 前端将token通过header传递到新后端
  • 后端JWT认证
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档