安卓 | 使用Retrofit2和本地API进行基本身份验证

4
我开始制作一个应用程序,并首先将其连接到模拟API。现在我想将其连接到在我的PC上运行的API。
首先,我正在尝试实现登录访问。由于我的API的基本URL为http://localhost:5000/energy/api/,我将其更改为http://<< MyIPAddress >>:5000/energy/api/(还不知道这是否正确)。我正在我的实际手机上进行测试。 但是,我的代码中传递了用户名和密码作为参数,而我想使用基本身份验证登录(这是我在Postman中的做法)。 例如,这就是我想要进行身份验证的方式: enter image description here 但是,我的代码却得到了这个结果: enter image description here 这是我的请求管理器:
object RequestManager {
    val interceptor = HttpLoggingInterceptor()
    val client = OkHttpClient.Builder().addInterceptor(interceptor).build()
    init {
        interceptor.level = HttpLoggingInterceptor.Level.BODY
    }

    val retrofit = Retrofit.Builder()
        .baseUrl("http://<<MYIP>>:5000/energy/api/")
        .addConverterFactory(GsonConverterFactory.create())
        .client(client)
        .build()

    val service = retrofit.create(Api::class.java)

我的 MainActivity.kt:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        login_button.setOnClickListener {
            buttonClicked()
        }

        if (getTokenFromPrefs()?.length!! > 0) {
            openSearchActivity()
        }
    }

    private fun buttonClicked() {
        val username = username_edittext.text.toString()
        val password = password_edittext.text.toString()
        Log.d("eeee","button clicked " + username_edittext.text.toString() + " password "+password_edittext.text.toString() )

        val call = RequestManager.service.login(username, password)
        call.enqueue(object : Callback<LoginResponse> {
            override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
                if (response.isSuccessful ){
                        openSearchActivity()
                        saveTokenToPrefs(response.toString())
                    }else {

                }
            }

            override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
            }
        })
    }

    val TOKENPREFSKEY = "tokenprefskey"
    private fun saveTokenToPrefs(token: String) {
        val pref = applicationContext.getSharedPreferences("CGEEnergy", 0)
        val editor = pref.edit()
        editor.putString(TOKENPREFSKEY, token)
        editor.commit()
    }

    private fun getTokenFromPrefs(): String? {
        val pref = applicationContext.getSharedPreferences("CGEEnergy", 0)
        return pref.getString(TOKENPREFSKEY, "")
    }

    private fun openSearchActivity() {
        val intent = Intent(this, SearchActivity::class.java)
        startActivity(intent)
        if (getTokenFromPrefs()?.length!! == 0) {
            openMainActivity()
        }
        finish()
    }
    private fun openMainActivity() {
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
        finish()
    }
}

我的 API.kt 代码:(由用户 Hello World 编辑和更正)

interface Api {
    @POST("Login")
    @FormUrlEncoded
    fun login(@Field("username") username: String, @Field("password") password: String): Call<LoginResponse>
}

我的LoginResponse.java代码:

public class LoginResponse {

    @SerializedName("token")
    @Expose
    private String token;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

}

有人可以帮我处理基本身份验证吗? 我的基础URL是否也有问题?(添加了IP而不是localhost)。 任何提示都将不胜感激。

PS. 关于HTTP安全问题,我已经添加了。

android:usesCleartextTraffic="true"

在我的AndroidManifest.xml文件中。

进行一个简单的测试来验证您的基本URL,使用您的设备尝试在浏览器中打开此IP。 - Azhagthott
@FranciscoBarrios 如果你的意思是通过Chrome导航到http://myIPaddress:5000/,那我不能 :/ - ZookKep
好的,我不确定这是否是问题,但你应该看一下,你的电脑可能有一个类似于192.168.0.xxx的IP地址,而你的手机(不是模拟器)应该在同一网段内,检查一下你的电脑和手机的IP地址以确保它们在同一网段内 ;) - Azhagthott
@FranciscoBarrios 我尝试在安卓浏览器中运行基本URL,但它显示网站无法访问。 - ZookKep
你是否在Retrofit API类中添加了基本身份验证头?您需要将Authorization Header添加到请求中,并添加文本:Basic Base64.encode(<username>:<password>) 例如 Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l - P Fuster
@PFuster,您能否详细说明一下?或者给我提供一个链接?我不太明白我应该添加什么。 - ZookKep
2个回答

5

基本认证要求以以下格式包含授权头: "Basic " + Base64.encode(<username>:<password>)

您应该像这样更改您的 API 接口:

interface Api {
    @POST("Login")
    fun login(@Header("Authorization") authorization: String): Call<LoginResponse>
}

现在您可以像这样向调用添加Auth标头:

val authPayload = "$userId:$password"
val data = authPayload.toByteArray()
val base64 = Base64.encodeToString(data, Base64.NO_WRAP)

val call = RequestManager.service.login("Basic $base64".trim())

1
@Query

这个注释是用于'GET'方法的。对于'POST'方法,您需要在函数名称前添加@FormUrlEncoded。并且您还需要使用@Field而不是@Query。因此,您的API调用接口方法将变成这样。
@POST("Login")
@FormUrlEncoded
fun login(@Field("username") username: String, @Field("password") password: String): Call<LoginResponse>

这非常有帮助!我尝试了,但它仍然无法工作(因为它仍然存在身份验证问题),但我肯定解决了一个我甚至不知道存在的问题!谢谢! - ZookKep
我很想这样做,但它并不能解决我的身份验证问题,这才是主要问题:/ - ZookKep
请尝试将您的IP更改为“10.0.2.2”。 - Hello World
我在我的实际手机上测试过,所以我不认为这会起作用。 - ZookKep

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接