前言:服務器
以前分享過在Android中使用Retrofit實現token失效刷新的處理方案,如今Flutter項目也有「token驗證過時」的需求。剛開始我使用的是EventBus來通知彈出登陸頁面,可是發如今refresh token過時後並無去登陸,緣由是EventBus須要在有生命週期的State狀態中才能發送消息,在這裏我構造了全局的上下文,以便彈出登陸頁面。因此接下來我簡單總結一下在Flutter項目中如何實現自動刷新token並重發請求的攔截器功能,但願對你們有所幫助。網絡
需求:異步
1.有兩個token, 分別爲access_token和refresh_Token, access_token的有效期爲1小時,refresh_Token的有效期爲7天;async
2.若是access_token1個小時後過時了,服務器會返回401,此時客戶端要根據刷新access_token的retrofit接口去從新請求新的access_token;ide
3.若是refresh_Token7天后也過時了,則要求跳到登陸頁面。工具
思路: gradle
1.Dio實現網絡請求ui
2.自定義token攔截器,實現token自動刷新並重發請求url
3.RefreshToken過時,彈出登陸頁面。調試
實現的步驟:
1.配置Android目錄的gradle依賴
dependencies { ... implementation 'de.greenrobot:eventbus:3.0.0-beta1' }
2.在pubspec.yaml添加sdk
dependencies: ... cupertino_icons: ^0.1.0 dio: ^2.1.7
3.封裝一個獲取新的accessToken方法
Future<String> getToken() async { String refreshToken = DataUtil.getRefreshToken; //獲取當前的refreshToken String accessToken; Dio tokenDio = new Dio(); //建立新Dio實例 tokenDio.options.headers['refresh-token'] = refreshToken;//設置當前的refreshToken try { String url = url; //refreshToken url var response = await tokenDio.get(url); //請求refreshToken刷新的接口 accessToken = response.data['access_token']; //新的accessToken refreshToken = response.data['refresh_token'];//新的refreshToken DataUtil.saveRefreshToken(refreshToken); //保存新的refreshToken } on DioError catch (e) { if(e.response.statusCode==401){ //401表明refresh_token過時 //refreshToken過時,彈出登陸頁面 //解決方法一:封裝一個全局的context //return Navigator.of(Util.context).push(new MaterialPageRoute( builder: (ctx) => new LoginPage())); //解決方法二:當refresh token過時,把退出的登陸的操做放在dio網絡請求的工具類去操做 } } return accessToken; }
4.token失效時,異步獲取accessToken
onError(DioError error) async { if (error.response != null && error.response.statusCode == 401) { 401表明access token過時 Dio dio = DioUtil().dio;//獲取Dio單例 dio.lock(); //加鎖 String accessToken = await getToken(); //異步獲取新的accessToken ... dio.unlock(); //解鎖 } super.onError(error); }
5.從新發起一個請求獲取數據
//從新發起一個請求獲取數據 Dio tokenDio2 = new Dio(); //建立新的dio實例 tokenDio2.options.headers['access-token'] = accessToken; try { var newRequest = await tokenDio2.request(request.path); return newRequest; } on DioError catch (e) { return e; }
6.Dio攔截器的完整代碼
typedef void ChildContext(BuildContext context); class TokenInterceptor extends Interceptor { ChildContext context; //上下文 @override onError(DioError error) async { if (error.response != null && error.response.statusCode == 401) { //401表明token過時 Dio dio = DioUtil().dio;//獲取Dio單例 dio.lock(); //加鎖 String accessToken = await getToken(); //異步獲取新的accessToken //從新發起一個請求獲取數據 Dio tokenDio2 = new Dio();//建立新的Dio實例 tokenDio2.options.headers['access-token'] = accessToken; try { var newRequest = await tokenDio2.request(request.path); return newRequest; } on DioError catch (e) { return e; } dio.unlock(); //解鎖 } super.onError(error); } Future<String> getToken() async { String refreshToken = DataUtil.getRefreshToken; //獲取當前的refreshToken String accessToken ; Dio tokenDio =new Dio(); //建立新的Dio實例 tokenDio.options.headers['refresh-token'] = refreshToken ;//設置當前的refreshToken try { String url = url; //refreshToken url var response = await tokenDio.get(url); //請求refreshToken刷新的接口 accessToken = response.data['access_token']; //新的accessToken refreshToken = response.data['refresh_token'];//新的refreshToken DataUtil.saveAccessToken(accessToken); //保存新的accessToken DataUtil.saveRefreshToken(refreshToken); //保存新的refreshToken } on DioError catch (e) { if(e.response.statusCode==401){//401表明refresh_token過時 //refreshToken過時,彈出登陸頁面 //解決方法一:封裝一個全局的context //return Navigator.of(Util.context).push(new MaterialPageRoute( builder: (ctx) => new LoginPage())); //解決方法二:當refresh token過時,把退出的登陸的操做放在dio網絡請求的工具類去操做 } } return accessToken; } }
7.添加自定義的token攔截器
/* *Dio網絡請求的工具類 */ class DioUtil { Dio dio; static DioUtil _instance; static DioUtil getInstance() { if (_instance == null) { _instance = DioUtil(); } return _instance; } //get方法 Future<Response> get(url, {data, options, cancelToken}) async { String accessToken = DataUtil.getAccessToken; //獲取當前的accessToken options = BaseOptions( connectTimeout: 15000, headers: { Constants.accessToken: accessToken }, ); dio = new Dio(options); //添加自定義的token攔截器 dio.interceptors.add(new TokenInterceptor()); Response response; try { response = await dio.get(url, cancelToken: cancelToken); } on DioError catch (e) { print(e.response.data); } return response; } }
8.總結
在Flutter項目中自定義一個自動刷新並重發請求的Dio攔截器,通過不斷調試,最終實現了功能。若是有什麼疑問的話,歡迎留言聯繫我哦!