SpringBoot框架集成token實現登錄校驗功能

簡介

公司新項目,需要做移動端(Android和IOS),登錄模塊,兩個移動端人員提出用token來校驗登錄狀態,一臉懵懵的,沒做過,對於token的基本定義都模棱兩可,然後查資料查查查,最終OK完成,寫篇博客記錄一下

思路:

1、基於session登錄

基於session的登錄(有回話狀態),用戶攜帶賬號密碼發送請求向服務器,服務器進行判斷,成功後將用戶信息放入session,用戶發送請求判斷session中是否有用戶信息,有的話放行,沒有的話進行攔截,但是考慮到時App產品,牽扯到要判斷用戶的session,需要sessionID,還要根據sessionId來獲取session,在進行校驗,還有sessionId的一個存儲等等,所以沒考慮用session

2、基於token登錄

基於token的登錄,是不存在回話狀態,大概思路,在用戶初次等路的時候,校驗用戶賬號密碼,成功後給其生成一個token,token=用戶ID+時間戳+過期時間+一個自己平臺規定的簽名,使用jjwt生成一個令牌,然後對其進行存庫,用戶每次訪問接口,都會在頭部Headers中帶上token,後來攔截器對其進行攔截,如果token爲空或錯誤則讓其登錄,如果有token,獲取token進行其解析,取出裏面的用戶ID,根據用戶ID查詢數據庫中所存token,判斷其是否正確,正確使其登錄,錯誤則提示登錄,大致思路就是這樣,下面開始代碼

導入jar包

1

2

3

4

5

6

<!-- 生成token -->

<dependency>

 <groupId>io.jsonwebtoken</groupId>

 <artifactId>jjwt</artifactId>

 <version>0.9.0</version>

</dependency>

開發步驟

1、創建token庫

201982384808369.jpg?201972384818uploading.4e448015.gif轉存失敗重新上傳取消

2、創建token實體類

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

package com.prereadweb.user.entity;

  

import lombok.Data;

  

/**

 * @Description: Token實體類

 * @author: Yangxf

 * @date: 2019/4/14 12:53

 */

@Data

public class TokenEntity {

  

 /* tokenId */

 private Long id;

  

 /* 用戶ID */

 private Long userId;

  

 /* 刷新時間 */

 private int buildTime;

  

 /* token */

 private String token;

  

}

3、編寫token的三個方法(添加、查詢、修改)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package com.prereadweb.user.mapper;

  

import com.prereadweb.user.entity.TokenEntity;

import org.apache.ibatis.annotations.Mapper;

  

/**

 * @Description: Token數據庫持久層接口

 * @author: Yangxf

 * @date: 2019/4/14 13:00

 */

@Mapper

public interface TokenMapper {

  

 /* 添加token */

 void addToken(TokenEntity token);

  

 /* 修改token */

 void updataToken(TokenEntity token);

  

 /* 查詢token */

 TokenEntity findByUserId(Long userId);

  

}

4、創建攔截器

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

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

package com.prereadweb.user.interceptor;

  

import com.prereadweb.user.entity.TokenEntity;

import com.prereadweb.user.mapper.TokenMapper;

import io.jsonwebtoken.Claims;

import io.jsonwebtoken.Jwts;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

  

import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.util.Date;

  

/**

 * @Description:攔截器

 * @author: Yangxf

 * @date: 2019/4/14 12:58

 */

public class LoginInterceptor implements HandlerInterceptor {

  

 @Autowired

 protected TokenMapper tokenMapper;

 //提供查詢

 @Override

 public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)

  throws Exception {}

 @Override

 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)

  throws Exception {}

 @Override

 public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {

 //此處爲不需要登錄的接口放行

 if (arg0.getRequestURI().contains("/login") || arg0.getRequestURI().contains("/register") || arg0.getRequestURI().contains("/error") || arg0.getRequestURI().contains("/static")) {

  return true;

 }

 //權限路徑攔截

 //PrintWriter resultWriter = arg1.getOutputStream();

 // TODO: 有時候用PrintWriter 回報 getWriter() has already been called for this response

 //換成ServletOutputStream就OK了

 arg1.setContentType("text/html;charset=utf-8");

 ServletOutputStream resultWriter = arg1.getOutputStream();

 final String headerToken=arg0.getHeader("token");

 //判斷請求信息

 if(null==headerToken||headerToken.trim().equals("")){

  resultWriter.write("你沒有token,需要登錄".getBytes());

  resultWriter.flush();

  resultWriter.close();

  return false;

 }

 //解析Token信息

 try {

  Claims claims = Jwts.parser().setSigningKey("preRead").parseClaimsJws(headerToken).getBody();

  String tokenUserId=(String)claims.get("userId");

  long iTokenUserId = Long.parseLong(tokenUserId);

  //根據客戶Token查找數據庫Token

  TokenEntity myToken= tokenMapper.findByUserId(iTokenUserId);

  

  //數據庫沒有Token記錄

  if(null==myToken) {

  resultWriter.write("我沒有你的token?,需要登錄".getBytes());

  resultWriter.flush();

  resultWriter.close();

  return false;

  }

  //數據庫Token與客戶Token比較

  if( !headerToken.equals(myToken.getToken()) ){

  resultWriter.print("你的token修改過?,需要登錄");

  resultWriter.flush();

  resultWriter.close();

  return false;

  }

  //判斷Token過期

  Date tokenDate= claims.getExpiration();

  int overTime=(int)(new Date().getTime()-tokenDate.getTime())/1000;

  if(overTime>60*60*24*3){

  resultWriter.write("你的token過期了?,需要登錄".getBytes());

  resultWriter.flush();

  resultWriter.close();

  return false;

  }

  

 } catch (Exception e) {

  resultWriter.write("反正token不對,需要登錄".getBytes());

  resultWriter.flush();

  resultWriter.close();

  return false;

 }

 //最後才放行

 return true;

 }

  

}

5、配置攔截器

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

package com.prereadweb.user.config;

  

import com.prereadweb.user.interceptor.LoginInterceptor;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

  

/**

 * @Description: 攔截器配置

 * @author: Yangxf

 * @date: 2019/4/14 13:09

 */

@Configuration

public class LoginConfiguration implements WebMvcConfigurer {

  

 /**

 * @Function: 這個方法才能在攔截器中自動注入查詢數據庫的對象

 * @author: YangXueFeng

 * @Date: 2019/4/14 13:10

 */

 @Bean

 LoginInterceptor loginInterceptor() {

 return new LoginInterceptor();

 }

  

 /**

 * @Function: 配置生成器:添加一個攔截器,攔截路徑爲login以後的路徑

 * @author: YangXueFeng

 * @Date: 2019/4/14 13:10

 */

 @Override

 public void addInterceptors(InterceptorRegistry registry ){

 registry.addInterceptor(loginInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/register", "/static");

 }

}

6、登錄

controller層

1

2

3

4

@RequestMapping("/getlogin")

public Object login(@Param("..") LoginQueryForm loginForm) {

return userViewService.login(loginForm);

}

serrvice層

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

@Override

 public Map<String, Object> login(LoginQueryForm loginForm) {

  

 Map<String, Object> map = new HashMap<>();

 //手機驗證碼登錄

 if(!Util.isEmpty(loginForm.getPhoneCode())) {

  return phoneCodeLogin(loginForm, map);

 }

 //判斷用戶信息爲空

 if (Util.isEmpty(loginForm.getPhone()) || Util.isEmpty(loginForm.getLoginPwd())) {

  return checkParameter(map);

 }

 //根據手機號查詢user對象

 UserEntity user = userMapper.getUser(loginForm.getPhone());

  

 //判斷用戶不存在

 if (Util.isEmpty(user)) {

  map.put("code", UserStatusEnum.USER_NON_EXISTENT.intKey());

  map.put("msg", UserStatusEnum.USER_NON_EXISTENT.value());

  return map;

 }

 /* 判斷密碼 */

 if(!MD5Util.string2MD5(loginForm.getLoginPwd()).equals(user.getLoginPwd())){

  map.put("code", UserStatusEnum.PWD_ERROR.intKey());

  map.put("msg", UserStatusEnum.PWD_ERROR.value());

  return map;

 }

  

 //根據數據庫的用戶信息查詢Token

 return operateToKen(map, user, user.getId());

 }

token操作

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

private Map<String, Object> operateToKen(Map<String, Object> map, UserEntity user, long userId) {

 //根據數據庫的用戶信息查詢Token

 TokenEntity token = tokenmapper.findByUserId(userId);

 //爲生成Token準備

 String TokenStr = "";

 Date date = new Date();

 int nowTime = (int) (date.getTime() / 1000);

 //生成Token

 TokenStr = creatToken(userId, date);

 if (null == token) {

  //第一次登陸

  token = new TokenEntity();

  token.setToken(TokenStr);

  token.setBuildTime(nowTime);

  token.setUserId(userId);

  token.setId(Long.valueOf(IdUtils.getPrimaryKey()));

  tokenmapper.addToken(token);

 }else{

  //登陸就更新Token信息

  TokenStr = creatToken(userId, date);

  token.setToken(TokenStr);

  token.setBuildTime(nowTime);

  tokenmapper.updataToken(token);

 }

 UserQueryForm queryForm = getUserInfo(user, TokenStr);

 /* 將用戶信息存入session */

 /*SessionContext sessionContext = SessionContext.getInstance();

 HttpSession session = sessionContext.getSession();

 httpSession.setAttribute("userInfo", user);*/

 //返回Token信息給客戶端

 successful(map);

 map.put("data", queryForm);

 return map;

 }

生成token

1

2

3

4

5

6

7

8

9

10

11

private String creatToken(Long userId, Date date) {

 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

 JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT") // 設置header

  .setHeaderParam("alg", "HS256").setIssuedAt(date) // 設置簽發時間

  .setExpiration(new Date(date.getTime() + 1000 * 60 * 60))

  .claim("userId",String.valueOf(userId) ) // 設置內容

  .setIssuer("lws")// 設置簽發人

  .signWith(signatureAlgorithm, "簽名"); // 簽名,需要算法和key

 String jwt = builder.compact();

 return jwt;

 }

至此,token登錄OK

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

您可能感興趣的文章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章