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_REMEMBER_LOGIN = "remember_login"
|
||||||
private const val KEY_USERNAME = "saved_username"
|
private const val KEY_USERNAME = "saved_username"
|
||||||
private const val KEY_PASSWORD = "saved_password"
|
private const val KEY_PASSWORD = "saved_password"
|
||||||
|
private const val KEY_ACCESS_TOKEN = "access_token"
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
const val DEFAULT_SERVER_URL = "https://template.gyouzhe.com"
|
const val DEFAULT_SERVER_URL = "https://template.gyouzhe.com"
|
||||||
|
|
@ -68,4 +69,12 @@ object AppConfig {
|
||||||
fun setSavedPassword(context: Context, password: String) {
|
fun setSavedPassword(context: Context, password: String) {
|
||||||
getPrefs(context).edit().putString(KEY_PASSWORD, password).apply()
|
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 data = json.optJSONObject("data")
|
||||||
val loginData = data?.toString() ?: "{}"
|
val loginData = data?.toString() ?: "{}"
|
||||||
val accessToken = data?.optString("access_token", "") ?: ""
|
val accessToken = data?.optString("access_token", "") ?: ""
|
||||||
|
AppConfig.setAccessToken(this@LoginActivity, accessToken)
|
||||||
|
|
||||||
Toast.makeText(applicationContext, "登录成功", Toast.LENGTH_SHORT).show()
|
Toast.makeText(applicationContext, "登录成功", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.stand.standapp.ui.chat
|
package com.stand.standapp.ui.chat
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.stand.standapp.AppConfig
|
||||||
import com.stand.standapp.ui.chat.model.ChatMessage
|
import com.stand.standapp.ui.chat.model.ChatMessage
|
||||||
import com.stand.standapp.ui.chat.model.ChatSession
|
import com.stand.standapp.ui.chat.model.ChatSession
|
||||||
import com.stand.standapp.ui.chat.repo.AiChatRepository
|
import com.stand.standapp.ui.chat.repo.AiChatRepository
|
||||||
|
|
@ -13,7 +15,7 @@ import kotlinx.coroutines.flow.onCompletion
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class ChatViewModel : ViewModel() {
|
class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
private val repository = AiChatRepository()
|
private val repository = AiChatRepository()
|
||||||
|
|
||||||
private val _sessions = MutableStateFlow<List<ChatSession>>(emptyList())
|
private val _sessions = MutableStateFlow<List<ChatSession>>(emptyList())
|
||||||
|
|
@ -56,7 +58,8 @@ class ChatViewModel : ViewModel() {
|
||||||
|
|
||||||
// 4. 发起网络请求,流式追加内容
|
// 4. 发起网络请求,流式追加内容
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
repository.streamChat(content)
|
val token = AppConfig.getAccessToken(getApplication())
|
||||||
|
repository.streamChat(content, token)
|
||||||
.catch { e ->
|
.catch { e ->
|
||||||
appendAiMessageChunk(aiMsgId, "\n[请求异常: ${e.message}]")
|
appendAiMessageChunk(aiMsgId, "\n[请求异常: ${e.message}]")
|
||||||
finalizeAiMessage(aiMsgId)
|
finalizeAiMessage(aiMsgId)
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,12 @@ class AiChatRepository {
|
||||||
.readTimeout(0, TimeUnit.MILLISECONDS) // SSE 需要关闭读取超时
|
.readTimeout(0, TimeUnit.MILLISECONDS) // SSE 需要关闭读取超时
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun streamChat(message: String): Flow<String> = callbackFlow {
|
fun streamChat(message: String, token: String): Flow<String> = callbackFlow {
|
||||||
val json = """{"message": "$message"}"""
|
val json = """{"message": "$message"}"""
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(backendUrl)
|
.url(backendUrl)
|
||||||
.post(json.toRequestBody("application/json".toMediaType()))
|
.post(json.toRequestBody("application/json".toMediaType()))
|
||||||
|
.header("Authorization", "Bearer $token")
|
||||||
.header("Accept", "text/event-stream")
|
.header("Accept", "text/event-stream")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue