網上的java基礎教程曾教會我們,將用戶登錄信息存在session(服務器端)中,需要驗證的時候拿出來作對比以達到身份
驗證的效果。但這種方式暴露的問題也是可想而知的:
1.Seesion:每次認證用戶發起請求時,服務器需要去創建一個記錄來存儲信息。當越來越多的用戶發請求時,內存的開銷也會不斷增加。
2.可擴展性:在服務端的內存中使用Seesion存儲登錄信息,伴隨而來的是可擴展性問題。
3.CORS(跨域資源共享):當我們需要讓數據跨多台移動設備上使用時,跨域資源的共享會是一個讓人頭疼的問題。在使用Ajax抓取另一個
域的資源,就可以會出現禁止請求的情況。
4.CSRF(跨站請求偽造):用戶在訪問網站時,他們很容易受到跨站請求偽造的攻擊,並且能夠被利用其訪問其他的網站。
在這些問題中,可擴展行是最突出的。因此我們有必要去尋求一種更有行之有效的方法。
而token卻能解決在服務端存儲信息時的許多問題:
1.用戶登錄
和往常一樣,一個表單,輸入用戶名、密碼然后登錄
<form action="servlet/Addcookies" method="post"> <label>賬 號:</label><input type="text" name="username" /><br> <label>密 碼:</label><input type="password" name="password" /><br> <button type="submit">登錄</button> </form>
可以看到,這個表單提交到一個servlet
2.servlet
下面看這個servlet
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username"); String Token = null; try { Token = JwtToken.creatToken(username); } catch (Exception e) { e.printStackTrace(); } Cookie cookie = new Cookie("token", Token); cookie.setPath("/"); // 設置路徑使得cookie共享 cookie.setMaxAge(1000);// 設置cookie有效期 response.addCookie(cookie);// 向客戶端添加cookie response.sendRedirect("../index2.jsp");// 請求轉發 }
根據表單發來的請求,獲取username參數值;
根據username參數值生成Token(具體方法后面講);
新建一個cookie,key值為token,value值為之前生成的Token;
頁面跳轉到index2.jsp。
3.index2.jsp
頁面沒什么,直接看js
//定義一個function,根據name獲取該cookie的value值 function getCookie(name){ //獲取cookies並分割成數組形式 var strcookie = document.cookie; var arrcookie = strcookie.split("; "); //遍歷所有cookie,並根據參數返回對應cookie的vaule for ( var i = 0; i < arrcookie.length; i++) { var arr = arrcookie[i].split("="); if (arr[0] == name){ return encodeURIComponent(arr[1]); } } return "找不到指定cookie或cookie過期了"; } var token=getCookie("token"); document.getElementById("go").innerHTML=token;
這樣,就將存在cookie里面的token拿到了,之后需要驗證用戶權限的話,就將這個token作為參數放到請求中;
服務端接收到這個攜帶token的請求,會對token解析,如果解析成功,就表示權限認證通過。
4.Token的生成與解析
直接看這個工具類
package com.eco.util; import java.util.HashMap; import java.util.Map; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; public class JwtToken { /* 生成token加密所需的公鑰,位於服務端 */ public static String SECRET = "eco"; /* * 根據用戶登錄信息(用戶名、密碼)生成token * * @pram name 用戶請求提供的某個參數 */ public static String creatToken(String name) throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("alg", "HS256"); map.put("typ", "JWT"); String token = JWT.create().withHeader(map).withClaim("name", name) .sign(Algorithm.HMAC384(SECRET)); return token; } /* * 解析用戶請求提供的token字符串,返回map * * @pram token 用戶請求提供的token */ public static Map<String, Claim> verifyToken(String token) throws Exception { JWTVerifier verifier = JWT.require(Algorithm.HMAC384(SECRET)).build(); DecodedJWT jwt = null; jwt = verifier.verify(token); return jwt.getClaims(); } }