From 6d12291941fe97b7b9ad3761b4630c0db280a6c7 Mon Sep 17 00:00:00 2001 From: "844143714@qq,com" <844143714@qq,com> Date: Tue, 19 May 2026 22:24:29 +0800 Subject: [PATCH] security(chat): secure chat stream endpoint with jwt validation and token extraction --- app/src/main/java/com/stand/standapp/AppConfig.kt | 9 +++++++++ app/src/main/java/com/stand/standapp/LoginActivity.kt | 1 + .../java/com/stand/standapp/ui/chat/ChatViewModel.kt | 9 ++++++--- .../com/stand/standapp/ui/chat/repo/AiChatRepository.kt | 3 ++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/stand/standapp/AppConfig.kt b/app/src/main/java/com/stand/standapp/AppConfig.kt index 4d2f411..34c480a 100644 --- a/app/src/main/java/com/stand/standapp/AppConfig.kt +++ b/app/src/main/java/com/stand/standapp/AppConfig.kt @@ -10,6 +10,7 @@ object AppConfig { private const val KEY_REMEMBER_LOGIN = "remember_login" private const val KEY_USERNAME = "saved_username" private const val KEY_PASSWORD = "saved_password" + private const val KEY_ACCESS_TOKEN = "access_token" // 默认配置 const val DEFAULT_SERVER_URL = "https://template.gyouzhe.com" @@ -68,4 +69,12 @@ object AppConfig { fun setSavedPassword(context: Context, password: String) { getPrefs(context).edit().putString(KEY_PASSWORD, password).apply() } + + fun getAccessToken(context: Context): String { + return getPrefs(context).getString(KEY_ACCESS_TOKEN, "") ?: "" + } + + fun setAccessToken(context: Context, token: String) { + getPrefs(context).edit().putString(KEY_ACCESS_TOKEN, token).apply() + } } diff --git a/app/src/main/java/com/stand/standapp/LoginActivity.kt b/app/src/main/java/com/stand/standapp/LoginActivity.kt index 94ed468..70359b5 100644 --- a/app/src/main/java/com/stand/standapp/LoginActivity.kt +++ b/app/src/main/java/com/stand/standapp/LoginActivity.kt @@ -155,6 +155,7 @@ class LoginActivity : AppCompatActivity() { val data = json.optJSONObject("data") val loginData = data?.toString() ?: "{}" val accessToken = data?.optString("access_token", "") ?: "" + AppConfig.setAccessToken(this@LoginActivity, accessToken) Toast.makeText(applicationContext, "登录成功", Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt b/app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt index 9cb0a77..e409853 100644 --- a/app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt +++ b/app/src/main/java/com/stand/standapp/ui/chat/ChatViewModel.kt @@ -1,7 +1,9 @@ package com.stand.standapp.ui.chat -import androidx.lifecycle.ViewModel +import android.app.Application +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import com.stand.standapp.AppConfig import com.stand.standapp.ui.chat.model.ChatMessage import com.stand.standapp.ui.chat.model.ChatSession import com.stand.standapp.ui.chat.repo.AiChatRepository @@ -13,7 +15,7 @@ import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.launch import java.util.UUID -class ChatViewModel : ViewModel() { +class ChatViewModel(application: Application) : AndroidViewModel(application) { private val repository = AiChatRepository() private val _sessions = MutableStateFlow>(emptyList()) @@ -56,7 +58,8 @@ class ChatViewModel : ViewModel() { // 4. 发起网络请求,流式追加内容 viewModelScope.launch { - repository.streamChat(content) + val token = AppConfig.getAccessToken(getApplication()) + repository.streamChat(content, token) .catch { e -> appendAiMessageChunk(aiMsgId, "\n[请求异常: ${e.message}]") finalizeAiMessage(aiMsgId) diff --git a/app/src/main/java/com/stand/standapp/ui/chat/repo/AiChatRepository.kt b/app/src/main/java/com/stand/standapp/ui/chat/repo/AiChatRepository.kt index 735765c..e97f385 100644 --- a/app/src/main/java/com/stand/standapp/ui/chat/repo/AiChatRepository.kt +++ b/app/src/main/java/com/stand/standapp/ui/chat/repo/AiChatRepository.kt @@ -22,11 +22,12 @@ class AiChatRepository { .readTimeout(0, TimeUnit.MILLISECONDS) // SSE 需要关闭读取超时 .build() - fun streamChat(message: String): Flow = callbackFlow { + fun streamChat(message: String, token: String): Flow = callbackFlow { val json = """{"message": "$message"}""" val request = Request.Builder() .url(backendUrl) .post(json.toRequestBody("application/json".toMediaType())) + .header("Authorization", "Bearer $token") .header("Accept", "text/event-stream") .build()