You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

前端调用Flask后端API登出时遇CORS跨域权限问题求助

登出请求CORS错误排查与解决

错误信息

Access to XMLHttpRequest at 'http://localhost:8000/api/auth/logout' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Logout error: Network Error


后端相关代码

/api/auth.py 文件内容

class UserLogout(Resource): 
    @JWTManager.token_required
    def post(self, public_id): 
        """Logout authenticated user by invalidating JWT refresh token"""
        
        current_user = User.find_by_public_id(public_id)

        if current_user: 
            # Get token from HTTPOnly cookies
            refresh_token = request.cookies.get('refresh_token')
            print("refresh_token")
            if not refresh_token: 
                return {'message': 'Refresh token not provided.'}, 401
            # Revoke refresh token 
            try: 
                JWTManager.revoke_token(refresh_token)
                response = make_response({'message': 'User logged out successfully.'}, 200)
                JWTManager.delete_token_cookies(response)
                response.delete_cookie('csrfToken', path='/', samesite='Strict')  
                return response 
            except TokenInvalidError: 
                return {'message': 'Invalid token: Please login again'}, 401
            except Exception as e: 
                return {'message': f'Error occurred during logout.', 'errors': e}, 500
        else: 
            return {'message': 'User not found.'}, 404

Flask服务器初始化文件内容

cors = CORS()


def create_app():

    app = Flask(__name__)

    # Set up general app configurations
    app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
    app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_NAME}'
    app.config['SOCKETIO_SECRET_KEY'] = os.getenv('SOCKETIO_SECRET_KEY')

    # Set up Redis key-value db store configurations
    app.config['SESSION_TYPE'] = 'redis'
    app.config['SESSION_PERMANENT'] = False
    app.config['SESSION_USE_SIGNER'] = True
    app.config['SESSION_REDIS'] = Redis(
        host=os.getenv('REDIS_HOST'),
        port= os.getenv('REDIS_PORT'),
        # password=os.getenv('REDIS_PASSWORD'), --> USE PASSWORD IN PROD. 
        db=os.getenv('REDIS_DB'),
        decode_responses=False 
    )

    app.redis_client = app.config['SESSION_REDIS']
    

    db.init_app(app)
    csrf.init_app(app)
    socketio.init_app(app, logger=True, engineio_logger=True, cors_allowed_origins="*")
    cors.init_app(app, supports_credentials=True, allow_headers=[
            'Content-Type', 'X-CSRF-Token', 'Authorization', 'Access-Control-Allow-Origin', 'Access-Control-Allow-Credentials'
        ], resources={r"/api/*": {"origins": "http://localhost:5173/*"}})

    from .auth import auth
    from website.views import views
    from .api import api_bp, initialize_routes

    # Intialize api routes 
    initialize_routes()

    app.register_blueprint(views, url_prefix='/')
    app.register_blueprint(auth, url_prefix='/')
    app.register_blueprint(api_bp, url_prefix='/api')

    if not database_exists():
        create_database(app)

    return app

def database_exists():
    return path.exists('website/' + DB_NAME)


def create_database(app):
    with app.app_context():
        db.create_all()

前端登出代码

const dispatch = useDispatch();
  const navigate = useNavigate();
  const csrfToken = useSelector((state: RootState) => state.csrfToken.value);
  console.log(csrfToken)
  // Handle logout
  const logout = async () => {
    try {
      await axiosInstance.post(
        '/auth/logout',
        {},
        {
          withCredentials: true,
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': csrfToken,
          },
        }
      );
      console.log("post work")
      // Clear CSRF token from Redux after successful logout
      dispatch(clearCsrfToken());
      // Redirect to login page upon successful logout
      navigate('/login');
    } catch (error) {
      if (axios.isAxiosError(error)) {
        console.error('Logout error:', error.message);
        if (error.response) {
          console.error('Server response:', error.response.data);
        }
      } else if (error instanceof Error) {
        console.error('Unexpected error:', error.message);
      }
    }
  };
  return logout;
};

解决步骤

  1. 修正CORS源配置格式
    当前resources中origins写了http://localhost:5173/*,这是错误格式,源不需要加/*,改成精确的源地址:

    resources={r"/api/*": {"origins": "http://localhost:5173"}}
    
  2. 清理请求头配置
    allow_headers中不需要包含Access-Control-Allow-Origin和Access-Control-Allow-Credentials,这两个是响应头,不是请求头,修改后:

    cors.init_app(app, supports_credentials=True, 
                  allow_headers=['Content-Type', 'X-CSRF-Token', 'Authorization'],
                  resources={r"/api/*": {"origins": "http://localhost:5173"}})
    
  3. 确保CORS中间件优先生效
    检查cors.init_app的调用顺序,必须在蓝图注册之前执行,否则蓝图路由不会应用CORS配置。

  4. 处理JWT装饰器的拦截响应
    如果@JWTManager.token_required验证失败时直接返回响应,可能会绕过CORS中间件。可以在全局添加CORS中间件,或者确保所有错误响应都手动添加CORS头:

    # 示例:在错误响应中添加CORS头
    response = make_response({'message': 'Invalid token: Please login again'}, 401)
    response.headers['Access-Control-Allow-Origin'] = 'http://localhost:5173'
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    return response
    
  5. 验证预检请求处理
    浏览器会先发OPTIONS预检请求,确保Flask-CORS自动处理OPTIONS请求,或者手动添加OPTIONS方法支持。

内容的提问来源于stack exchange,提问作者user23776560

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 07:04:56