@@ -53,19 +53,9 @@ namespace YhCore.WebApi.Middleware
|
|||||||
timeNow = TimeNow()
|
timeNow = TimeNow()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// 未捕获异常 → 平台兜底:200 + code:6 内部服务错误(对齐 Java GlobalExceptionHandler.handleException)
|
// 未捕获异常 → 平台兜底:200 + code:6 内部服务错误(对齐 Java GlobalExceptionHandler.handleException)
|
||||||
// [TEMP-DIAG 2026-09-03] 完整堆栈落 api-access 日志,定位 GetUserInfo code6 根因后移除
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log4net.LogManager.GetLogger(typeof(GlobalExceptionMiddleware))
|
|
||||||
.Error("UNCAUGHT-EXCEPTION url=" + context.Request.Path + context.Request.QueryString + " => " + ex);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// 诊断日志失败不影响响应
|
|
||||||
}
|
|
||||||
await WriteJsonAsync(context, StatusCodes.Status200OK, new
|
await WriteJsonAsync(context, StatusCodes.Status200OK, new
|
||||||
{
|
{
|
||||||
code = ErrorCode.InternalServerError.Code,
|
code = ErrorCode.InternalServerError.Code,
|
||||||
|
|||||||
@@ -87,6 +87,18 @@ namespace YhCore.WebApi.Middleware
|
|||||||
"<p><a href=\"/\">重新登录</a></p></body></html>");
|
"<p><a href=\"/\">重新登录</a></p></body></html>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// API 渠道(/api/*、/mobiapi/*)的 AJAX/XHR 请求:未认证时返回 401 JSON,而非 302 重定向到 CAS。
|
||||||
|
// 原因:浏览器 fetch/XHR 会跟随 302 跨域跳转到 sso.yunhaixingtong.com,而 CAS 登录页无
|
||||||
|
// Access-Control-Allow-Origin 头,导致 CORS 拦截(Response to preflight request ... blocked)。
|
||||||
|
// 前端据此 401 做整页跳转登录(location.href = "/")。文档导航(浏览器地址栏 / Accept:text/html)仍走 302。
|
||||||
|
if (IsProtectedPath(context.Request.Path.Value, sso) && IsAjaxRequest(context.Request))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
|
context.Response.ContentType = "application/json; charset=utf-8";
|
||||||
|
await context.Response.WriteAsync("{\"code\":4,\"msg\":\"未登录\",\"data\":null}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 未认证 → Challenge → 302 重定向 CAS 登录页(ticket 校验与回跳由新框架 CasAuthenticationHandler 处理)
|
// 未认证 → Challenge → 302 重定向 CAS 登录页(ticket 校验与回跳由新框架 CasAuthenticationHandler 处理)
|
||||||
await context.ChallengeAsync(YhAuthDefaults.CasScheme);
|
await context.ChallengeAsync(YhAuthDefaults.CasScheme);
|
||||||
return;
|
return;
|
||||||
@@ -95,6 +107,30 @@ namespace YhCore.WebApi.Middleware
|
|||||||
await _next(context);
|
await _next(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断请求是否为 AJAX/XHR(而非浏览器整页文档导航)。任一命中即视为 AJAX:
|
||||||
|
/// 1. X-Requested-With: XMLHttpRequest(jQuery/axios/原生 XHR 常见);
|
||||||
|
/// 2. Sec-Fetch-Dest: empty(现代浏览器 fetch/XHR 必带);
|
||||||
|
/// 3. Accept 不含 text/html(fetch 默认 */*、axios 默认 application/json)。
|
||||||
|
/// 文档导航(浏览器地址栏、location.href、<a>)Accept 含 text/html 且无前两者 → false,保持 302。
|
||||||
|
/// </summary>
|
||||||
|
private static bool IsAjaxRequest(HttpRequest request)
|
||||||
|
{
|
||||||
|
if (string.Equals(request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(request.Headers["Sec-Fetch-Dest"].ToString(), "empty", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var accept = request.Headers["Accept"].ToString();
|
||||||
|
return !string.IsNullOrEmpty(accept) &&
|
||||||
|
accept.IndexOf("text/html", StringComparison.OrdinalIgnoreCase) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>路径是否命中保护模式(yh:sso:urlPattern / yh:sso:mobiApiPattern,默认 /api/*、/mobiapi/*,通配后缀匹配,忽略大小写)</summary>
|
/// <summary>路径是否命中保护模式(yh:sso:urlPattern / yh:sso:mobiApiPattern,默认 /api/*、/mobiapi/*,通配后缀匹配,忽略大小写)</summary>
|
||||||
private static bool IsProtectedPath(string path, IConfigurationSection sso)
|
private static bool IsProtectedPath(string path, IConfigurationSection sso)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user