fix: add User-Agent/Referer headers, GET index first for session cookie, handle non-JSON login response
This commit is contained in:
parent
26ebfe132c
commit
a0a4b5698d
@ -16,9 +16,10 @@ import kotlinx.serialization.json.*
|
|||||||
* Sage 客户端 - 处理登录、Session 管理和 UI 加载
|
* Sage 客户端 - 处理登录、Session 管理和 UI 加载
|
||||||
*
|
*
|
||||||
* Sage 使用 Cookie Session 认证:
|
* Sage 使用 Cookie Session 认证:
|
||||||
* 1. POST /rbac/user/userpassword_login.dspy 登录获取 session cookie
|
* 1. GET 首页获取初始 session cookie
|
||||||
* 2. 后续请求自动携带 cookie
|
* 2. POST /rbac/user/userpassword_login.dspy 登录
|
||||||
* 3. GET /xxx.ui 获取 JSON 格式的 UI 描述
|
* 3. 后续请求自动携带 cookie
|
||||||
|
* 4. GET /xxx.ui 获取 JSON 格式的 UI 描述
|
||||||
*/
|
*/
|
||||||
class SageClient {
|
class SageClient {
|
||||||
|
|
||||||
@ -54,45 +55,55 @@ class SageClient {
|
|||||||
suspend fun login(username: String, password: String): Boolean = mutex.withLock {
|
suspend fun login(username: String, password: String): Boolean = mutex.withLock {
|
||||||
_loginError.value = null
|
_loginError.value = null
|
||||||
try {
|
try {
|
||||||
|
// Step 1: GET the index page to obtain initial session cookies
|
||||||
|
// Sage requires a valid session cookie before accepting login POST
|
||||||
|
val indexResponse = client.get("$baseUrl/")
|
||||||
|
println("[Sage] GET / status: ${indexResponse.status}")
|
||||||
|
|
||||||
|
// Step 2: POST login
|
||||||
val url = "$baseUrl/rbac/user/userpassword_login.dspy"
|
val url = "$baseUrl/rbac/user/userpassword_login.dspy"
|
||||||
val encodedUser = java.net.URLEncoder.encode(username, "UTF-8")
|
val encodedUser = java.net.URLEncoder.encode(username, "UTF-8")
|
||||||
val encodedPass = java.net.URLEncoder.encode(password, "UTF-8")
|
val encodedPass = java.net.URLEncoder.encode(password, "UTF-8")
|
||||||
val formBody = "username=$encodedUser&passwd=$encodedPass"
|
val formBody = "username=$encodedUser&passwd=$encodedPass"
|
||||||
|
|
||||||
val response = client.post(url) {
|
val response = client.post(url) {
|
||||||
|
header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36")
|
||||||
|
header(HttpHeaders.Referrer, "$baseUrl/")
|
||||||
contentType(ContentType.Application.FormUrlEncoded)
|
contentType(ContentType.Application.FormUrlEncoded)
|
||||||
setBody(formBody)
|
setBody(formBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
val body = response.bodyAsText()
|
val body = response.bodyAsText()
|
||||||
println("[Sage] Login response status: ${response.status}")
|
println("[Sage] Login response status: ${response.status}")
|
||||||
println("[Sage] Login response body: ${body.take(200)}")
|
println("[Sage] Login response body: ${body.take(300)}")
|
||||||
|
|
||||||
|
// Check if response is JSON
|
||||||
|
if (!body.trimStart().startsWith("{")) {
|
||||||
|
// Non-JSON response - likely an error page or auth failure
|
||||||
|
if (response.status.value == 401 || response.status.value == 403) {
|
||||||
|
_loginError.value = "登录失败: 服务器拒绝请求 (${response.status.value})"
|
||||||
|
} else {
|
||||||
|
_loginError.value = "登录失败: 服务器返回非JSON响应 (${response.status.value})"
|
||||||
|
}
|
||||||
|
println("[Sage] Non-JSON login response: ${body.take(200)}")
|
||||||
|
return@withLock false
|
||||||
|
}
|
||||||
|
|
||||||
// Sage 返回 UiMessage 格式: {"widgettype": "Message", "options": {...}}
|
// Sage 返回 UiMessage 格式: {"widgettype": "Message", "options": {...}}
|
||||||
// 或者返回错误: {"widgettype": "Error", "options": {...}}
|
|
||||||
val json = try {
|
val json = try {
|
||||||
Json.parseToJsonElement(body).jsonObject
|
Json.parseToJsonElement(body).jsonObject
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_loginError.value = "登录响应格式错误: ${e.message}"
|
_loginError.value = "登录响应解析错误: ${e.message}"
|
||||||
return@withLock false
|
return@withLock false
|
||||||
}
|
}
|
||||||
|
|
||||||
val widgetType = json["widgettype"]?.jsonPrimitive?.content
|
val widgetType = json["widgettype"]?.jsonPrimitive?.content
|
||||||
|
|
||||||
if (widgetType == "Message" || widgetType == "UiMessage") {
|
if (widgetType == "Message" || widgetType == "UiMessage") {
|
||||||
// 检查是否有 session cookie
|
|
||||||
val cookies = cookieStorage.get(URLBuilder(baseUrl).build())
|
val cookies = cookieStorage.get(URLBuilder(baseUrl).build())
|
||||||
if (cookies.isNotEmpty()) {
|
println("[Sage] Login successful, cookies: ${cookies.size}")
|
||||||
println("[Sage] Login successful, got ${cookies.size} cookies")
|
_isLoggedIn.value = true
|
||||||
_isLoggedIn.value = true
|
true
|
||||||
true
|
|
||||||
} else {
|
|
||||||
// 即使没有 cookie,如果服务器返回成功也算登录成功
|
|
||||||
// 有些部署可能使用 token 而非 cookie
|
|
||||||
println("[Sage] Login successful (no cookies)")
|
|
||||||
_isLoggedIn.value = true
|
|
||||||
true
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 错误消息
|
// 错误消息
|
||||||
val options = json["options"]?.jsonObject ?: JsonObject(emptyMap())
|
val options = json["options"]?.jsonObject ?: JsonObject(emptyMap())
|
||||||
@ -118,7 +129,9 @@ class SageClient {
|
|||||||
val url = if (path.startsWith("http")) path else "$baseUrl/${path.trimStart('/')}"
|
val url = if (path.startsWith("http")) path else "$baseUrl/${path.trimStart('/')}"
|
||||||
println("[Sage] Fetching UI: $url")
|
println("[Sage] Fetching UI: $url")
|
||||||
|
|
||||||
val response = client.get(url)
|
val response = client.get(url) {
|
||||||
|
header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36")
|
||||||
|
}
|
||||||
val body = response.bodyAsText()
|
val body = response.bodyAsText()
|
||||||
|
|
||||||
if (!response.status.isSuccess()) {
|
if (!response.status.isSuccess()) {
|
||||||
@ -151,6 +164,7 @@ class SageClient {
|
|||||||
url {
|
url {
|
||||||
params.forEach { (k, v) -> parameters.append(k, v) }
|
params.forEach { (k, v) -> parameters.append(k, v) }
|
||||||
}
|
}
|
||||||
|
header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36")
|
||||||
when {
|
when {
|
||||||
jsonBody != null -> {
|
jsonBody != null -> {
|
||||||
contentType(ContentType.Application.Json)
|
contentType(ContentType.Application.Json)
|
||||||
@ -169,6 +183,7 @@ class SageClient {
|
|||||||
url {
|
url {
|
||||||
params.forEach { (k, v) -> parameters.append(k, v) }
|
params.forEach { (k, v) -> parameters.append(k, v) }
|
||||||
}
|
}
|
||||||
|
header(HttpHeaders.UserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user