spring集成jwt驗證方式,token驗證

爲何要告別session?有這樣一個場景,系統的數據量達到千萬級,須要幾臺服務器部署,當一個用戶在其中一臺服務器登陸後,用session保存其登陸信息,其餘服務器怎麼知道該用戶登陸了?(單點登陸),固然解決辦法有,能夠用spring-session。若是該系統同時爲移動端服務呢?移動端經過url向後臺要數據,若是用session,經過sessionId識別用戶,萬一sessionId被截獲了,別人能夠利用sessionId向後臺要數據,就有安全隱患了。因此有必要跟session說拜拜了。服務端不須要存儲任何用戶的信息,用戶的驗證應該放在客戶端,jwt就是這種方式!

什麼是jwt?

最詳細的是官網:https://jwt.io/java

這裏寫圖片描述

這裏以java的ssm框架爲例,集成jwt。

1.pom.xml 導入jwt的包web

<!-- jwt --> <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>2.2.0</version> </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.編寫jwt的工具類,有加密解密功能就好spring

import com.auth0.jwt.JWTSigner; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper; import java.util.HashMap; import java.util.Map; public class JWT { private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK sdfkjsdrow32234545fdf>?N<:{LWPW"; private static final String EXP = "exp"; private static final String PAYLOAD = "payload"; //加密,傳入一個對象和有效期 public static <T> String sign(T object, long maxAge) { try { final JWTSigner signer = new JWTSigner(SECRET); final Map<String, Object> claims = new HashMap<String, Object>(); ObjectMapper mapper = new ObjectMapper(); String jsonString = mapper.writeValueAsString(object); claims.put(PAYLOAD, jsonString); claims.put(EXP, System.currentTimeMillis() + maxAge); return signer.sign(claims); } catch(Exception e) { return null; } } //解密,傳入一個加密後的token字符串和解密後的類型 public static<T> T unsign(String jwt, Class<T> classT) { final JWTVerifier verifier = new JWTVerifier(SECRET); try { final Map<String,Object> claims= verifier.verify(jwt); if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) { long exp = (Long)claims.get(EXP); long currentTimeMillis = System.currentTimeMillis(); if (exp > currentTimeMillis) { String json = (String)claims.get(PAYLOAD); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.readValue(json, classT); } } return null; } catch (Exception e) { return null; } } } 
  • 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

3.jwt有了,ssm要如何去利用,用戶驗證的第一步是登陸,登陸時根據用戶傳來的username和password到數據庫驗證身份,若是合法,便給該用戶jwt加密生成token數據庫

//處理登陸 @RequestMapping(value="login", produces = "application/json; charset=utf-8") public @ResponseBody ResponseData login(HttpServletRequest request, @RequestParam( "email") String email, @RequestParam("password") String password) { Login login = new Login(); login.setEmail(email); login.setPassword(password); ResponseData responseData = ResponseData.ok(); //先到數據庫驗證 Integer loginId = userService.checkLogin(login); if(null != loginId) { User user = userService.getUserByLoginId(loginId); login.setId(loginId); //給用戶jwt加密生成token String token = JWT.sign(login, 60L* 1000L* 30L); //封裝成對象返回給客戶端 responseData.putDataValue("loginId", login.getId()); responseData.putDataValue("token", token); responseData.putDataValue("user", user); } else{ responseData = ResponseData.customerError(); } return responseData; }
  • 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

4.在用戶登陸時,把loginId和token返回給前臺,之後用戶每次請求時,都得帶上這兩個參數,後臺拿到token後解密出loginId,與用戶傳遞過來的loginId比較,若是相同,則說明用戶身份合法。由於是每一個登陸事後的每一個請求,這裏用springmvc的攔截器作json

<mvc:interceptors> <mvc:interceptor> <!-- 匹配的是url路徑, 若是不配置或/**,將攔截全部的Controller --> <mvc:mapping path="/**" /> <!-- /register 和 /login 不須要攔截--> <mvc:exclude-mapping path="/register" /> <mvc:exclude-mapping path="/login" /> <bean class="com.xforce.charles.interceptor.TokenInterceptor"></bean> </mvc:interceptor> <!-- 當設置多個攔截器時,先按順序調用preHandle方法,而後逆序調用每一個攔截器的postHandle和afterCompletion方法 --> </mvc:interceptors> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

5.攔截器代碼安全

import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSONObject; import com.xforce.charles.model.Admin; import com.xforce.charles.model.Login; import com.xforce.charles.util.JWT; import com.xforce.charles.util.ResponseData; public class TokenInterceptor implements HandlerInterceptor{ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception arg3) throws Exception { } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView model) throws Exception { } //攔截每一個請求 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { response.setCharacterEncoding("utf-8"); String token = request.getParameter("token"); ResponseData responseData = ResponseData.ok(); //token不存在 if(null != token) { Login login = JWT.unsign(token, Login.class); String loginId = request.getParameter("loginId"); //解密token後的loginId與用戶傳來的loginId不一致,通常都是token過時 if(null != loginId && null != login) { if(Integer.parseInt(loginId) == login.getId()) { return true; } else{ responseData = ResponseData.forbidden(); responseMessage(response, response.getWriter(), responseData); return false; } } else { responseData = ResponseData.forbidden(); responseMessage(response, response.getWriter(), responseData); return false; } } else { responseData = ResponseData.forbidden(); responseMessage(response, response.getWriter(), responseData); return false; } } //請求不經過,返回錯誤信息給客戶端 private void responseMessage(HttpServletResponse response, PrintWriter out, ResponseData responseData) { responseData = ResponseData.forbidden(); response.setContentType("application/json; charset=utf-8"); String json = JSONObject.toJSONString(responseData); out.print(json); out.flush(); out.close(); } } 
  • 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

6.注意點:用@ResponseBody返回json數據時,有時會有亂碼,須要在springmvc的配置文件裏面加如下配置(spring4以上)服務器

<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /> </bean> </mvc:message-converters> </mvc:annotation-driven> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.最後分享一個類,用於返回給客戶端的萬能類,我以爲它能夠知足通常的接口session

import java.util.HashMap; import java.util.Map; public class ResponseData { private final String message; private final int code; private final Map<String, Object> data = new HashMap<String, Object>(); public String getMessage() { return message; } public int getCode() { return code; } public Map<String, Object> getData() { return data; } public ResponseData putDataValue(String key, Object value) { data.put(key, value); return this; } private ResponseData(int code, String message) { this.code = code; this.message = message; } public static ResponseData ok() { return new ResponseData(200, "Ok"); } public static ResponseData notFound() { return new ResponseData(404, "Not Found"); } public static ResponseData badRequest() { return new ResponseData(400, "Bad Request"); } public static ResponseData forbidden() { return new ResponseData(403, "Forbidden"); } public static ResponseData unauthorized() { return new ResponseData(401, "unauthorized"); } public static ResponseData serverInternalError() { return new ResponseData(500, "Server Internal Error"); } public static ResponseData customerError() { return new ResponseData(1001, "customer Error"); } }
相關文章
相關標籤/搜索