fix: 完善单点登录传递回调地址和serverUrl的功能。优化token刷新机制,判断单点登录和管理员登录等等不同路径的处理机制。提示词管理的模板数据查找的时候只需要返回固定的5个类型。隐藏评查点设置中关于抽取的自定义模板的选择。
This commit is contained in:
@@ -196,13 +196,16 @@ export async function getUserSession(request: Request) {
|
||||
let tokenExpiresIn = session.get("tokenExpiresIn");
|
||||
const userInfo = session.get("userInfo");
|
||||
let frontendJWT = session.get("frontendJWT");
|
||||
|
||||
|
||||
let isTokenExpired = false;
|
||||
let refreshedSession = null;
|
||||
let shouldRegenerateJWT = false;
|
||||
|
||||
// 如果有token信息,检查是否需要刷新
|
||||
if (accessToken && refreshToken && tokenIssuedAt && tokenExpiresIn) {
|
||||
|
||||
// 🔑 admin 用户不需要刷新 OAuth token,只需要维护 JWT
|
||||
const isAdmin = userRole === 'admin';
|
||||
|
||||
// 如果有token信息,检查是否需要刷新(admin用户跳过OAuth token刷新)
|
||||
if (!isAdmin && accessToken && refreshToken && tokenIssuedAt && tokenExpiresIn) {
|
||||
try {
|
||||
const tokenInfo = {
|
||||
accessToken,
|
||||
@@ -213,30 +216,30 @@ export async function getUserSession(request: Request) {
|
||||
|
||||
// 检查并自动刷新token
|
||||
const refreshResult = await tokenManager.checkAndRefreshToken(tokenInfo);
|
||||
|
||||
|
||||
if (refreshResult.success && refreshResult.newTokenInfo) {
|
||||
const newToken = refreshResult.newTokenInfo;
|
||||
|
||||
|
||||
// 如果token被刷新了,更新session
|
||||
if (newToken.accessToken !== accessToken) {
|
||||
console.log("Token已刷新,更新session");
|
||||
|
||||
|
||||
session.set("accessToken", newToken.accessToken);
|
||||
session.set("refreshToken", newToken.refreshToken);
|
||||
session.set("tokenIssuedAt", newToken.tokenIssuedAt);
|
||||
session.set("tokenExpiresIn", newToken.tokenExpiresIn);
|
||||
|
||||
|
||||
// 更新本地变量
|
||||
accessToken = newToken.accessToken;
|
||||
tokenIssuedAt = newToken.tokenIssuedAt;
|
||||
tokenExpiresIn = newToken.tokenExpiresIn;
|
||||
|
||||
|
||||
// 标记需要重新生成JWT
|
||||
shouldRegenerateJWT = true;
|
||||
|
||||
|
||||
refreshedSession = session;
|
||||
}
|
||||
|
||||
|
||||
isTokenExpired = false;
|
||||
} else {
|
||||
console.error("Token刷新失败:", refreshResult.error);
|
||||
@@ -246,6 +249,18 @@ export async function getUserSession(request: Request) {
|
||||
console.error("Token验证过程中出错:", error);
|
||||
isTokenExpired = true;
|
||||
}
|
||||
} else if (isAdmin) {
|
||||
// admin 用户:不检查 OAuth token 过期,始终保持登录状态
|
||||
// console.log("admin 用户登录,跳过 OAuth token 刷新");
|
||||
isTokenExpired = false;
|
||||
|
||||
// admin 用户需要有一个合理的 tokenExpiresIn 用于 JWT 生成
|
||||
// 如果没有设置,使用一个默认值(如2小时)
|
||||
if (!tokenExpiresIn) {
|
||||
tokenExpiresIn = 7200; // 2小时
|
||||
session.set("tokenExpiresIn", tokenExpiresIn);
|
||||
refreshedSession = session;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查前端JWT状态
|
||||
@@ -458,11 +473,11 @@ export async function createSimpleUserSession(isAuthenticated: boolean, userRole
|
||||
*/
|
||||
export async function logout(request: Request) {
|
||||
const session = await getSession(request);
|
||||
|
||||
|
||||
// 获取访问令牌和应用ID,用于调用IDaaS单点登出
|
||||
const accessToken = session.get("accessToken");
|
||||
const appId = OAUTH_CONFIG.appId;
|
||||
|
||||
const appId = OAUTH_CONFIG.appId || 'idaasoauth2';
|
||||
|
||||
// 如果存在访问令牌,调用IDaaS单点登出
|
||||
if (accessToken && appId) {
|
||||
try {
|
||||
@@ -485,18 +500,20 @@ export async function logout(request: Request) {
|
||||
|
||||
/**
|
||||
* 调用IDaaS单点登出接口
|
||||
*
|
||||
*
|
||||
* @param accessToken - 用户的访问令牌
|
||||
* @param appId - 应用ID
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async function callIDaaSLogout(accessToken: string, appId: string): Promise<void> {
|
||||
const logoutUrl = `${OAUTH_CONFIG.serverUrl}/public/sp/slo/${appId}`;
|
||||
|
||||
const serverUrl = OAUTH_CONFIG.serverUrl || 'http://10.79.112.85';
|
||||
const redirectUri = OAUTH_CONFIG.redirectUri || 'http://10.79.97.17/';
|
||||
const logoutUrl = `${serverUrl}/public/sp/slo/${appId}`;
|
||||
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('access_token', accessToken);
|
||||
formData.append('redirect_url', encodeURIComponent(OAUTH_CONFIG.redirectUri));
|
||||
|
||||
formData.append('redirect_url', encodeURIComponent(redirectUri));
|
||||
|
||||
try {
|
||||
const response = await fetch(logoutUrl, {
|
||||
method: 'POST',
|
||||
@@ -505,11 +522,11 @@ async function callIDaaSLogout(accessToken: string, appId: string): Promise<void
|
||||
},
|
||||
body: formData.toString(),
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`IDaaS登出失败: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
|
||||
console.log("IDaaS单点登出请求成功");
|
||||
} catch (error) {
|
||||
console.error("调用IDaaS登出接口失败:", error);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
interface OAuthConfig {
|
||||
serverUrl: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
clientSecret?: string; // 可选,客户端不需要,仅服务器端使用
|
||||
redirectUri: string;
|
||||
appId: string;
|
||||
}
|
||||
@@ -47,6 +47,19 @@ export class OAuthClient {
|
||||
...config,
|
||||
serverUrl: config.serverUrl.replace(/\/$/, '') // 移除末尾斜杠
|
||||
};
|
||||
|
||||
// 🔍 仅在服务器端打印配置调试信息
|
||||
if (typeof window === 'undefined') {
|
||||
console.log('🔧 [服务器端] OAuthClient 初始化配置:', {
|
||||
serverUrl: this.config.serverUrl,
|
||||
clientId: this.config.clientId,
|
||||
redirectUri: this.config.redirectUri,
|
||||
appId: this.config.appId,
|
||||
hasClientSecret: !!this.config.clientSecret,
|
||||
clientSecretLength: this.config.clientSecret?.length || 0,
|
||||
clientSecretPreview: this.config.clientSecret ? `${this.config.clientSecret.substring(0, 10)}...` : 'undefined'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +75,15 @@ export class OAuthClient {
|
||||
redirect_uri: this.config.redirectUri,
|
||||
state: state
|
||||
});
|
||||
|
||||
|
||||
// 只打印公开信息,不打印 client_secret(安全考虑)
|
||||
console.log('🔧 OAuth授权URL配置:', {
|
||||
serverUrl: this.config.serverUrl,
|
||||
clientId: this.config.clientId,
|
||||
redirectUri: this.config.redirectUri,
|
||||
client_secret: this.config.clientSecret
|
||||
});
|
||||
|
||||
return `${this.config.serverUrl}/oauth/authorize?${params.toString()}`;
|
||||
}
|
||||
|
||||
@@ -72,18 +93,12 @@ export class OAuthClient {
|
||||
* @returns 访问令牌响应
|
||||
*/
|
||||
async getAccessToken(code: string): Promise<TokenResponse | null> {
|
||||
console.log('🔧 OAuth配置信息:', {
|
||||
serverUrl: this.config.serverUrl,
|
||||
clientId: this.config.clientId,
|
||||
redirectUri: this.config.redirectUri
|
||||
});
|
||||
|
||||
const url = `${this.config.serverUrl}/oauth/token`;
|
||||
const data = new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
code: code,
|
||||
client_id: this.config.clientId,
|
||||
client_secret: this.config.clientSecret,
|
||||
client_secret: this.config.clientSecret || '', // 提供默认值避免类型错误
|
||||
redirect_uri: this.config.redirectUri
|
||||
});
|
||||
|
||||
@@ -92,18 +107,27 @@ export class OAuthClient {
|
||||
grant_type: 'authorization_code',
|
||||
code: code,
|
||||
client_id: this.config.clientId,
|
||||
redirect_uri: this.config.redirectUri
|
||||
redirect_uri: this.config.redirectUri,
|
||||
// 仅在服务器端打印 client_secret 状态
|
||||
hasClientSecret: !!this.config.clientSecret,
|
||||
clientSecretLength: this.config.clientSecret?.length || 0
|
||||
});
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: data
|
||||
body: data,
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
console.log('🔧 Token响应状态:', response.status, response.statusText);
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -125,7 +149,12 @@ export class OAuthClient {
|
||||
|
||||
return tokenResponse;
|
||||
} catch (error) {
|
||||
console.error('❌ 获取访问令牌网络错误:', error);
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 获取访问令牌超时(15秒):', error.message);
|
||||
} else {
|
||||
console.error('❌ 获取访问令牌网络错误:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -139,12 +168,19 @@ export class OAuthClient {
|
||||
const url = `${this.config.serverUrl}/api/bff/v1.2/oauth2/userinfo`;
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
}
|
||||
},
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('获取用户信息失败:', response.status, response.statusText);
|
||||
return null;
|
||||
@@ -152,7 +188,12 @@ export class OAuthClient {
|
||||
|
||||
return await response.json() as UserInfoResponse;
|
||||
} catch (error) {
|
||||
console.error('获取用户信息网络错误:', error);
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 获取用户信息超时(15秒):', error.message);
|
||||
} else {
|
||||
console.error('❌ 获取用户信息网络错误:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -168,18 +209,31 @@ export class OAuthClient {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refreshToken,
|
||||
client_id: this.config.clientId,
|
||||
client_secret: this.config.clientSecret
|
||||
client_secret: this.config.clientSecret || '' // 提供默认值避免类型错误
|
||||
});
|
||||
|
||||
console.log('🔧 [刷新Token] 请求参数状态:', {
|
||||
hasClientSecret: !!this.config.clientSecret,
|
||||
clientSecretLength: this.config.clientSecret?.length || 0,
|
||||
refreshTokenLength: refreshToken?.length || 0
|
||||
});
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: data
|
||||
body: data,
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('刷新访问令牌失败:', errorData);
|
||||
@@ -188,7 +242,12 @@ export class OAuthClient {
|
||||
|
||||
return await response.json() as TokenResponse;
|
||||
} catch (error) {
|
||||
console.error('刷新访问令牌网络错误:', error);
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 刷新访问令牌超时(15秒):', error.message);
|
||||
} else {
|
||||
console.error('❌ 刷新访问令牌网络错误:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -207,17 +266,28 @@ export class OAuthClient {
|
||||
});
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: data
|
||||
body: data,
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('登出失败:', error);
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 登出超时(15秒):', error.message);
|
||||
} else {
|
||||
console.error('❌ 登出失败:', error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ export async function getPromptTemplates(searchParams: PromptSearchParams = {},
|
||||
}> {
|
||||
try {
|
||||
// console.log('获取提示词模板列表,参数:', searchParams);
|
||||
const TYPE = 'Common,LLM_Extraction,VLM_Extraction,Evaluation,Summary';
|
||||
|
||||
const page = searchParams.page || 1;
|
||||
const pageSize = searchParams.pageSize || 10;
|
||||
@@ -157,6 +158,8 @@ export async function getPromptTemplates(searchParams: PromptSearchParams = {},
|
||||
|
||||
if (searchParams.type) {
|
||||
filter['template_type'] = `eq.${searchParams.type}`;
|
||||
}else{
|
||||
filter['template_type'] = `in.(${TYPE})`;
|
||||
}
|
||||
|
||||
if (searchParams.status) {
|
||||
|
||||
Reference in New Issue
Block a user