日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

接口使用jwt返回token_API接口JWT方式的Token认证(下),客户端(Android)的实现

發(fā)布時間:2024/9/27 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 接口使用jwt返回token_API接口JWT方式的Token认证(下),客户端(Android)的实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上篇文章已經(jīng)介紹了 JWT 認證在 Laravel 框架服務器上的實現(xiàn)。這篇文章繼續(xù)介紹 Android 客戶端的實現(xiàn)。回顧下 JWT 認證的流程,客戶端先提交賬號密碼進行登錄,賬號密碼驗證成功后,服務器會生成一個 token,其中包含了用戶信息,token 到期時間等信息,服務器將 token 返回給客戶端后不會保存此 token。客戶端接受到 token 后,需要對 token進行存儲,在以后訪問需要認證的 API 接口是,在 HTTP 請求通過認證頭提交 token,服務器校驗 token 的合法性,是否過期,攜帶的用戶信息是否匹配,全部通過后,完成驗證,之后才能完成后續(xù)操作。

先看一下已經(jīng)實現(xiàn)的 API 接口的路由:

$api = app('Dingo\Api\Routing\Router');

$api->version('v1', ['namespace' => 'App\Http\Controllers'], function ($api) {

$api->get('login', 'Auth\AuthenticateController@authenticate');

$api->post('register', 'Auth\RegisterController@register');

$api->group(['middleware' => 'jwt.auth', 'providers' => 'jwt'], function ($api) {

$api->get('user', 'UserController@getUserInfo');

$api->get('notices', 'NoticeController@index');

});

});1

2

3

4

5

6

7

8

9

10

111

2

3

4

5

6

7

8

9

10

11

其中 login 和 register 是用來獲取 token 的,而 user 和 notices 則需要客戶端提供 token 。下面我們就在 android 客戶端上實現(xiàn)對這些接口的訪問。

本文采用的 Android 代碼下載地址:

https://github.com/zhongchenyu/jokes

由于后續(xù)可能會重構代碼,本文使用的代碼保存在 demo2 分支。

1.構建 UI

在主頁新增一頁 MoreFragment,布局文件代碼如下:

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent" android:layout_height="match_parent"

tools:context="chenyu.jokes.feature.more.MoreFragment" android:orientation="vertical"

android:background="@color/bgGrey">

android:layout_width="match_parent" android:layout_height="wrap_content"

android:layout_marginTop="16dp" android:background="@android:color/white">

android:layout_width="80dp" android:layout_height="80dp"

android:layout_alignParentStart="true" app:srcCompat="@drawable/ic_36"

android:layout_marginStart="16dp" android:layout_marginTop="16dp"

android:layout_centerVertical="true" android:adjustViewBounds="false"/>

android:layout_width="wrap_content" android:layout_height="32dp"

android:visibility="invisible" android:textSize="24sp"

android:layout_toEndOf="@+id/avatar" android:layout_marginStart="16dp"

android:layout_alignParentTop="true" android:layout_marginTop="8dp"/>

android:layout_width="wrap_content" android:layout_height="32dp"

android:textSize="16sp" android:visibility="invisible"

android:layout_toEndOf="@+id/avatar" android:layout_marginStart="16dp"

android:layout_marginTop="8dp" android:layout_below="@+id/name"/>

android:text="登錄"

android:layout_width="72dp" android:layout_height="32dp"

android:layout_toEndOf="@+id/avatar" android:layout_centerVertical="true"

android:layout_marginStart="32dp" android:padding="0dp"

android:textColor="@android:color/white" android:textSize="16sp"

android:background="@drawable/selector_bg_corner"/>

android:text="注冊" android:padding="0dp"

android:layout_width="72dp" android:layout_height="32dp"

android:background="@drawable/selector_bg_corner" android:layout_toEndOf="@+id/login"

android:textColor="@android:color/white" android:textSize="16sp"

android:layout_centerVertical="true" android:layout_marginStart="16dp"/>

android:text="退出" android:padding="0dp"

android:layout_width="72dp" android:layout_height="32dp"

android:layout_marginEnd="16dp"

android:background="@drawable/selector_bg_corner" android:visibility="invisible"

android:textColor="@android:color/white" android:textSize="16sp"

android:layout_alignParentEnd="true" android:layout_centerVertical="true"/>

android:layout_width="match_parent" android:layout_height="wrap_content"

android:orientation="horizontal" android:layout_marginTop="8dp"

android:background="@android:color/white">

android:text="獲取通知"

android:layout_width="wrap_content" android:layout_height="wrap_content"

android:enabled="false" android:layout_gravity="top"

android:background="@drawable/selector_bg_corner" android:layout_marginTop="16dp"

android:textColor="@android:color/white" android:textSize="16sp"

android:layout_marginStart="16dp" android:layout_marginBottom="16dp"/>

android:layout_width="match_parent" android:layout_height="wrap_content"

android:textSize="16sp" android:layout_marginStart="16dp"

android:layout_marginTop="16dp"/>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

681

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

登錄之前效果如下,界面顯示登錄和注冊按鈕,獲取通知按鈕為不可點擊狀態(tài)。

登錄后效果如下,登錄和注冊按鈕隱藏,變?yōu)轱@示用戶名和郵箱,退出按鈕也被顯示出來,并且獲取通知按鈕變?yōu)榭梢渣c擊。

2. 實現(xiàn)注冊功能

在 ServiceAPI 下添加網(wǎng)絡接口:

@FormUrlEncoded @POST("register") Observable register(

@Field("name") String name,

@Field("email") String email,

@Field("password") String password

);1

2

3

4

51

2

3

4

5

我們用的是 MVP 架構,網(wǎng)絡請求是在 Presenter 中完成的,那么在 MorePresenter 的 onCreate 函數(shù)中注冊請求:

restartableFirst(REGISTER,

new Func0>() {

@Override public Observable call() {

return App.getServerAPI().register(mName, mEmail, mPassword) .subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());

}

},

new Action2() {

@Override public void call(MoreFragment moreFragment, Token token) {

moreFragment.onRegisterSuccess(token);

}

}, new Action2() {

@Override public void call(MoreFragment moreFragment, Throwable throwable) {

moreFragment.onError(throwable);

}

}

);1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

161

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

調用 register 網(wǎng)絡接口,在請求成功調用moreFragment 的 onRegisterSuccess 函數(shù)。

同時在 MorePresenter 中公開一個 register 函數(shù),供 View 層來調用,發(fā)起網(wǎng)絡請求:

public void register(String name, String email, String password) {

mName = name;

mEmail = email;

mPassword = password;

start(REGISTER);

}1

2

3

4

5

61

2

3

4

5

6

然后是 View 層的實現(xiàn),MoreFragment 中對注冊按鈕添加監(jiān)聽,點擊后彈出對話框進行注冊:

@OnClick({R.id.login, R.id.logout, R.id.register, R.id.notice}) public void click(View view) {

switch (view.getId()) {

...

case R.id.register:

showRegisterDialog();

break;

}

}1

2

3

4

5

6

7

81

2

3

4

5

6

7

8

在看下 showRegisterDialog() 函數(shù):

private void showRegisterDialog() {

AlertDialog.Builder builder = new AlertDialog.Builder(getContext());

builder.setIcon(R.mipmap.ic_launcher).setTitle("注冊");

View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_register, null);

builder.setView(view);

final EditText edtUserName = (EditText) view.findViewById(R.id.username);

final EditText edtPassword = (EditText) view.findViewById(R.id.password);

final EditText edtEmail = (EditText) view.findViewById(R.id.email);

final EditText edtPasswordConfirm = (EditText) view.findViewById(R.id.password_confirmation);

builder.setPositiveButton("確定", null);

builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {

@Override public void onClick(DialogInterface dialog, int which) {

}

});

final AlertDialog alertDialog = builder.create();

alertDialog.show();

alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(

new View.OnClickListener() {

@Override public void onClick(View v) {

String userName = edtUserName.getText().toString().trim();

String password = edtPassword.getText().toString().trim();

String email = edtEmail.getText().toString().trim();

String password_confirm = edtPasswordConfirm.getText().toString().trim();

if(! password.equals(password_confirm) ) {

Toast.makeText(getContext(), "兩次輸入密碼不一致", Toast.LENGTH_SHORT).show();

return;

}

getPresenter().register(userName, email, password);

alertDialog.dismiss();

}

});

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

381

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

AlertDialog 采用了自定義的 layout,包含 用戶名、郵箱、密碼、確認密碼 這4個文本編輯框。我們給確定按鈕注冊了一個空的監(jiān)聽器,這是因為在點擊確定時要驗證密碼和確認密碼是否相同,如果不同,彈出提示消息,對話框不會消失,這樣用戶才有機會進行修改,如果監(jiān)聽器不是 null,那用戶點擊確定后對話框必定會消失。所以這里給確定按鈕注冊一個空的 DialogInterface.OnClickListener,并在對話框顯示出來給,查找到確認按鈕,并注冊一個 View.OnClickListener,來實現(xiàn)上述需求。

如果兩次密碼確認一致,則調用 Presenter 中的 register 函數(shù),并取消對話框。

注冊成功后的相應比較簡單,直接彈出提示:

public void onRegisterSuccess(Token token) {

Toast.makeText(getContext(), "注冊成功,請登錄", Toast.LENGTH_SHORT).show();

}1

2

31

2

3

看下效果,點擊注冊,彈出對話框:

輸入密碼不一致時,點擊確定,彈出提示,對話框不消失:

密碼輸入一致,點擊確定,發(fā)起注冊請求,對話框消失,提示注冊成功:

3. 實現(xiàn)登錄功能

先看一下登錄接口返回的數(shù)據(jù),其中包含了用戶信息和 token:

{

"user": {

"id": 9,

"name": "user666",

"email": "user6@user.com"

},

"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjksImlzcyI6Imh0dHA6XC9cL2hvbWVzdGVhZC5hcHBcL2FwaVwvbG9naW4iLCJpYXQiOjE0OTM3NTQ0NjUsImV4cCI6MTQ5Mzc1ODA2NSwibmJmIjoxNDkzNzU0NDY1LCJqdGkiOiJGeTRmb2FYeWI5Q2RZTGlXIn0.Isu2XpPypZIMjB8P8Fis-qLknij6hdWfaQ_Jl1Gzo-o"

}1

2

3

4

5

6

7

81

2

3

4

5

6

7

8

登錄功能和注冊功能很相似,但是登錄成功后我們要根據(jù)服務器返回的用戶信息更新UI,并對 token進行存儲。

首先在 Model 路徑下創(chuàng)建 User 類和 Account 類用于解析和存儲網(wǎng)絡數(shù)據(jù):

@JsonIgnoreProperties(ignoreUnknown = true) public class User {

public String id;

public String name;

public String email;

}1

2

3

4

51

2

3

4

5

@JsonIgnoreProperties(ignoreUnknown = true) public class Account {

public User user;

public String token;

}1

2

3

41

2

3

4

ServiceAPI 增加 網(wǎng)絡接口,我們用 Account類來解析接口返回的 Json數(shù)據(jù):

@GET("login") Observable login(

@Query("email") String email,

@Query("password") String password

);1

2

3

41

2

3

4

接下來在 MorePresenter 的 onCreate 中注冊網(wǎng)絡請求:

restartableFirst(LOGIN,

new Func0>() {

@Override public Observable call() {

return App.getServerAPI().login(mEmail, mPassword)

.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());

}

},

new Action2() {

@Override public void call(MoreFragment moreFragment, Account account) {

moreFragment.onLoginSuccess(account);

}

},

new Action2() {

@Override public void call(MoreFragment moreFragment, Throwable throwable) {

moreFragment.onError(throwable);

}

}

);1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

181

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

網(wǎng)絡請求成功后會調用 MoreFragment 的 onLoginSuccess 函數(shù)。

同時在 MorePresenter 中公開 login 函數(shù)供外部調用:

public void login(String email, String password) {

mEmail = email;

mPassword = password;

start(LOGIN);

}1

2

3

4

51

2

3

4

5

接下來是 View 層處理,在 MoreFragment 中,點擊登錄按鈕后,彈出登錄對話框:

@OnClick({R.id.login, R.id.logout, R.id.register, R.id.notice}) public void click(View view) {

switch (view.getId()) {

case R.id.login:

showLoginDialog();

break;

...

}

}

private void showLoginDialog() {

AlertDialog.Builder builder = new AlertDialog.Builder(getContext());

builder.setIcon(R.mipmap.ic_launcher).setTitle("登錄");

View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_login, null);

builder.setView(view);

final EditText edtPassword = (EditText) view.findViewById(R.id.password);

final EditText edtEmail = (EditText) view.findViewById(R.id.email);

builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {

@Override public void onClick(DialogInterface dialog, int which) {

String password = edtPassword.getText().toString().trim();

String email = edtEmail.getText().toString().trim();

getPresenter().login( email, password);

}

});

builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {

@Override public void onClick(DialogInterface dialog, int whick) {

}

});

builder.show();

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

351

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

因為不需要做校驗,登錄對話框比注冊時簡單,點擊確定后就調用 MorePresenter 的 login 函數(shù),發(fā)送登錄請求。

再看一下登錄成功后的處理:

public void onLoginSuccess(Account account) {

AccountManager.create().setAccount(account);

mTxtName.setVisibility(View.VISIBLE);

mTxtName.setText(account.user.name);

mTxtEmail.setVisibility(View.VISIBLE);

mTxtEmail.setText(account.user.email);

mBtnLogin.setVisibility(View.INVISIBLE);

mBtnLogout.setVisibility(View.VISIBLE);

mBtnRegister.setVisibility(View.INVISIBLE);

mBtnNotice.setEnabled(true);

}1

2

3

4

5

6

7

8

9

10

111

2

3

4

5

6

7

8

9

10

11

首先對賬號信息進行存儲,包含用戶的 ID、name、email,以及此次的 token,這些信息會被保存到 SharedPreferences 里,AccountManager 是我們自定義的賬號管理類,可以在應用的任何地方存儲和獲取用戶信息,具體在下一節(jié)中介紹。

然后就是 UI 的變更了,登錄成功后將登錄和注冊按鈕隱藏,顯示用戶的 name 和email,顯示退出按鈕,將獲取通知按鈕設置為可點擊。

最后看下實現(xiàn)效果,點擊登錄按鈕,彈出對話框:

登錄成功后界面變化:

4. 實現(xiàn)全局賬號信息存取

JWT 的 token 的有效期一般設置為數(shù)小時,Laravel 下的 JWT 默認有效期為60分鐘。在這期間客戶端需要對 token 進行存儲,那么存儲在什么位置合適呢?因為 應用中任何位置都有可能訪問需要認證的 API,這個 token 需要在應用全局可用,不會隨著 Fragment 或者 Activity 的生命周期而消亡,并且在應用退出后也需要保留。

綜合考慮上面的需求,決定將賬戶信息保存到 SharedPreferences 中,由于使用 SharedPreferences 需要用到 context,因此在 Application 類中提供一個獲取全局 context 的方法,以便在任何地方都可以調用 AccountManager 類。

在 App 類下:

private static Context context;

@Override public void onCreate(){

super.onCreate();

context = getApplicationContext();

...

}

public static ServerAPI getServerAPI() {

return serverAPI;

}1

2

3

4

5

6

7

8

9

101

2

3

4

5

6

7

8

9

10

public class AccountManager {

private static SharedPreferences sp;

private static SharedPreferences.Editor editor;

public static AccountManager create() {

AccountManager accountManager = new AccountManager();

accountManager.sp = App.getAppContext().getSharedPreferences("account", 0);

accountManager.editor = sp.edit();

return accountManager;

}

public void setToken(String token) {

editor.putString("token", token);

editor.commit();

}

public String getToken() {

String token = sp.getString("token", "");

return token;

}

public void setAccount(Account account) {

editor.putString("token", account.token);

editor.putString("userId", account.user.id);

editor.putString("userEmail", account.user.email);

editor.putString("userName", account.user.name);

editor.commit();

}

public Account getAccount() {

Account account = new Account();

account.token = sp.getString("token", "");

account.user.id = sp.getString("userId", "");

account.user.name = sp.getString("userEmail", "");

account.user.email = sp.getString("userEmail", "");

return account;

}

public void clearAccount() {

editor.putString("token", "");

editor.putString("userId", "");

editor.putString("userEmail", "");

editor.putString("userName", "");

editor.commit();

}

public void setUser(User user) {

editor.putString("userId", user.id);

editor.putString("userEmail", user.email);

editor.putString("userName", user.name);

editor.commit();

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

531

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

代碼比較簡單,提供一個靜態(tài)函數(shù) create 來創(chuàng)建并返回 AccountManager,同時做好 SharedPreferences 存取的準備工作,這里用到了 App 里的getAppContext() 函數(shù)來獲取全局 context。之后提供了對賬號Account

的存儲、讀取和清除函數(shù),也可以單獨存取 User 和 token。

4. 訪問需要認證的 API

獲取到 token 之后就可以訪問 需要認證的 API 了,服務器已經(jīng)準備好了兩個 API,一個是簡單測試用的 notices API,認證成功就返回一段話,還有一個就是 user API,認證成功后返回 User 信息,user API 當前用來做校驗 token 是否有效使用,在后面的章節(jié)介紹,這一節(jié)只介紹 notices API。

首先添加 Model,在 ServiceAPI 下創(chuàng)建網(wǎng)絡接口:

public class Notice {

public String content;

}1

2

31

2

3

@GET("notices") Observable getNotice(

@Header("Authorization") String token

);1

2

31

2

3

注意和之前的接口不同,這里添加了 @Header 注解,這樣發(fā)送網(wǎng)絡請求時會添加認證頭。

接下來 MorePresenter 注冊請求,公開函數(shù),和之前的基本類似,不同的是在訪問 API 接口時,調用了 AccountManager 來獲取 token,注意 token 前加了 Bearer:

restartableFirst(NOTICE,

new Func0>() {

@Override public Observable call() {

return App.getServerAPI().getNotice("Bearer " + AccountManager.create().getToken())

.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());

}

},

new Action2() {

@Override public void call(MoreFragment moreFragment, Notice notice) {

moreFragment.onGetNoticeSuccess(notice);

}

},

new Action2() {

@Override public void call(MoreFragment moreFragment, Throwable throwable) {

moreFragment.onError(throwable);

}

}

);

public void getNotice() {

start(NOTICE);

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

211

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

然后是 View 層處理,也很簡單,點擊獲取通知按鈕,調用 MorePresenter 的 getNotice函數(shù),請求成功后,顯示獲取的通知消息:

@OnClick({R.id.login, R.id.logout, R.id.register, R.id.notice}) public void click(View view) {

switch (view.getId()) {

...

case R.id.notice:

getPresenter().getNotice();

break;

}

}

public void onGetNoticeSuccess(Notice notice) {

mTxtNotice.setText(notice.content);

}1

2

3

4

5

6

7

8

9

10

11

121

2

3

4

5

6

7

8

9

10

11

12

最后看下效果,登錄成功后獲取通知:

假如 token 已經(jīng)過期,我們再取點擊按鈕,則無法通過認證:

5. 實現(xiàn)退出賬號

因為 JWT 是無狀態(tài)無連接的認證方式,服務器上不需要保存 token 狀態(tài),因此退出時只需要清除掉客戶端本地的賬號信息就行了,不需要和服務器作交互。

看下實現(xiàn)代碼,調用 AccountManager 清除掉存儲的賬號信息,并恢復 UI 到登錄前的樣子就行了。

@OnClick({R.id.login, R.id.logout, R.id.register, R.id.notice}) public void click(View view) {

switch (view.getId()) {

case R.id.logout:

AccountManager.create().clearAccount();

mBtnLogin.setVisibility(View.VISIBLE);

mBtnLogout.setVisibility(View.INVISIBLE);

mBtnRegister.setVisibility(View.VISIBLE);

mBtnNotice.setEnabled(false);

mTxtName.setVisibility(View.INVISIBLE);

mTxtEmail.setVisibility(View.INVISIBLE);

mTxtNotice.setText("");

break;

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

161

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

6. UI 恢復 和 token 檢測

上面的代碼已經(jīng)實現(xiàn)了登錄成功后用戶信息和 token 的存儲,那么我們希望在應用或者特定的 View 啟動的時候,能夠將存儲的用戶信息恢復到 UI 上,并且檢測下存儲的 token 是否有效,是否過期,如果未過期,則自動恢復 UI 到已登錄的狀態(tài),不需要用戶再登錄。綜上,我們在 MoreFragment 啟動的時候,訪問 user API 接口,攜帶存儲的 token,給服務器驗證,如果驗證成功,則恢復 UI 到登錄成功后的樣子,如果驗證失敗,則保留未登錄的狀態(tài),等待用戶再次輸入賬號密碼進行登錄。

要實現(xiàn)上述功能,和之前的代碼一樣的,首先創(chuàng)建好 Model、ServiceAPI 接口、MorePresenter中注冊好請求,具體代碼就不貼了,都是類似的。主要看下 MoreFragment 的代碼,我們在 onCreateView 里處理:

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_more, container, false);

ButterKnife.bind(this, view);

if(AccountManager.create().getToken() != "") {

getPresenter().getUserInfo();

}

return view;

}1

2

3

4

5

6

7

8

9

10

111

2

3

4

5

6

7

8

9

10

11

首先通過 AccountManager 獲取存儲的 token,如果 token 是空的,說明之前就是未登錄狀態(tài),不需要處理,UI 或保持初始的未登錄狀態(tài),如果 token 非空,則調用 MorePresenter 來訪問 user API。

如果認證失敗,則彈出提示,UI 不會有變化,保持未登錄狀態(tài)。如果認證成功,則調用 MoreFragment 的 onGetUserSuccess 函數(shù)來更新UI,這里恢復 UI 時用戶信息的來源可以是本地 SharedPreferences,也可以是服務器剛返回的數(shù)據(jù),正常情況下兩者應該是一樣的,但是我們認為服務器的數(shù)據(jù)更可信,因而采用服務器的數(shù)據(jù)更新 UI,并將服務器的 User 數(shù)據(jù)進行存儲。

public void onGetUserSuccess(User user) {

AccountManager.create().setUser(user);

mTxtName.setVisibility(View.VISIBLE);

mTxtName.setText(user.name);

mTxtEmail.setVisibility(View.VISIBLE);

mTxtEmail.setText(user.email);

mBtnLogin.setVisibility(View.INVISIBLE);

mBtnLogout.setVisibility(View.VISIBLE);

mBtnRegister.setVisibility(View.INVISIBLE);

mBtnNotice.setEnabled(true);

}1

2

3

4

5

6

7

8

9

10

111

2

3

4

5

6

7

8

9

10

11

為了測試效果,我們特意將服務器上的 token 有效期配置為1分鐘,修改服務器的 .env 文件,設置 JWT_TTL=1 。

看下效果,登錄成功或退出應用,在 token 過期前重新啟動應用,進入 MoreFragment 頁面,自動進入已登錄狀態(tài):

再次退出應用,等 token 過期后,啟動應用,提示未認證,進入 MoreFragment 頁面,處于未登錄狀態(tài):

后記

JWT 方式的 API 基本功能,以及 Laravel 服務器和 Android 客戶端的實現(xiàn)方式就介紹完了,JWT 這種無狀態(tài)的方式還是很適合 API 認證的,客戶端只需要生成和驗證 token,客戶端只需要存儲 token 就行,token 有效期就存儲在 token 自身,不需要服務器為每個登錄的用戶去存儲 token 狀態(tài),這樣大大減小了開銷。并且 token 本身就包含了用戶 ID 等一些非敏感信息,因此在很多網(wǎng)絡請求的時候,甚至可以只傳輸 token,不需要再有單獨的用戶信息參數(shù),也是減少了一筆開銷。

上面介紹的內容可以完成 JWT 認證的基本功能了,但還是有很多可以改善的地方,比如 password 是明文傳輸?shù)?#xff0c;很不安全,這個作為一個 Demo 項目,就沒考慮這么周全。另外由于 JWT 方式一個天生的缺點,服務器無法控制 token 的有效期,只要你發(fā)出了一個 token,它的有效期就定死了,因為服務器不存儲 token 狀態(tài),所有就無法提前結束 token 生命周期。

因此在配置 token 有效期是要比較謹慎,不能太長了。但是太短也不行,因為 token 方式,包括除了 JWT 外的其他 token方式,其實就是用 token 代替賬號密碼作為用戶驗證的憑證,只要一次賬號密碼驗證通過,后續(xù)一段時間內只需要 token 就可以驗證,不需要密碼,降低風險,有效期太短必然導致密碼頻繁發(fā)送,且用戶需要頻繁地登錄,影響用戶體驗。所有要根據(jù)實際情況選擇一個合適的有效期。

另外 token 到期后如何處理也是個問題。如果用戶沒使用應用的時候 token 過期了,那還好點,想想用戶正在操作應用的時候,突然 token 就到期了,操作被中斷,需要重新登錄,那一定是一件很不爽的事情。JWT 本身也提供了一種解決方法,設置了一個 token 刷新時間,在 token 過期但是沒超過刷新時間的情況下,用舊的 token 可以獲取到新的 token。另外也可以考慮在每次發(fā)送 API 請求的時候都去刷新 token,或者周期性發(fā)送心跳包來更新 token,不過這在并發(fā)請求比較多的時候,也會涉及到異步?jīng)_突的問題,需要謹慎考慮。

后續(xù)如果有時間,再深入研究下這些問題。

總結

以上是生活随笔為你收集整理的接口使用jwt返回token_API接口JWT方式的Token认证(下),客户端(Android)的实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。