import { type LoaderFunctionArgs, redirect } from "@remix-run/node"; import { createUserSession, saveUserInfo, type UserRole } from "~/api/login/auth.server"; import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt"; import { validateRequest, logSecurityEvent } from "~/middleware/host-validation"; export async function loader({ request }: LoaderFunctionArgs) { // ==================== Host头验证 ==================== // OAuth回调是安全敏感的操作,需要严格验证请求来源 const hostValidation = validateRequest(request); if (!hostValidation.valid) { // 记录安全事件 logSecurityEvent('host_validation_failed', hostValidation.error || 'Unknown validation error', request); console.error('❌ OAuth回调Host验证失败:', hostValidation.error); return redirect("/login?error=invalid_host"); } // console.log('✅ OAuth回调Host验证通过'); // ==================== Host头验证结束 ==================== const url = new URL(request.url); const origin = url.origin; // 获取请求的源 (e.g., "http://10.79.97.17:51703") const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); const error = url.searchParams.get("error"); const error_description = url.searchParams.get("error_description"); console.log("🔧 OAuth2.0回调参数:", { code: code ? `${code.substring(0, 10)}...` : null, state: state, error: error, error_description: error_description, fullUrl: request.url }); // 检查是否有错误 if (error) { console.error("❌ OAuth2.0授权失败:", error, error_description); return redirect(`/login?error=${encodeURIComponent(error_description || error)}`); } // 检查是否有授权码 if (!code) { console.error("❌ OAuth2.0回调缺少授权码"); return redirect("/login?error=missing_code"); } // 验证状态值 if (!state || !state.endsWith("_idp")) { console.error("❌ OAuth2.0状态值验证失败:", { state, expectedSuffix: "_idp" }); return redirect("/login?error=invalid_state"); } console.log("✅ OAuth2.0回调参数验证通过"); try { console.log("🔧 开始处理OAuth2.0回调"); // --- 修改开始: 不再直接调用OAuthClient,而是通过内部代理API --- // 获取访问令牌 (通过代理) console.log(`🔧 [Callback] 开始通过内部代理获取访问令牌... (目标: ${origin}/api/oauth/token)`); const proxyResponse = await fetch(`${origin}/api/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ code }), }); const tokenResponse = await proxyResponse.json(); if (!proxyResponse.ok || !tokenResponse.success) { console.error("❌ [Callback] 通过内部代理获取访问令牌失败:", tokenResponse); return redirect("/login?error=token_proxy_error"); } // --- 修改结束 --- console.log("✅ [Callback] 访问令牌获取成功"); // --- 修改开始: 通过内部代理获取用户信息 --- console.log(`🔧 [Callback] 开始通过内部代理获取用户信息... (目标: ${origin}/api/oauth/userinfo)`); const userInfoProxyResponse = await fetch(`${origin}/api/oauth/userinfo`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ accessToken: tokenResponse.access_token }), }); const userInfoResponse = await userInfoProxyResponse.json(); if (!userInfoProxyResponse.ok || !userInfoResponse.success) { console.error("❌ [Callback] 通过内部代理获取用户信息失败:", userInfoResponse); return redirect("/login?error=userinfo_proxy_error"); } // 将代理返回的用户信息包装成与原有一致的结构 const userInfo = { success: true, data: userInfoResponse.data, }; // --- 修改结束 --- console.log("✅ [Callback] 用户信息获取成功"); // TODO 根据用户信息判断用户角色,这里可以根据实际业务逻辑调整 暂定都是common const userRole = "common"; // 获取重定向URL const redirectTo = url.searchParams.get("redirect") || "/"; // 成功获取用户信息之后通过auth.server.ts中的saveUserInfo方法去写入自己的数据库中,通过sub作为唯一值去添加数据 const saveResult = await saveUserInfo(userInfo.data); if (!saveResult.success) { console.error("保存用户信息到数据库失败:", saveResult.error); // 注意:即使保存到数据库失败,我们仍然继续登录流程,因为用户已经通过了身份验证 return redirect("/login?error=save_user_error"); } console.log("用户信息已成功保存到数据库"); const savedUserData = saveResult.data!; // 生成前端专用JWT const jwtUserInfo: UserInfoForJWT = { sub: userInfo.data.sub, user_id: savedUserData.id!, username: savedUserData.username, nick_name: savedUserData.nick_name, email: savedUserData.email, phone_number: savedUserData.phone_number, ou_id: savedUserData.ou_id, ou_name: savedUserData.ou_name, is_leader: savedUserData.is_leader, user_role: userRole as 'common' | 'developer' }; const frontendJWT = JWTUtils.generateJWT(jwtUserInfo, tokenResponse.expires_in); console.log("前端JWT已生成"); // 更新userInfo以包含数据库ID、JWT,并用数据库标准字段覆盖关键属性,确保 nick_name 等存在 const enhancedUserInfo = { ...userInfo.data, // 保留OAuth返回的原字段(包含 nickname 等) username: savedUserData.username, nick_name: savedUserData.nick_name, phone_number: savedUserData.phone_number, email: savedUserData.email, ou_id: savedUserData.ou_id, ou_name: savedUserData.ou_name, status: savedUserData.status, is_leader: savedUserData.is_leader, user_id: savedUserData.id, user_role: userRole, frontend_jwt: frontendJWT }; // 使用统一的session创建函数 return createUserSession({ isAuthenticated: true, userRole: userRole as UserRole, redirectTo, accessToken: tokenResponse.access_token, refreshToken: tokenResponse.refresh_token, tokenExpiresIn: tokenResponse.expires_in, userInfo: enhancedUserInfo, frontendJWT }); } catch (error) { console.error("OAuth2.0回调处理失败:", error); return redirect("/login?error=callback_error"); } } export default function Callback() { return (

正在处理登录...

); }