ICode9

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

Flask 学习-31.flask_jwt_extended 验证token四种方headers/cookies/json/query_string

2022-09-01 13:34:29  阅读:239  来源: 互联网

标签:Content cookies extended string jwt json access token


前言

用户携带授权token访问时,其jwt的所处位置列表,默认是在请求头部headers中验证。
可以通过JWT_TOKEN_LOCATION进行全局配置,设置token是在请求头部,还是cookies,还是json, 还是查询参数query_string 四种方式。

JWT_TOKEN_LOCATION 全局配置

JWT_TOKEN_LOCATION 配置参数可以全局配置允许JWT执行以下操作的所有方式,发送到您的web应用程序。默认情况下,这将仅为headers

app.config["JWT_TOKEN_LOCATION"] = ["headers", "cookies", "json", "query_string"]

或者在 config.py 配置文件中

    # jwt 四种认证方式
    JWT_TOKEN_LOCATION = ["headers", "cookies", "json", "query_string"]

JWT 可以通过多种不同方式随请求一起发送。JWT_TOKEN_LOCATION 您可以通过配置选项控制您希望在 Flask 应用程序中接受 JWT 的方式。您还可以通过locations. jwt_required()
官方文档使用示例

from flask import Flask
from flask import jsonify

from flask_jwt_extended import create_access_token
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager
from flask_jwt_extended import set_access_cookies
from flask_jwt_extended import unset_jwt_cookies

app = Flask(__name__)

# Here you can globally configure all the ways you want to allow JWTs to
# be sent to your web application. By default, this will be only headers.
app.config["JWT_TOKEN_LOCATION"] = ["headers", "cookies", "json", "query_string"]

# If true this will only allow the cookies that contain your JWTs to be sent
# over https. In production, this should always be set to True
app.config["JWT_COOKIE_SECURE"] = False

# Change this in your code!
app.config["JWT_SECRET_KEY"] = "super-secret"

jwt = JWTManager(app)


@app.route("/login_without_cookies", methods=["POST"])
def login_without_cookies():
    access_token = create_access_token(identity="example_user")
    return jsonify(access_token=access_token)


@app.route("/login_with_cookies", methods=["POST"])
def login_with_cookies():
    response = jsonify({"msg": "login successful"})
    access_token = create_access_token(identity="example_user")
    set_access_cookies(response, access_token)
    return response


@app.route("/logout_with_cookies", methods=["POST"])
def logout_with_cookies():
    response = jsonify({"msg": "logout successful"})
    unset_jwt_cookies(response)
    return response


@app.route("/protected", methods=["GET", "POST"])
@jwt_required()
def protected():
    return jsonify(foo="bar")


@app.route("/only_headers")
@jwt_required(locations=["headers"])
def only_headers():
    return jsonify(foo="baz")


if __name__ == "__main__":
    app.run()

测试验证

1.先测试token放到headers的情况,主要是以下2个视图

@app.route("/login_without_cookies", methods=["POST"])
def login_without_cookies():
    access_token = create_access_token(identity="example_user")
    return jsonify(access_token=access_token)


@app.route("/only_headers")
@jwt_required(locations=["headers"])
def only_headers():
    return jsonify(foo="baz")

先登录

POST http://127.0.0.1:5000/login_without_cookies HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:5000
Content-Type: application/json
Content-Length: 0

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Thu, 01 Sep 2022 04:10:32 GMT
Content-Type: application/json
Content-Length: 365
Connection: close

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2MjAwNTQzMiwianRpIjoiYzQzYTAwMTEtMzU5OC00OTYwLTgwYTAtODViMzExMGM3MjRiIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImV4YW1wbGVfdXNlciIsIm5iZiI6MTY2MjAwNTQzMiwiY3NyZiI6IjY4ZjZiOGFlLTcwZjYtNDIwNy05YjU3LWFiZTBmZTVjMzk0MyIsImV4cCI6MTY2MjAwOTAzMn0.ttnNTdyliZXAa2_d9esJeuofHnwEkfHqshJ15zkXKzs"
}

token 放到headers 中验证

GET http://127.0.0.1:5000/only_headers HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:5000
Content-Type: application/json
Content-Length: 0
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2MjAwNTQzMiwianRpIjoiYzQzYTAwMTEtMzU5OC00OTYwLTgwYTAtODViMzExMGM3MjRiIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImV4YW1wbGVfdXNlciIsIm5iZiI6MTY2MjAwNTQzMiwiY3NyZiI6IjY4ZjZiOGFlLTcwZjYtNDIwNy05YjU3LWFiZTBmZTVjMzk0MyIsImV4cCI6MTY2MjAwOTAzMn0.ttnNTdyliZXAa2_d9esJeuofHnwEkfHqshJ15zkXKzs

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Thu, 01 Sep 2022 04:13:14 GMT
Content-Type: application/json
Content-Length: 19
Connection: close

{
  "foo": "baz"
}

访问/protected 地址也是可以的,默认四种验证方式都支持。

2.cookies验证

在web网站中,使用cookies保存token会比较常见,主要用到登录和退出登录方法

@app.route("/login_with_cookies", methods=["POST"])
def login_with_cookies():
    response = jsonify({"msg": "login successful"})
    access_token = create_access_token(identity="example_user")
    set_access_cookies(response, access_token)
    return response


@app.route("/logout_with_cookies", methods=["POST"])
def logout_with_cookies():
    response = jsonify({"msg": "logout successful"})
    unset_jwt_cookies(response)
    return response

访问/login_with_cookies登录后会返回cookies

POST http://127.0.0.1:5000/login_with_cookies HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:5000
Content-Type: application/json
Content-Length: 0

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Thu, 01 Sep 2022 04:16:24 GMT
Content-Type: application/json
Content-Length: 32
Set-Cookie: access_token_cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2MjAwNTc4NCwianRpIjoiZWNhYWViYTEtNDZiNC00ZGM1LWEyMzItMTJjYTcyYTgzN2U3IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImV4YW1wbGVfdXNlciIsIm5iZiI6MTY2MjAwNTc4NCwiY3NyZiI6IjYxNTAwMjk3LTg1ZTctNGY1Zi04Zjk4LTVkZDVlOGY4MTBhZCIsImV4cCI6MTY2MjAwOTM4NH0.B2L-WGf92ZORlcfvx5PqR9XSWx_rm_fSxwmQsHeRBdE; HttpOnly; Path=/
Set-Cookie: csrf_access_token=61500297-85e7-4f5f-8f98-5dd5e8f810ad; Path=/
Connection: close

{
  "msg": "login successful"
}

带上cookie的请求

GET http://127.0.0.1:5000/protected HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:5000
Content-Type: application/json
Content-Length: 0
Cookie: access_token_cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2MjAwNTc4NCwianRpIjoiZWNhYWViYTEtNDZiNC00ZGM1LWEyMzItMTJjYTcyYTgzN2U3IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImV4YW1wbGVfdXNlciIsIm5iZiI6MTY2MjAwNTc4NCwiY3NyZiI6IjYxNTAwMjk3LTg1ZTctNGY1Zi04Zjk4LTVkZDVlOGY4MTBhZCIsImV4cCI6MTY2MjAwOTM4NH0.B2L-WGf92ZORlcfvx5PqR9XSWx_rm_fSxwmQsHeRBdE; HttpOnly; Path=/

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Thu, 01 Sep 2022 04:18:50 GMT
Content-Type: application/json
Content-Length: 19
Connection: close

{
  "foo": "bar"
}

如果是post请求,请求头部还需带上X-CSRF-TOKEN

POST http://127.0.0.1:5000/protected HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:5000
Content-Type: application/json
Content-Length: 0
Cookie: access_token_cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2MjAwNTc4NCwianRpIjoiZWNhYWViYTEtNDZiNC00ZGM1LWEyMzItMTJjYTcyYTgzN2U3IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImV4YW1wbGVfdXNlciIsIm5iZiI6MTY2MjAwNTc4NCwiY3NyZiI6IjYxNTAwMjk3LTg1ZTctNGY1Zi04Zjk4LTVkZDVlOGY4MTBhZCIsImV4cCI6MTY2MjAwOTM4NH0.B2L-WGf92ZORlcfvx5PqR9XSWx_rm_fSxwmQsHeRBdE; HttpOnly; Path=/
X-CSRF-TOKEN: 61500297-85e7-4f5f-8f98-5dd5e8f810ad

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Thu, 01 Sep 2022 04:23:36 GMT
Content-Type: application/json
Content-Length: 19
Connection: close

{
  "foo": "bar"
}

3.json 中带上access_token

POST http://127.0.0.1:5000/protected HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:5000
Content-Type: application/json
Content-Length: 370

{ 
    "access_token" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2MjAwNTc4NCwianRpIjoiZWNhYWViYTEtNDZiNC00ZGM1LWEyMzItMTJjYTcyYTgzN2U3IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImV4YW1wbGVfdXNlciIsIm5iZiI6MTY2MjAwNTc4NCwiY3NyZiI6IjYxNTAwMjk3LTg1ZTctNGY1Zi04Zjk4LTVkZDVlOGY4MTBhZCIsImV4cCI6MTY2MjAwOTM4NH0.B2L-WGf92ZORlcfvx5PqR9XSWx_rm_fSxwmQsHeRBdE"
}

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Thu, 01 Sep 2022 05:04:24 GMT
Content-Type: application/json
Content-Length: 19
Connection: close

{
  "foo": "bar"
}

4.query_string查询字符串

请求参数带上‘jwt=${token}’,示例

GET http://127.0.0.1:5000/protected?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2MjAwNTc4NCwianRpIjoiZWNhYWViYTEtNDZiNC00ZGM1LWEyMzItMTJjYTcyYTgzN2U3IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImV4YW1wbGVfdXNlciIsIm5iZiI6MTY2MjAwNTc4NCwiY3NyZiI6IjYxNTAwMjk3LTg1ZTctNGY1Zi04Zjk4LTVkZDVlOGY4MTBhZCIsImV4cCI6MTY2MjAwOTM4NH0.B2L-WGf92ZORlcfvx5PqR9XSWx_rm_fSxwmQsHeRBdE HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:5000
Content-Type: application/json
Content-Length: 0

HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.8.5
Date: Thu, 01 Sep 2022 05:06:40 GMT
Content-Type: application/json
Content-Length: 19
Connection: close

{
  "foo": "bar"
}

Web 浏览器中使用javascript 获取token

Headers

通过标头工作 JWT 是一个非常简单的过程。您需要做的就是在登录时存储令牌,并在每次向受保护路由发出请求时将令牌添加为标头。注销就像删除令牌一样简单。

async function login() {
  const response = await fetch('/login_without_cookies', {method: 'post'});
  const result = await response.json();
  localStorage.setItem('jwt', result.access_token);
}

function logout() {
  localStorage.removeItem('jwt');
}

async function makeRequestWithJWT() {
  const options = {
    method: 'post',
    headers: {
      Authorization: `Bearer ${localStorage.getItem('jwt')}`,
    }
  };
  const response = await fetch('/protected', options);
  const result = await response.json();
  return result;
}

Cookies

如果您使用 Web 浏览器,Cookie 是处理 JWT 的绝佳方式。与标头方法相比,它们提供了一些不错的好处:它们可以配置为仅通过 HTTPS 发送。这可以防止 JWT 通过不安全的连接意外发送并可能受到损害。
它们存储在一个仅限 http 的 cookie 中,这可以防止 XSS 攻击能够窃取底层 JWT。您的 Flask 应用程序可以隐式刷新即将到期的 JWT,这简化了保持活动用户登录的逻辑。下一节将详细介绍!
当然,在使用 cookie 时,您还需要做一些额外的工作来防止跨站请求伪造 (CSRF) 攻击。在这个扩展中,我们通过称为双重提交验证的东西来处理这个问题。
双重提交验证背后的基本思想是,仅当请求中还存在特殊的双重提交令牌时,来自 cookie 的 JWT 才会被认为是有效的,并且双重提交令牌不能是由 Web 浏览器自动发送的东西(即它不能是另一个cookie)。
默认情况下,我们通过在有人登录时设置两个 cookie 来完成此操作。第一个 cookie 包含 JWT,并且在该 JWT 中编码的是双重提交令牌。此 cookie 设置为仅 http,因此无法通过 javascript 访问(这是防止 XSS 攻击能够窃取 JWT 的原因)。我们设置的第二个 cookie 仅包含相同的双重提交令牌,但这次是在 javascript 可读的 cookie 中。每当发出请求时,它都需要包含一个X-CSRF-TOKEN标头,其中包含双重提交令牌的值。如果此标头中的值与存储在 JWT 中的值不匹配,则请求被踢出无效。
因为双重提交令牌需要作为标头出现(不会在请求中自动发送),并且在不同域上运行的一些恶意 javascript 将无法读取您网站上包含双重提交令牌的 cookie,我们已成功阻止任何 CSRF 攻击。
这确实意味着每当您发出请求时,您都需要手动包含X-CSRF-TOKEN标头,否则您的请求也将被踢出无效。让我们看看如何做到这一点:

async function login() {
  await fetch('/login_with_cookies', {method: 'post'});
}

async function logout() {
  await fetch('/logout_with_cookies', {method: 'post'});
}

function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
}

async function makeRequestWithJWT() {
  const options = {
    method: 'post',
    credentials: 'same-origin',
    headers: {
      'X-CSRF-TOKEN': getCookie('csrf_access_token'),
    },
  };
  const response = await fetch('/protected', options);
  const result = await response.json();
  return result;
}

请注意,还有其他 CSRF 选项,例如在表单中查找双重提交令牌、更改 cookie 路径等,可用于根据应用程序的需要进行定制。请参阅 跨站点请求伪造选项https://flask-jwt-extended.readthedocs.io/en/stable/options/#cross-site-request-forgery-options了解详细信息。

查询字符串

您还可以将 JWT 作为查询字符串的一部分发送。但是,请务必注意,在大多数情况下,我们建议不要这样做。它可能会导致一些不明显的安全问题,例如将 JWT 保存在浏览器历史记录中或将 JWT 登录到后端服务器,这都可能导致令牌受损。但是,此功能可能会提供一些有限的用处,例如发送密码重置链接,因此我们在此扩展程序中支持它。

async function login() {
  const response = await fetch('/login_without_cookies', {method: 'post'});
  const result = await response.json();
  localStorage.setItem('jwt', result.access_token);
}

function logout() {
  localStorage.removeItem('jwt');
}

async function makeRequestWithJWT() {
  const jwt = localStorage.getItem('jwt')
  const response = await fetch(`/protected?jwt=${jwt}`, {method: 'post'});
  const result = await response.json();
  return result;
}

JSON

这看起来与 Headers 方法非常相似,只是我们将 JWT 作为 JSON Body 的一部分而不是 header 发送。请注意,HEAD 或 GET 请求不能将 JSON 正文作为请求的一部分,因此这仅适用于 POST/PUT/PATCH/DELETE/等操作。
在大多数情况下,在 JSON 正文中发送 JWT 可能不是很有用,但无论如何我们都包含了它的选项。

async function login() {
  const response = await fetch('/login_without_cookies', {method: 'post'});
  const result = await response.json();
  localStorage.setItem('jwt', result.access_token);
}

function logout() {
  localStorage.removeItem('jwt');
}

// Note that if we change the method to `get` this will blow up with a
// "TypeError: Window.fetch: HEAD or GET Request cannot have a body"
async function makeRequestWithJWT() {
  const options = {
    method: 'post',
    body: JSON.stringify({access_token: localStorage.getItem('jwt')}),
    headers: {
      'Content-Type': 'application/json',
    },
  };
  const response = await fetch('/protected', options);
  const result = await response.json();
  return result;
}

标签:Content,cookies,extended,string,jwt,json,access,token
来源: https://www.cnblogs.com/yoyoketang/p/16646094.html

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

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

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

ICode9版权所有