如何使用 JWT 为 Android 个应用程序实施刷新令牌流程
How to implement a refresh token process with JWT for Android apps
我正在使用 Oauth2 令牌系统访问我的 Android 应用程序的 REST API。我在客户端的令牌刷新部分遇到了一些问题。
流程如下:我的应用程序向服务器发出请求(在参数中带有访问令牌)感谢一些异步任务(PostCommentAsyncTask()
、AddFriendAsyncTask()
等...),所以如果accessToken 有效没关系,但如果它已过期,我从先例 AsyncTask
的 onPostExecute()
方法中调用另一个 AsyncTask
(GetRefreshTokenAsyncTask()
) 来获取新的 accessToken。这对我来说是棘手的部分。当我获得新的访问令牌时,我想重新执行对服务器的初始 AsyncTask 请求。我不知道如何正确地做到这一点。
示例 1:
request PostCommentAsyncTask()
--> (acessToken expired) -->GetRefreshTokenAsyncTask()
-->request PostCommentAsyncTask()
--> (good token)--> Ok
编辑:
我最终选择了使用 Volley
库(不再需要使用 Asynctask 了)。
当我使用 JSON Web Token
时,我可以检查令牌有效负载中编码的过期日期。
这里是 isAccessTokenExpired()
在向服务器发出请求之前检查访问令牌是否未过期的方法:
public Boolean isAccessTokenExpired(String accessToken){
String[] accessTokenPart = accessToken.split("\.");
String header =accessTokenPart[0];
String payload =accessTokenPart[1];
String signature =accessTokenPart[2];
try {
byte[] decodedPayload = Base64.decode(payload, Base64.DEFAULT);
payload = new String(decodedPayload,"UTF-8");
} catch(UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
JSONObject obj = new JSONObject(payload);
int expireDate = obj.getInt("exp");
Timestamp timestampExpireDate= new Timestamp( expireDate);
long time = System.currentTimeMillis();
Timestamp timestamp = new Timestamp(time);
return timestamp.after(timestampExpireDate);
} catch (JSONException e) {
e.printStackTrace();
return true;
}
}
这是从我的 OAUTH2 服务器获取一对新的访问 token/Refresh 令牌的 refreshJsonWebToken()
方法:
public void refreshJsonWebToken(){
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
String refreshToken = settings.getString("refreshToken", null);
final HashMap<String, String> params = new HashMap<String, String>();
params.put("grant_type","refresh_token");
params.put("client_id","client");
params.put("refresh_token",refreshToken);
JsonObjectRequest req = new JsonObjectRequest(URL_OAUTH2, new JSONObject(params), new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
try {
String newRefreshToken = response.getString("refresh_token");
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("accessToken", newAccessToken);
editor.putString("refreshToken", newRefreshToken);
editor.apply();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("grid", "Error: " + error.getMessage());
}
}
});
AppController.getInstance().addToRequestQueue(req);
}
最后是 getPost()
方法,我使用了先例方法:
private void getPost(String latitude, String longitude) {
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
String accessToken = settings.getString("accessToken", null);
final HashMap<String, String> params = new HashMap<String, String>();
params.put("action", "getLocalPosts");
params.put("latitude", latitude);
params.put("longitude", longitude);
if (isAccessTokenExpired(accessToken)){
refreshJsonWebToken();
}
settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
accessToken = settings.getString("accessToken", null);
JsonObjectRequest req = new JsonObjectRequest(URL_APP+accessToken, new JSONObject(params), new Response.Listener<JSONObject>() {
//Some code ....
});
AppController.getInstance().addToRequestQueue(req);
}
我认为 Handler
在这种情况下更好,因为 Looper
有同步消息队列,这在这里很方便。您创建一个 HandlerThread
并将您的 Handler
与其相关联。然后你可以根据需要调用 postRunnable
,例如您添加 PostCommentRunnable
,如果令牌已过期,您添加 GetRefreshTokenRunnable
和 PostCommentRunnable
,它们将按顺序执行。
如果你还想用AsyncTasks
,你能不能在启动PostCommentAsyncTask
之前检查令牌是否过期?我认为那将是一个更好的设计。如果你不能,那么你可以一个接一个地执行它们,因为它们默认在同一个后台线程上工作,例如:
new PostCommentAsyncTask().execute();
class PostCommentAsyncTask extends AsyncTask {
//...
onPostExecute() {
if (tokenExpired) {
new GetRefreshTokenAsyncTask().execute();
new PostCommentAsyncTask().execute(); // this guy will wait till refresh token is executed.
}
}
}
我正在使用 Oauth2 令牌系统访问我的 Android 应用程序的 REST API。我在客户端的令牌刷新部分遇到了一些问题。
流程如下:我的应用程序向服务器发出请求(在参数中带有访问令牌)感谢一些异步任务(PostCommentAsyncTask()
、AddFriendAsyncTask()
等...),所以如果accessToken 有效没关系,但如果它已过期,我从先例 AsyncTask
的 onPostExecute()
方法中调用另一个 AsyncTask
(GetRefreshTokenAsyncTask()
) 来获取新的 accessToken。这对我来说是棘手的部分。当我获得新的访问令牌时,我想重新执行对服务器的初始 AsyncTask 请求。我不知道如何正确地做到这一点。
示例 1:
request PostCommentAsyncTask()
--> (acessToken expired) -->GetRefreshTokenAsyncTask()
-->request PostCommentAsyncTask()
--> (good token)--> Ok
编辑:
我最终选择了使用 Volley
库(不再需要使用 Asynctask 了)。
当我使用 JSON Web Token
时,我可以检查令牌有效负载中编码的过期日期。
这里是 isAccessTokenExpired()
在向服务器发出请求之前检查访问令牌是否未过期的方法:
public Boolean isAccessTokenExpired(String accessToken){
String[] accessTokenPart = accessToken.split("\.");
String header =accessTokenPart[0];
String payload =accessTokenPart[1];
String signature =accessTokenPart[2];
try {
byte[] decodedPayload = Base64.decode(payload, Base64.DEFAULT);
payload = new String(decodedPayload,"UTF-8");
} catch(UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
JSONObject obj = new JSONObject(payload);
int expireDate = obj.getInt("exp");
Timestamp timestampExpireDate= new Timestamp( expireDate);
long time = System.currentTimeMillis();
Timestamp timestamp = new Timestamp(time);
return timestamp.after(timestampExpireDate);
} catch (JSONException e) {
e.printStackTrace();
return true;
}
}
这是从我的 OAUTH2 服务器获取一对新的访问 token/Refresh 令牌的 refreshJsonWebToken()
方法:
public void refreshJsonWebToken(){
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
String refreshToken = settings.getString("refreshToken", null);
final HashMap<String, String> params = new HashMap<String, String>();
params.put("grant_type","refresh_token");
params.put("client_id","client");
params.put("refresh_token",refreshToken);
JsonObjectRequest req = new JsonObjectRequest(URL_OAUTH2, new JSONObject(params), new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
try {
String newRefreshToken = response.getString("refresh_token");
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("accessToken", newAccessToken);
editor.putString("refreshToken", newRefreshToken);
editor.apply();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("grid", "Error: " + error.getMessage());
}
}
});
AppController.getInstance().addToRequestQueue(req);
}
最后是 getPost()
方法,我使用了先例方法:
private void getPost(String latitude, String longitude) {
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
String accessToken = settings.getString("accessToken", null);
final HashMap<String, String> params = new HashMap<String, String>();
params.put("action", "getLocalPosts");
params.put("latitude", latitude);
params.put("longitude", longitude);
if (isAccessTokenExpired(accessToken)){
refreshJsonWebToken();
}
settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
accessToken = settings.getString("accessToken", null);
JsonObjectRequest req = new JsonObjectRequest(URL_APP+accessToken, new JSONObject(params), new Response.Listener<JSONObject>() {
//Some code ....
});
AppController.getInstance().addToRequestQueue(req);
}
我认为 Handler
在这种情况下更好,因为 Looper
有同步消息队列,这在这里很方便。您创建一个 HandlerThread
并将您的 Handler
与其相关联。然后你可以根据需要调用 postRunnable
,例如您添加 PostCommentRunnable
,如果令牌已过期,您添加 GetRefreshTokenRunnable
和 PostCommentRunnable
,它们将按顺序执行。
如果你还想用AsyncTasks
,你能不能在启动PostCommentAsyncTask
之前检查令牌是否过期?我认为那将是一个更好的设计。如果你不能,那么你可以一个接一个地执行它们,因为它们默认在同一个后台线程上工作,例如:
new PostCommentAsyncTask().execute();
class PostCommentAsyncTask extends AsyncTask {
//...
onPostExecute() {
if (tokenExpired) {
new GetRefreshTokenAsyncTask().execute();
new PostCommentAsyncTask().execute(); // this guy will wait till refresh token is executed.
}
}
}