101 lines
3.7 KiB
TypeScript
101 lines
3.7 KiB
TypeScript
import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
|
|
import { OAuthClient } from "~/api/login/oauth-client";
|
|
import { OAUTH_CONFIG } from "~/config/api-config";
|
|
import { sessionStorage, saveUserInfo } from "~/api/login/auth.server";
|
|
import { toastService } from "~/components/ui";
|
|
|
|
export async function loader({ request }: LoaderFunctionArgs) {
|
|
const url = new URL(request.url);
|
|
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");
|
|
|
|
// 检查是否有错误
|
|
if (error) {
|
|
console.error("OAuth2.0授权失败:", error, error_description);
|
|
return redirect(`/login?error=${encodeURIComponent(error_description || error)}`);
|
|
}
|
|
|
|
// 检查是否有授权码
|
|
if (!code) {
|
|
toastService.error("通过OAuth2.0登录回调缺少授权码");
|
|
console.error("OAuth2.0回调缺少授权码");
|
|
return redirect("/login?error=missing_code");
|
|
}
|
|
|
|
// 验证状态值(可选,但建议实现)
|
|
// 这里简单验证state是否以_idp结尾
|
|
// if (!state || !state.endsWith("_idp")) {
|
|
// console.error("OAuth2.0状态值验证失败");
|
|
// return redirect("/login?error=invalid_state");
|
|
// }
|
|
|
|
try {
|
|
// 创建OAuth客户端
|
|
const oauthClient = new OAuthClient(OAUTH_CONFIG);
|
|
|
|
// 获取访问令牌
|
|
const tokenResponse = await oauthClient.getAccessToken(code);
|
|
if (!tokenResponse) {
|
|
console.error("获取访问令牌失败");
|
|
return redirect("/login?error=token_error");
|
|
}
|
|
|
|
// 获取用户信息
|
|
const userInfo = await oauthClient.getUserInfo(tokenResponse.access_token);
|
|
if (!userInfo || !userInfo.success) {
|
|
console.error("获取用户信息失败:", userInfo);
|
|
return redirect("/login?error=userinfo_error");
|
|
}
|
|
|
|
// 创建会话
|
|
const session = await sessionStorage.getSession();
|
|
session.set("isAuthenticated", true);
|
|
session.set("accessToken", tokenResponse.access_token);
|
|
session.set("refreshToken", tokenResponse.refresh_token);
|
|
session.set("tokenIssuedAt", Date.now());
|
|
session.set("tokenExpiresIn", tokenResponse.expires_in);
|
|
session.set("userInfo", userInfo.data);
|
|
|
|
// TODO 根据用户信息判断用户角色,这里可以根据实际业务逻辑调整 暂定都是common
|
|
// const userRole = userInfo.data.username === "admin" ? "developer" : "common";
|
|
const userRole = "common";
|
|
session.set("userRole", userRole);
|
|
|
|
// 获取重定向URL
|
|
const redirectTo = url.searchParams.get("redirect") || "/";
|
|
|
|
const cookie = await sessionStorage.commitSession(session);
|
|
|
|
// 成功获取用户信息之后通过auth.server.ts中的saveUserInfo方法去写入自己的数据库中,通过sub作为唯一值去添加数据
|
|
const saveResult = await saveUserInfo(userInfo.data);
|
|
if (!saveResult.success) {
|
|
console.error("保存用户信息到数据库失败:", saveResult.error);
|
|
// 注意:即使保存到数据库失败,我们仍然继续登录流程,因为用户已经通过了身份验证
|
|
} else {
|
|
console.log("用户信息已成功保存到数据库");
|
|
}
|
|
|
|
return redirect(redirectTo, {
|
|
headers: {
|
|
"Set-Cookie": cookie
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("OAuth2.0回调处理失败:", error);
|
|
return redirect("/login?error=callback_error");
|
|
}
|
|
}
|
|
|
|
export default function Callback() {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen">
|
|
<div className="text-center">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto"></div>
|
|
<p className="mt-4 text-gray-600">正在处理登录...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|