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

Svelte跳转至Microsoft Entra ID因CORS失败的问题求助

解决方案:SvelteKit中API请求时的认证跳转问题

问题根源

当客户端通过fetch调用API路由(+server.ts)时,服务端返回的302重定向会被浏览器的CORS安全机制拦截——因为重定向目标是第三方域名(Microsoft Entra ID),不符合跨域请求的规则。而页面导航(page.server.ts)是浏览器发起的普通HTTP请求,不存在这个限制,因此跳转逻辑正常。

解决步骤

1. 在hooks.server.ts中区分请求类型,返回不同响应

通过请求头的Accept字段判断是页面导航还是API请求:页面导航的Accept会包含text/html,API请求通常是application/json或其他非HTML类型。针对不同请求类型返回对应处理逻辑:

  • 页面导航:直接重定向到Microsoft认证页
  • API请求:返回401状态码,由客户端处理跳转
import { redirect, type Handle } from '@sveltejs/kit';
import { verifyToken } from '$lib/auth'; // 你的token校验工具函数

export const handle: Handle = async ({ event, resolve }) => {
  // 跳过认证回调路由,避免死循环
  if (event.url.pathname.startsWith('/auth/callback')) {
    return resolve(event);
  }

  const token = event.cookies.get('access_token');
  const isAuthenticated = await verifyToken(token);

  if (!isAuthenticated) {
    const acceptHeader = event.request.headers.get('accept');
    const isPageNavigation = acceptHeader?.includes('text/html');

    if (isPageNavigation) {
      // 构造Microsoft Entra ID认证地址,携带原页面路径作为state
      const authUrl = new URL('https://login.microsoftonline.com/你的租户ID/oauth2/v2.0/authorize');
      authUrl.searchParams.set('client_id', '你的客户端ID');
      authUrl.searchParams.set('response_type', 'code');
      authUrl.searchParams.set('redirect_uri', `${event.url.origin}/auth/callback`);
      authUrl.searchParams.set('scope', 'openid profile email');
      authUrl.searchParams.set('state', event.url.pathname); // 保存原页面,认证后跳转回去
      throw redirect(302, authUrl.toString());
    } else {
      // API请求返回401,让客户端处理跳转
      return new Response(JSON.stringify({ error: '未授权' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' }
      });
    }
  }

  return resolve(event);
};

2. 客户端全局处理401错误

在客户端封装统一的fetch工具函数,或者在布局组件中全局拦截401响应,触发页面跳转:

<script>
  import { goto } from '$app/navigation';

  // 封装带认证处理的fetch函数
  async function authFetch(url: string, options: RequestInit = {}) {
    const response = await fetch(url, options);
    if (response.status === 401) {
      // 跳转到认证页(或直接跳转到Microsoft认证地址)
      await goto('/auth/login');
    }
    return response;
  }

  // 示例:在页面中使用封装后的fetch
  async function loadData() {
    const res = await authFetch('/api/your-data');
    if (res.ok) {
      const data = await res.json();
      // 处理数据
    }
  }
</script>

3. 统一认证回调逻辑

在/auth/callback/+server.ts中处理Microsoft的认证回调,获取token后跳转到state参数保存的原页面:

import { redirect } from '@sveltejs/kit';
import { exchangeCodeForToken } from '$lib/auth'; // 你的code换token函数

export async function GET({ url, cookies }) {
  const code = url.searchParams.get('code');
  const state = url.searchParams.get('state') || '/';

  if (code) {
    const token = await exchangeCodeForToken(code);
    // 保存token到cookie
    cookies.set('access_token', token.access_token, {
      path: '/',
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      maxAge: token.expires_in
    });
  }

  // 跳回原页面
  throw redirect(302, state);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 18:32:35