我一直在试图弄清楚如何为我的Android应用验证用户。它基于已经开发了api的网站,使用JWT进行身份验证。
我反对刷新令牌的问题。假设我想从API获取内容,我需要auth令牌。我检查我当前的身份验证令牌。如果它已过期,我需要使用某种刷新令牌来获取新的。
然而,似乎无论我怎么想实现它,我都遇到了一些问题:
我提出了一个解决#1的解决方案,至少可以减少#2的痛苦。我可以使用某种getToken
方法。举个例子,使用JS风格的承诺,因为它让我更容易理解:
function getToken() {
return new Promise((resolve) => {
// Check for token, and return if valid.
// Otherwise, go to the server and get a new one
...
resolve(token)
}
}
// When making an API call
getToken().then((token) => {
// Call API
})
我想我可以解决这个问题,因为请求永远不会在UI线程上运行,这解决了#1,而就#2而言,它至少是可以忍受的。
我的问题是:有更好的方法吗?似乎AccountManager
可能能够为我处理这类事情,但它的文档充其量只是次要的,所以我不确定如何实现它。如果AccountManager
能够做到并且你知道一个很好的教程,请对此进行评论。
答案 0 :(得分:1)
实现此目的的方法是拦截401状态代码并刷新令牌。
如果您使用的是Volley,则可以扩展Request
类并覆盖parseNetworkEror(VolleyError error)
方法。如果需要,请安排一个作业,该作业将刷新令牌(JobDispatcher)并触发事件以传达有关更改的UI(EventBus)。
以下示例使用OAuth身份验证,但可以轻松更改以实现JWT。
@Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
if (getDataAccess().shouldRefreshToken(volleyError)) {
if (!EventBus.getDefault().hasSubscriberForEvent(TokenRefreshedEvent.class)) {
EventBus.getDefault().register(this);
}
CSApplication app = CSApplication.getInstance();
FirebaseJobDispatcher dispatcher = app.getJobDispatcher(app.getApplicationContext());
Job myJob = dispatcher.newJobBuilder()
.setService(JobRefreshToken.class)
.setTag("REFRESH_TOKEN")
.setTrigger(Trigger.NOW)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(Constraint.ON_ANY_NETWORK)
.build();
int result = dispatcher.schedule(myJob);
if (result == FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS) {
LogUtils.log(LogUtils.Type.JOB, GsonRequest.class, "Scheduling job refresh token");
} else {
LogUtils.log(LogUtils.Type.JOB, GsonRequest.class, "Error on schedule refresh token");
}
}
return super.parseNetworkError(volleyError);
}
public boolean shouldRefreshToken(VolleyError error) {
boolean shouldRefreshToken = error.networkResponse != null && error.networkResponse.statusCode == 401;
if (shouldRefreshToken) {
Map<String, String> headers = error.networkResponse.headers;
if (headers.containsKey("WWW-Authenticate")) {
String value = headers.get("WWW-Authenticate");
boolean issuerInvalid = value.contains("The issuer is invalid");
shouldRefreshToken = !issuerInvalid;
if (issuerInvalid) {
log(LogUtils.Type.VOLLEY, DataAccess.class, "Issuer do token é inválido");
}
}
}
return shouldRefreshToken;
}
职位代码
getDataAccess().refreshToken(getApplicationContext(), new VolleyCallback<Void>() {
@Override
public void onSuccess(Void aVoid) {
EventBus.getDefault().post(new TokenRefreshedEvent(true));
job.jobFinished(params, false);
log(LogUtils.Type.JOB, JobRefreshToken.class, "Refresh Token job finished");
}
@Override
public void onError(VolleyError error) {
super.onError(error);
EventBus.getDefault().post(new TokenRefreshedEvent(false));
job.jobFinished(params, false);
}
});
return true;
}
答案 1 :(得分:0)
我最终做的是创建一个方法getToken
,它返回当前令牌或获取新令牌(阻止)。有了这个策略,我需要确保永远不会从UI线程调用它。我创建了一个Retrofit2
拦截器,调用getToken
。这种方法的好处是我可以调用我的Retrofit方法而不必担心令牌,它会检查到期并在必要时获得一个新的。