HTTP是無狀態協議,用於傳輸數據。它啓用了客戶端和服務器端>之間的通信。它最初是爲了在Web瀏覽器和Web服務器之間建立連接而建立的。比如在網上購物,我們添加一些商品,例如。耳機到我們的購物車,然後,我們繼續尋找其他項目,在此期間,我們希望在執行任何其他任務是存儲購物車項目的狀態且不丟失它們。這意味着我們希望在整個購物過程中記住我們的狀態。由於HTTP是無狀態協議,因此要克服問題,我們可以使用會話或者令牌
1、基於會話的身份驗證
在JSON Web令牌出現之前,我們主要使用這種身份驗證。在這種身份驗證中,服務器負責身份驗證,客戶端不知道發送請求後服務器端會發生什麼。
那麼什麼是會話Cookie?
由於客戶端未指定Expires(過期時間)或Max-Age(最大上限)指令,因此在客戶端關閉時將其刪除。但是,Web瀏覽器可能會使用會話還原,這會使大多數會話Cookie永久保持狀態,就像從未關閉過瀏覽器一樣。
用戶在網絡瀏覽器上登錄網站發生什麼。例如,用戶登錄後,服務器 將爲該用戶創建一個會話並將該會話數據存儲在服務器內存中。當用戶在網站上執行某些活動時,會創建一個會話ID,該會話ID存儲在客戶端瀏覽器的cookie中。在用戶提出的每個請求中,cookie都將隨之發送。然後,當用戶最初登錄時,服務器可以使用存儲在服務器內存中的會話數據來驗證Cookie上的會話數據。當用戶從網站註銷時,該會話數據將從數據庫和服務器內存中刪除。
2、基於令牌的身份驗證
在基於令牌的身份驗證中,我們使用JWT(JSON Web Tokens)進行身份驗證。這是RESTful API的廣泛使用方法。
當用戶發送帶有登錄詳細信息的用戶身份驗證請求時,服務器將以JSON WEB TOKENS(JWT)的形式創建一個加密的令牌,並將其發送回客戶端。當客戶端收到令牌時,這意味着該用戶以通過身份驗證,可以使用客戶端執行任何活動。
JWT通常存儲在客戶端的localstorage中,當用戶從服務器請求任何數據或對該網站執行任何活動時,JWT將作爲該用戶的唯一密鑰發送。因此,當服務器接收到該請求時,它將針對每個請求驗證JWT僅是該特定用戶,然後將所需的響應發送回客戶端。
localStorage.setItem("key", "value");
用戶狀態存儲在客戶端JWT中。當用戶註銷時,令牌將從客戶端(localStorage)中刪除。因此,大多數數據存儲在客戶端,並且可以直接訪問,而不是向服務器發送請求。
JSON WEB TOKENS由(.)連接的三個部分組成:
1.標頭
2.有效載荷
3.簽名
JWT結構:
xxxxx.yyyyy.zzzzz
輸出包含三個由點分割的Base64-URL字符串,可以在HTML和HTTP環境中輕鬆傳遞這些字符串,與基於XML的標準(例如SAML)相比,它更緊湊。
JWT已對先前的標頭和有效負載進行了編碼,並用一個密鑰進行簽名,如下
哪個更好用?
在現代Web應用程序中,JWT被廣泛使用,因爲它的伸縮性優於基於會話的cookie,因爲令牌存儲在客戶端,而會話使用服務器內存來存儲用戶數據,這可能是一個大問題。大量用戶一次訪問應用程序。由於JWT是隨着每個請求一起發送的,而且包含所有用戶信息,因此即使對JWT進行了編碼,也有必要在JWT中使用必要的信息,並且應避免使用敏感信息或者將其加密以防止安全攻擊。
沒有固定的方法可以始終使用,它取決於開發人員和要求的類型,以找出在哪種情況下需要使用哪種方法。
3、jwt實現登錄
//定義JWT的有效時長七天
private static final long EXPIRE_TIME = 60 * 1000 * 60 * 24 * 7;
//簽發人
private static String ISSUER = "K_ang";
/*祕鑰*/
private static final String SING = "K*&^A%$#N@!G";
/**
* 生成令牌
*java項目www.fhadmin.org
* @param map
* @return
*/
public static String getToken(Map<String, String> map) {
//設置過期時間
Date date = null;
try {
date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//創建token
JWTCreator.Builder builder = JWT.create()
.withIssuer(ISSUER)
.withExpiresAt(date);
//添加信息
map.forEach((k, v) -> {
builder.withClaim(k, v);
});
return builder.sign(Algorithm.HMAC256(SING));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 驗證token
*java項目www.fhadmin.org
* @param token
*/
public static boolean verify(String token, String userNo) {
try {
//設置加密算法
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SING)).withClaim("userNo", userNo).build();
//校驗token
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 獲取token信息方法
*java項目www.fhadmin.org
* @param
* @return
*/
public static String getTokenInfo(String token) {
DecodedJWT decode = JWT.decode(token);
return decode.getClaim("userNo").asString();
}
}
//java項目www.fhadmin.org
@PostMapping("/login")
public Result login(@PathParam("empNo") String empNo, @PathParam("empPassword") String empPassword) {
if (empNo == null || "".equals(empNo)) {
return ResultUtil.error(103, "請輸入用戶名,用戶名不能爲空");
}
if (empPassword == null || "".equals(empPassword)) {
return ResultUtil.error(103, "請輸入密碼,密碼不能爲空");
}
Emp emp = empService.login(empNo, empPassword);
if (emp == null) {
return ResultUtil.error(103, "用戶不存在,獲取token失敗");
}
if (emp.getEmpPassword() == null || !emp.getEmpPassword().equals(empPassword)) {
return ResultUtil.error(103, "密碼錯誤,獲取token失敗");
}
//正常token
String token = JwtUtils.sign(empNo, empPassword);
emp.setToken(token);
return ResultUtil.success(200, "登錄成功", emp);
}