返回

Flask Swagger CORS 问题解决指南

python

Flask Swagger中的CORS问题处理

CORS(Cross-Origin Resource Sharing,跨域资源共享)是在Web开发中常见的安全机制,它限制了运行在一个域中的网页脚本访问另一个域中的资源。当你在Flask中集成Swagger UI时,并且你的API服务与Swagger UI不在同一个域下,CORS问题很可能出现。 这会导致浏览器拒绝跨域请求,即使服务器返回了数据。

常见CORS错误信息:

Access to fetch at 'https://XXXXXXXXXXXXX.XX/' from origin 'https://XX.XX.XX.X:40' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

产生CORS问题的根本原因是浏览器的同源策略。 简单来说,同源策略要求Web应用只能访问与其来源(协议、域名、端口)相同的资源。 如果请求的资源与当前页面的来源不同,浏览器就会阻止这个请求。

以下几种方法可以用来解决 Flask Swagger 中的 CORS 问题。

1. 使用 Flask-CORS 扩展

flask-cors是一个简单且强大的Flask扩展,它能轻松地处理CORS相关的HTTP头部设置。这是最常用、推荐的解决方案。

原理: flask-cors为 Flask 应用自动添加响应头 Access-Control-Allow-Origin,告诉浏览器允许特定的域进行跨域请求。

操作步骤:

  1. 安装 Flask-CORS:

    pip install flask-cors
    
  2. 初始化 CORS:

    在 Flask 应用初始化时,将CORS应用于整个app或特定路由。以下展示几种常见的用法:

    • 允许所有来源:
    from flask import Flask
    from flask_cors import CORS
    
    app = Flask(__name__)
    CORS(app)  # 允许来自所有域的请求
    
    @app.route("/")
    def hello():
        return "Hello World!"
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    • 允许特定来源:
    from flask import Flask
    from flask_cors import CORS
    
    app = Flask(__name__)
    CORS(app, resources={r"/api/*": {"origins": "https://example.com"}})  # 只允许来自example.com域对/api/下的所有路径的请求
    
    @app.route("/api/resource")
    def resource():
        return "This is a protected resource!"
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    • 更细粒度的配置(允许特定methods和headers):
    from flask import Flask
    from flask_cors import CORS
    
    app = Flask(__name__)
    CORS(app, resources={r"/api/*": {"origins": "https://example.com", "methods": ["GET", "POST"], "allow_headers": ["Content-Type", "Authorization"]}})
    
    @app.route("/api/resource", methods=["GET", "POST"])
    def resource():
        return "This is a protected resource!"
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    配置说明:

    • origins:指定允许的来源域。可以使用通配符 * 允许所有域,但不建议在生产环境中使用,因为存在安全风险。最好明确指定允许的域。
    • methods:允许的HTTP方法,如GET、POST、PUT、DELETE等。
    • allow_headers:允许客户端在请求中携带的自定义头部。 如果不设置,浏览器可能会阻止携带某些头部信息的请求。 常用的 Content-Type 一般是需要的。 Authorization 则用于携带token.
  3. 确保Swagger UI的URL在允许的来源中 。 如果Swagger UI运行在https://my-swagger-ui.com,你的Flask应用需要配置CORS(app, resources={r"/*": {"origins": "https://my-swagger-ui.com"}})

2. 手动添加响应头

虽然使用flask-cors扩展通常更方便,但也可以手动设置CORS响应头。

原理: 拦截请求并在响应中添加相应的HTTP头信息,明确告知浏览器允许跨域请求。

操作步骤:

  1. 创建一个装饰器或中间件:

    from flask import Flask, make_response
    
    app = Flask(__name__)
    
    @app.after_request
    def add_cors_headers(response):
        response.headers['Access-Control-Allow-Origin'] = '*' #不建议生产环境使用 *,需改为指定域名
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
        response.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
        return response
    
    
    @app.route("/")
    def hello():
        return "Hello World!"
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    或,也可以使用更严格的方式控制OPTIONS请求:

    from flask import Flask, make_response, request
    
    app = Flask(__name__)
    
    @app.before_request
    def handle_preflight():
       if request.method == "OPTIONS":
           res = make_response()
           res.headers['Access-Control-Allow-Origin'] = '*' #不建议生产环境使用 *,需改为指定域名
           res.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
           res.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
           return res
    
    
    @app.after_request
    def add_cors_headers(response):
       response.headers['Access-Control-Allow-Origin'] = '*' #不建议生产环境使用 *,需改为指定域名
       return response
    
    
    @app.route("/")
    def hello():
       return "Hello World!"
    
    if __name__ == '__main__':
       app.run(debug=True)
    
  2. 注册中间件或使用装饰器

  3. flask-cors一样,谨慎设置 Access-Control-Allow-Origin 在生产环境中应避免使用 * ,以防止潜在的安全风险。

安全建议

  • 不要在生产环境中使用通配符 * 作为 Access-Control-Allow-Origin 明确指定允许的域名。
  • 只允许必要的HTTP方法和头部。 避免允许不安全的方法或未知的头部。
  • 验证 Origin 头部。 在处理跨域请求时,始终验证 Origin 头部,以确保请求来自可信的来源。

解决 CORS 问题需要理解其根本原因,并选择合适的解决方案。 flask-cors 扩展提供了一种便捷的方法来处理 CORS,但手动添加响应头也同样可行。 务必牢记安全最佳实践,防止潜在的安全风险。