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
相关产品推荐
相关产品推荐

