security(chat): secure chat stream endpoint with jwt validation and token extraction
This commit is contained in:
parent
05392384b6
commit
6d12291941
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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<List<ChatSession>>(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)
|
||||
|
|
|
|||
|
|
@ -22,11 +22,12 @@ class AiChatRepository {
|
|||
.readTimeout(0, TimeUnit.MILLISECONDS) // SSE 需要关闭读取超时
|
||||
.build()
|
||||
|
||||
fun streamChat(message: String): Flow<String> = callbackFlow {
|
||||
fun streamChat(message: String, token: String): Flow<String> = 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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue