При первом входе в систему выдается 401 несанкционированный доступ на Android Kotlin

#android #node.js #android-studio #kotlin

#Android #node.js #android-studio #Kotlin

Вопрос:

Я разрабатываю приложение для Android, которое требует аутентификации (входа в систему).

  • Когда пользователь входит в систему, токен-носитель jwt генерируется из моего серверной части
  • Я сохраняю этот токен, используя SharedPreference
  • Используя модифицированный перехватчик OkHttp, я добавляю перехватчик, которому требуется этот сохраненный токен предъявителя для доступа к авторизованным конечным точкам.

Проблема в том

  • Когда пользователь входит в систему в первый раз и посещает любую авторизованную конечную точку, он выдает 401 — несанкционированный. Пользователь должен закрыть приложение, затем открыть его (без входа в систему), конечные точки начинают работать, возвращая 200. (Тем временем я выполнил проверку, и токен на предъявителя был отправлен и сохранен в приложении) но по какой-то причине в первый раз конечные точки возвращают 401. Если я не закрою приложение и не открою его, то конечные точки выдадут 200.

Серверные ребята говорят, что это из моего приложения, потому что отправлен токен на предъявителя, и он работает в первый раз на почтовом отправителе. Серверная часть использует NodeJS.

LoginActivity

 if (it.data?.status == "success") {

   //get the token and store it
   val token = it.data.token

   //store token
   PreferenceHelper.storeToken = token

  //store user session
  PreferenceHelper.isLoggedInSeller = true

  //go to home activity
  val intent = Intent(this, SellerHomeActivity::class.java)
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
  startActivity(intent)           

}
 

PreferenceHelper

 object PreferenceHelper {

private const val NAME = "session"
private const val MODE = Context.MODE_PRIVATE
private lateinit var preferences: SharedPreferences

//SharedPreferences variables
private val hasLoggedInSeller = Pair("isLoginSeller", false)
private val loginToken = Pair("login_token", "")


fun init(context: Context) {
    preferences = context.getSharedPreferences(NAME, MODE)
}

//an inline function to put variable and save it
private inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
    val editor = edit()
    operation(editor)
    editor.apply()
}

//check if seller has logged in
var isLoggedInSeller: Boolean
    get() = preferences.getBoolean(hasLoggedInSeller.first, hasLoggedInSeller.second)
    set(value) = preferences.edit {
        it.putBoolean(hasLoggedInSeller.first, value)
    }

//store login token for buyer
var storeToken: String?
    get() = preferences.getString(loginToken.first, loginToken.second)
    set(value) = preferences.edit {
        it.putString(loginToken.first, value)
    }
}
 

AuthInterceptor

 class AuthInterceptor : Interceptor {

var  token = PreferenceHelper.storeToken

override fun intercept(chain: Interceptor.Chain): Response {

    val requestBuilder = chain.request().newBuilder()

    // If token has been saved, add it to the request
    token?.let {
        requestBuilder.addHeader("Authorization", "Bearer $it")
    }


    return chain.proceed(requestBuilder.build())
 }

}
 

Модернизация

  @Provides
@Singleton
fun provideRetrofit(gson: Gson) : Retrofit = Retrofit.Builder()
    .baseUrl(EndPoints.BASE_URL)
    .client(
        OkHttpClient.Builder().also { client ->
            val logging = HttpLoggingInterceptor()
            if (BuildConfig.DEBUG) {
                logging.setLevel(HttpLoggingInterceptor.Level.BODY)
            }
            client.addInterceptor(logging)
            client.addInterceptor(AuthInterceptor())
            client.connectTimeout(120, TimeUnit.SECONDS)
            client.readTimeout(120, TimeUnit.SECONDS)
            client.protocols(Collections.singletonList(Protocol.HTTP_1_1))
        }.build()
    )
    .addConverterFactory(ScalarsConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()
 

Выход

 //remove session and token
PreferenceHelper.storeToken = ""
PreferenceHelper.isLoggedInSeller = false
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(intent)
 

Ответ №1:

Проблема заключается в Interceptor том, чтобы переместить token оператор внутри intercept метода, который будет запускаться для каждого запроса, иначе он будет создаваться только один раз для каждого экземпляра и будет продолжать использовать один и тот же токен

 class AuthInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val requestBuilder = chain.request().newBuilder()
        PreferenceHelper.storeToken?.let {
            requestBuilder.addHeader("Authorization", "Bearer $it")
        }
        return chain.proceed(requestBuilder.build())
    }
}