Я не могу получить токены на android-приложение с сервера django
Я пытаюсь понять, почему я не могу получить токены доступа в моем android-приложении с сервера django. Структура приложения для android кажется мне нормальной. Тем не менее проблема сохраняется.
Я протестировал ответ сервера с помощью postman, и он выглядит нормально (я получаю оба токена, доступ и обновление), но в моем приложении для android это не работает. Когда я вызываю функцию входа в систему из приложения, ответ сервера равен 200, но у меня нет токенов, сохраненных в xml-файле. Я делюсь с вами некоторыми фрагментами кода в надежде, что кто-нибудь здесь сможет мне помочь. Заранее благодарю любого!
ФУНКЦИЯ ВХОДА В СИСТЕМУ
suspend fun loginUser(userService: UserService, email: String, password: String, context: Context, navController: NavController) {
try {
val response = userService.loginUser(UserLoginRequest(email, password)).await()
if (response.isSuccessful) {
val tokenResponse = response.body()!!
TokenManager.saveTokens(tokenResponse.accessToken, tokenResponse.refreshToken)
withContext(Dispatchers.Main) {
navController.navigate(Screen.FeedScreen.route)
}
} else {
withContext(Dispatchers.Main) {
Toast.makeText(context, "Login failed: ${response.message()}", Toast.LENGTH_LONG).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(context, "An error occurred: ${e.localizedMessage}", Toast.LENGTH_LONG).show()
}
}
}
МЕНЕДЖЕР ТОКЕНОВ
package com.example.events.util
import android.content.SharedPreferences
import android.util.Log
import com.example.events.network.NetworkModule
import com.example.events.network.RefreshRequestBody
import com.example.events.network.RefreshTokenResponse
import com.example.events.network.TokenRefreshService
import com.google.gson.Gson
object TokenManager {
private lateinit var preferences: SharedPreferences
// Initialise with SharedPreferences
fun init(preferences: SharedPreferences) {
this.preferences = preferences
}
val accessToken: String?
get() = preferences.getString("access", null)
fun isTokenExpired(): Boolean {
val expiry = preferences.getLong("expiry", 0)
return expiry < System.currentTimeMillis()
}
fun refreshToken(): String? {
val refreshTokenValue = preferences.getString("refresh", null) ?: return null
val refreshRequestBody = RefreshRequestBody(refreshTokenValue)
try {
val call = NetworkModule.retrofit.create(TokenRefreshService::class.java)
.refreshToken(refreshRequestBody)
val response = call.execute()
if (response.isSuccessful) {
val refreshTokenResponse =
Gson().fromJson(response.body()?.string(), RefreshTokenResponse::class.java)
refreshTokenResponse?.let {
with(preferences.edit()) {
putString("access", it.access)
putString("refresh", it.refresh)
putLong(
"expiry",
System.currentTimeMillis() + 1000 * 60 * 5
) // Adjust based on actual token expiry from backend
apply()
}
return it.access
}
}
} catch (e: Exception) {
e.printStackTrace()
}
// No need for finally block to close the ResponseBody when using Retrofit
return null
}
fun saveTokens(accessToken: String, refreshToken: String) {
if (accessToken.isNotEmpty() && refreshToken.isNotEmpty()) {
preferences.edit().apply {
putString("access", accessToken)
putString("refresh", refreshToken)
apply()
}
} else {
Log.e("TokenManager", "Attempted to save null or empty tokens.")
}
}
}
СЕРВИСНАЯ ФУНКЦИЯ ПОЛЬЗОВАТЕЛЯ
@Headers("Content-Type: application/json")
@POST("/users/login/")
fun loginUser(@Body data: UserLoginRequest): Deferred<Response<TokenResponse>>
СЕТЕВОЙ МОДУЛЬ
package com.example.events.network
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.net.CookieManager
import java.net.CookiePolicy
object NetworkModule {
private const val BASE_URL = "http://192.168.0.100:8000/"
val retrofit: Retrofit by lazy {
/*val cookieManager = CookieManager().apply {
setCookiePolicy(CookiePolicy.ACCEPT_ALL)
}*/
val client = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor()) // Add the AuthInterceptor
// .cookieJar(JavaNetCookieJar(cookieManager)) // Add Cookie Manager
.build()
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
}
AUTH INTERCEPTOR
package com.example.events.network
import com.example.events.util.TokenManager
import okhttp3.Interceptor
import okhttp3.Response
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val accessToken = TokenManager.accessToken
val requestBuilder = chain.request().newBuilder()
// Add the Authorization header
accessToken?.let {
requestBuilder.addHeader("Authorization", "Bearer $it")
}
// Proceed with the original request
val originalResponse = chain.proceed(requestBuilder.build())
// Check if the token is expired after receiving the response
if (TokenManager.isTokenExpired()) {
synchronized(this) {
// Close the original response to avoid IllegalStateException
originalResponse.close()
val newAccessToken = TokenManager.refreshToken()
newAccessToken?.let {
// Add the new access token to the Authorization header
requestBuilder.addHeader("Authorization", "Bearer $it")
}
// Retry the request with the new access token
return chain.proceed(requestBuilder.build())
}
}
// Return the original response if the token was not expired
return originalResponse
}
}
ОТВЕТ НА ТОКЕН
package com.example.events.model
data class TokenResponse(
val accessToken: String,
val refreshToken: String
)
ЗАПРОС НА ВХОД ПОЛЬЗОВАТЕЛЯ
package com.example.events.model.user
data class UserLoginRequest(
val email: String, // or username, in future
val password: String
)