Token機制是sso單點登錄的最主要實現機制,最常用的實現機制。


  Token機制是sso單點登錄的最主要實現機制,最常用的實現機制。傳統身份認證,一般一個應用服務器,在客戶端和服務器關聯的時候,在應用服務器上做了一個HttpSession對象保持着客戶端和服務器上狀態信息存儲。

 

1、傳統身份認證。

答:HTTP是一種沒有狀態的協議,也就是它並不知道是誰是訪問應用。這里我們把用戶看成是客戶端,客戶端使用用戶名還有密碼通過了身份驗證,不過下回這個客戶端再發送請求時候,還得再驗證一下。
  解決的方法就是,當用戶請求登錄的時候,如果沒有問題,我們在服務端生成一條記錄,這個記錄里可以說明一下登錄的用戶是誰,然后把這條記錄的 ID 號發送給客戶端,客戶端收到以后把這個 ID 號存儲在 Cookie 里,下次這個用戶再向服務端發送請求的時候,可以帶着這個 Cookie ,這樣服務端會驗證一個這個 Cookie 里的信息,看看能不能在服務端這里找到對應的記錄,如果可以,說明用戶已經通過了身份驗證,就把用戶請求的數據返回給客戶端。
  上面說的就是 Session,我們需要在服務端存儲為登錄的用戶生成的 Session ,這些 Session 可能會存儲在內存,磁盤,或者數據庫里。我們可能需要在服務端定期的去清理過期的 Session 。

這種認證中出現的問題是:

  a)、Session:每次認證用戶發起請求時,服務器需要去創建一個記錄來存儲信息。當越來越多的用戶發請求時,內存的開銷也會不斷增加。
  b)、可擴展性:在服務端的內存中使用Session存儲登錄信息,伴隨而來的是可擴展性問題。
  c)、CORS(跨域資源共享):當我們需要讓數據跨多台移動設備上使用時,跨域資源的共享會是一個讓人頭疼的問題。在使用Ajax抓取另一個域的資源,就可以會出現禁止請求的情況。
  d)、CSRF(跨站請求偽造):用戶在訪問銀行網站時,他們很容易受到跨站請求偽造的攻擊,並且能夠被利用其訪問其他的網站。在這些問題中,可擴展性是最突出的。因此我們有必要去尋求一種更有行之有效的方法。

 

2、Token身份認證。

答:2.1)、使用基於 Token 的身份驗證方法,在服務端不需要存儲用戶的登錄記錄。大概的流程是這樣的:

    a)、客戶端使用用戶名、密碼請求登錄。
    b)、服務端收到請求,去驗證用戶名、密碼。
    c)、驗證成功后,服務端會簽發一個 Token(令牌),再把這個 Token 發送給客戶端。
    d)、客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 、Session Storage里。
    e)、客戶端每次向服務端請求資源的時候需要帶着服務端簽發的 Token。
    f)、服務端收到請求,然后去驗證客戶端請求里面帶着的 Token,如果驗證成功,就向客戶端返回請求的數據。

2.2)、使用Token驗證的優勢:

  a)、無狀態、可擴展。
  b)、在客戶端存儲的Tokens是無狀態的,並且能夠被擴展。基於這種無狀態和不存儲Session信息,負載負載均衡器能夠將用戶信息從一個服務傳到其他服務器上。
  c)、安全性。
  d)、請求中發送token而不再是發送cookie能夠防止CSRF(跨站請求偽造)。即使在客戶端使用cookie存儲token,cookie也僅僅是一個存儲機制而不是用於認證。不將信息存儲在Session中,讓我們少了對session操作。

 

3、JSON Web Token(JWT)機制。

答:Token在Java中具體實現的方案,JWT(Json數據做web網絡層的令牌機制),可以做加密擴展或者簽名擴展。

1)、JSON Web Token(JWT)機制。

  a、JWT是一種緊湊(小而少,只要包含了用戶信息即可)且自包含(json數據中包含本次訪問簡單記錄,記錄不敏感數據)的,用於在多方傳遞JSON對象的技術。傳遞的數據可以使用數字簽名增加其安全行。可以使用HMAC加密算法或RSA公鑰/私鑰加密方式。
  b、緊湊:數據小,可以通過URL,POST參數,請求頭發送。且數據小代表傳輸速度快。
  c、自包含:使用payload數據塊記錄用戶必要且不隱私的數據,可以有效的減少數據庫訪問次數,提高代碼性能。
  d、JWT一般用於處理用戶身份驗證或數據信息交換。
  e、用戶身份驗證:一旦用戶登錄,每個后續請求都將包含JWT,允許用戶訪問該令牌允許的路由,服務和資源。單點登錄是當今廣泛使用JWT的一項功能,因為它的開銷
    很小,並且能夠輕松地跨不同域使用。
  f、數據信息交換:JWT是一種非常方便的多方傳遞數據的載體,因為其可以使用數據簽名來保證數據的有效性和安全性。
  h、官網: http://jwt.io

 

2)、JWT數據結構。JWT的數據結構是 : A.B.C。 由字符點'.'來分隔三部分數據。

2.1)、A:header 頭信息。

  a)、數據結構: {"alg": "加密算法名稱", "typ" : "JWT"}。
  b)、alg是加密算法定義內容,如:HMAC SHA256 或 RSA。
  c)、typ是token類型,這里固定為JWT。

2.2)、B:payload (有效荷載,可以考慮不傳遞任何信息)。

  a)、在payload數據塊中一般用於記錄實體(通常為用戶信息)或其他數據的。主要分為三個部分,分別是:已注冊信息(registered claims),公開數據(public claims),私有數據(private claims)。
  b)、payload中常用信息有:iss(發行者),exp(到期時間),sub(主題),aud(受眾)等。前面列舉的都是已注冊信息。
  c)、公開數據部分一般都會在JWT注冊表中增加定義。避免和已注冊信息沖突。
  d)、公開數據和私有數據可以由程序員任意定義。
  e)、注意:即使JWT有簽名加密機制,但是payload內容都是明文記錄,除非記錄的是加密數據,否則不排除泄露隱私數據的可能。不推薦在payload中記錄任何敏感數據。

2.3)、C:Signature 簽名。

  簽名信息。這是一個由開發者提供的信息。是服務器驗證的傳遞的數據是否有效安全的標准。在生成JWT最終數據的之前。先使用header中定義的加密算法,將header和payload進行加密,並使用點進行連接。如:加密后的head.加密后的payload。再使用相同的加密算法,對加密后的數據和簽名信息進行加密。得到最終結果。

 

4、JWT(JSON Web Token)執行流程,如下所示:

 

5、使用一個簡單的項目來驗證一下JWT的實現。pom.xml依賴配置,如下所示:

 1 <project xmlns="http://maven.apache.org/POM/4.0.0"
 2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
 4     http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6     <groupId>com.bie</groupId>
 7     <artifactId>sso-jwt</artifactId>
 8     <version>1.0</version>
 9     <packaging>war</packaging>
10 
11     <dependencies>
12         <!-- spring5.0.6要求Jdk1.8以上版本 -->
13         <dependency>
14             <groupId>org.springframework</groupId>
15             <artifactId>spring-context</artifactId>
16             <version>5.0.6.RELEASE</version>
17         </dependency>
18         <dependency>
19             <groupId>org.springframework</groupId>
20             <artifactId>spring-webmvc</artifactId>
21             <version>5.0.6.RELEASE</version>
22         </dependency>
23         <dependency>
24             <groupId>org.springframework</groupId>
25             <artifactId>spring-aspects</artifactId>
26             <version>5.0.6.RELEASE</version>
27         </dependency>
28         <!-- JWT核心依賴 -->
29         <dependency>
30             <groupId>com.auth0</groupId>
31             <artifactId>java-jwt</artifactId>
32             <version>3.3.0</version>
33         </dependency>
34         <!-- java開發JWT的依賴jar包。 -->
35         <dependency>
36             <groupId>io.jsonwebtoken</groupId>
37             <artifactId>jjwt</artifactId>
38             <version>0.9.0</version>
39         </dependency>
40         <!-- 給springmvc提供的響應擴展。@ResponseBody -->
41         <dependency>
42             <groupId>com.fasterxml.jackson.core</groupId>
43             <artifactId>jackson-databind</artifactId>
44             <version>2.9.5</version>
45         </dependency>
46         <!-- jstl -->
47         <dependency>
48             <groupId>javax.servlet</groupId>
49             <artifactId>jstl</artifactId>
50             <version>1.2</version>
51         </dependency>
52         <!-- servlet -->
53         <dependency>
54             <groupId>javax.servlet</groupId>
55             <artifactId>servlet-api</artifactId>
56             <version>2.5</version>
57             <scope>provided</scope>
58         </dependency>
59         <!-- jsp -->
60         <dependency>
61             <groupId>javax.servlet.jsp</groupId>
62             <artifactId>jsp-api</artifactId>
63             <version>2.2</version>
64             <scope>provided</scope>
65         </dependency>
66     </dependencies>
67 
68     <build>
69         <pluginManagement>
70             <plugins>
71                 <!-- 配置Tomcat插件 -->
72                 <plugin>
73                     <groupId>org.apache.tomcat.maven</groupId>
74                     <artifactId>tomcat7-maven-plugin</artifactId>
75                     <version>2.2</version>
76                 </plugin>
77             </plugins>
78         </pluginManagement>
79         <plugins>
80             <plugin>
81                 <groupId>org.apache.tomcat.maven</groupId>
82                 <artifactId>tomcat7-maven-plugin</artifactId>
83                 <configuration>
84                     <port>80</port>
85                     <path>/</path>
86                 </configuration>
87             </plugin>
88         </plugins>
89     </build>
90 
91 </project>

JWTSubject作為Subject數據使用。也就是payload中保存的public claims,其中不包含任何敏感數據。建議使用實體類型,或者BO、DTO數據對象。

 1 package com.bie.sso.commons;
 2 
 3 /**
 4  * 作為Subject數據使用。也就是payload中保存的public
 5  * claims,其中不包含任何敏感數據。開發中建議使用實體類型,或者BO、DTO數據對象。
 6  * 
 7  * @author biehl
 8  *
 9  */
10 public class JWTSubject {
11 
12     private String username;
13 
14     public JWTSubject() {
15         super();
16     }
17 
18     public JWTSubject(String username) {
19         super();
20         this.username = username;
21     }
22 
23     public String getUsername() {
24         return username;
25     }
26 
27     public void setUsername(String username) {
28         this.username = username;
29     }
30 
31 }

JWTResult結果對象。可以存放錯誤編碼、正確編碼、claims驗證過程中payload中的數據。是payload里面的數據對象,是JWT里面的對象。

 1 package com.bie.sso.commons;
 2 
 3 import io.jsonwebtoken.Claims;
 4 
 5 /**
 6  * 結果對象。
 7  * 
 8  * @author biehl
 9  *
10  */
11 public class JWTResult {
12 
13     /**
14      * 錯誤編碼。在JWTUtils中定義的常量, 200為正確。
15      */
16     private int errCode;
17 
18     /**
19      * 是否成功,代表結果的狀態。
20      */
21     private boolean success;
22 
23     /**
24      * 驗證過程中payload中的數據。是payload里面的數據對象,是JWT里面的對象。
25      */
26     private Claims claims;
27 
28     public int getErrCode() {
29         return errCode;
30     }
31 
32     public void setErrCode(int errCode) {
33         this.errCode = errCode;
34     }
35 
36     public boolean isSuccess() {
37         return success;
38     }
39 
40     public void setSuccess(boolean success) {
41         this.success = success;
42     }
43 
44     public Claims getClaims() {
45         return claims;
46     }
47 
48     public void setClaims(Claims claims) {
49         this.claims = claims;
50     }
51 
52 }

JWTResponseData發送給客戶端的數據對象。 商業開發中,一般除特殊請求外,大多數的響應數據都是一個統一類型的數據,統一數據有統一的處理方式,便於開發和維護。

 1 package com.bie.sso.commons;
 2 
 3 /***
 4  * 發送給客戶端的數據對象。 商業開發中,一般除特殊請求外,大多數的響應數據都是一個統一類型的數據,統一數據有統一的處理方式,便於開發和維護。
 5  * 
 6  * @author biehl
 7  *
 8  */
 9 public class JWTResponseData {
10 
11     private Integer code;// 返回碼,類似HTTP響應碼。如:200成功,500服務器錯誤,404資源不存在等。
12 
13     private Object data;// 業務數據
14 
15     private String msg;// 返回描述
16 
17     private String token;// 身份標識, JWT生成的令牌。
18 
19     public Integer getCode() {
20         return code;
21     }
22 
23     public void setCode(Integer code) {
24         this.code = code;
25     }
26 
27     public Object getData() {
28         return data;
29     }
30 
31     public void setData(Object data) {
32         this.data = data;
33     }
34 
35     public String getMsg() {
36         return msg;
37     }
38 
39     public void setMsg(String msg) {
40         this.msg = msg;
41     }
42 
43     public String getToken() {
44         return token;
45     }
46 
47     public void setToken(String token) {
48         this.token = token;
49     }
50 
51 }

JWTUtils是JWT的工具。

  1 package com.bie.sso.commons;
  2 
  3 import java.util.Date;
  4 
  5 import javax.crypto.SecretKey;
  6 import javax.crypto.spec.SecretKeySpec;
  7 
  8 import com.fasterxml.jackson.core.JsonProcessingException;
  9 import com.fasterxml.jackson.databind.ObjectMapper;
 10 
 11 import io.jsonwebtoken.Claims;
 12 import io.jsonwebtoken.ExpiredJwtException;
 13 import io.jsonwebtoken.JwtBuilder;
 14 import io.jsonwebtoken.Jwts;
 15 import io.jsonwebtoken.SignatureAlgorithm;
 16 import io.jsonwebtoken.SignatureException;
 17 
 18 /**
 19  * JWT工具
 20  * 
 21  * @author biehl
 22  *
 23  */
 24 public class JWTUtils {
 25 
 26     // 服務器的key。用於做加解密的key數據,如果可以使用客戶端生成的key,當前定義的常量可以不使用。
 27     private static final String JWT_SECERT = "test_jwt_secert";
 28     // 做json和java對象之間的相互轉換。
 29     private static final ObjectMapper MAPPER = new ObjectMapper();
 30     public static final int JWT_ERRCODE_EXPIRE = 1005;// Token過期
 31     public static final int JWT_ERRCODE_FAIL = 1006;// 驗證不通過
 32 
 33     /**
 34      * 創建密匙key
 35      * 
 36      * @return
 37      */
 38     public static SecretKey generalKey() {
 39         try {
 40             // byte[] encodedKey = Base64.decode(JWT_SECERT);
 41             // 不管哪種方式最終得到一個byte[]類型的key就行
 42             byte[] encodedKey = JWT_SECERT.getBytes("UTF-8");
 43             SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
 44             return key;
 45         } catch (Exception e) {
 46             e.printStackTrace();
 47             return null;
 48         }
 49     }
 50 
 51     /**
 52      * 簽發JWT,創建token的方法。
 53      * 
 54      * @param id
 55      *            jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
 56      * @param iss
 57      *            jwt簽發者,誰去生成的token信息。
 58      * @param subject
 59      *            jwt所面向的用戶。payload中記錄的public claims公開信息。當前環境中就是用戶的登錄名。
 60      * @param ttlMillis
 61      *            有效期,單位毫秒。
 62      * @return token token是一次性的。是為一個用戶的有效登錄周期准備的一個token。用戶退出或者超時,token將會失效。
 63      * 
 64      * @throws Exception
 65      */
 66     public static String createJWT(String id, String iss, String subject, long ttlMillis) {
 67         // 加密算法。
 68         SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
 69         // 當前時間。
 70         long nowMillis = System.currentTimeMillis();
 71         // 當前時間的日期對象。
 72         Date now = new Date(nowMillis);
 73         // 獲取到密匙key。
 74         SecretKey secretKey = generalKey();
 75         // 創建JWT的構建器。 就是使用指定的信息和加密算法,生成Token的工具。
 76         JwtBuilder builder = Jwts.builder().setId(id) // 設置身份標志。就是一個客戶端的唯一標記。 如:可以使用用戶的主鍵,客戶端的IP,服務器生成的隨機數據。
 77                 .setIssuer(iss)
 78 
 79                 .setSubject(subject)
 80 
 81                 .setIssuedAt(now) // token生成的時間。
 82                 .signWith(signatureAlgorithm, secretKey); // 設定密匙和算法
 83         // 如果有效期大於等於0,ttlMillis是多長時間,單位是毫秒。
 84         if (ttlMillis >= 0) {
 85             long expMillis = nowMillis + ttlMillis; // 當前時間加上有效期就是token的失效時間。
 86             Date expDate = new Date(expMillis); // token的失效時間。
 87             builder.setExpiration(expDate);// 設置token的失效時間。
 88         }
 89         return builder.compact(); // 生成token
 90     }
 91 
 92     /**
 93      * 驗證JWT
 94      * 
 95      * @param jwtStr
 96      * @return
 97      */
 98     public static JWTResult validateJWT(String jwtStr) {
 99         // 創建JWTResult對象實例
100         JWTResult checkResult = new JWTResult();
101         // 創建Claims對象實例
102         Claims claims = null;
103         try {
104             // 解析JWT字符串
105             claims = parseJWT(jwtStr);
106             // 設定信息
107             checkResult.setSuccess(true);
108             checkResult.setClaims(claims);
109         } catch (ExpiredJwtException e) { // token超時,Token過期
110             checkResult.setErrCode(JWT_ERRCODE_EXPIRE);
111             checkResult.setSuccess(false);
112         } catch (SignatureException e) { // 校驗失敗,驗證不通過
113             checkResult.setErrCode(JWT_ERRCODE_FAIL);
114             checkResult.setSuccess(false);
115         } catch (Exception e) {
116             checkResult.setErrCode(JWT_ERRCODE_FAIL);
117             checkResult.setSuccess(false);
118         }
119         return checkResult;
120     }
121 
122     /**
123      * 
124      * 解析JWT字符串
125      * 
126      * @param jwt
127      *            就是服務器為客戶端生成的簽名數據,就是token。
128      * @return
129      * @throws Exception
130      */
131     public static Claims parseJWT(String jwt) throws Exception {
132         // 創建密匙key,通過key校驗token
133         SecretKey secretKey = generalKey();
134         // getBody獲取的就是token中記錄的payload數據。就是payload中保存的所有的claims。
135         return Jwts.parser()
136 
137                 .setSigningKey(secretKey) // 設置密匙
138 
139                 .parseClaimsJws(jwt) // 解析的是什么字符串
140 
141                 .getBody(); // getBody獲取的就是token中記錄的payload數據。就是payload中保存的所有的claims。
142     }
143 
144     /**
145      * 生成subject信息
146      * 
147      * @param subObj
148      *            - 要轉換的對象。
149      * @return java對象->JSON字符串出錯時返回null
150      */
151     public static String generalSubject(Object subObj) {
152         try {
153             return MAPPER.writeValueAsString(subObj);
154         } catch (JsonProcessingException e) {
155             e.printStackTrace();
156             return null;
157         }
158     }
159 
160 }

JWTController控制層的業務邏輯,如下所示:

  1 package com.bie.sso.controller;
  2 
  3 import java.util.HashMap;
  4 import java.util.Map;
  5 import java.util.UUID;
  6 
  7 import javax.servlet.http.HttpServletRequest;
  8 
  9 import org.springframework.stereotype.Controller;
 10 import org.springframework.web.bind.annotation.RequestMapping;
 11 import org.springframework.web.bind.annotation.ResponseBody;
 12 
 13 import com.bie.sso.commons.JWTResponseData;
 14 import com.bie.sso.commons.JWTResult;
 15 import com.bie.sso.commons.JWTSubject;
 16 import com.bie.sso.commons.JWTUtils;
 17 
 18 /**
 19  * 
 20  * @author biehl
 21  *
 22  */
 23 @Controller
 24 public class JWTController {
 25 
 26     private static final Map<String, String> USERS = new HashMap<>(16);
 27 
 28     static {
 29         // 初始化10個賬號密碼信息
 30         for (int i = 0; i < 10; i++) {
 31             USERS.put("admin" + i, "password" + 1);
 32         }
 33     }
 34 
 35     /**
 36      * 是否可登錄
 37      * 
 38      * @param username
 39      * @param password
 40      * @return
 41      */
 42     public static boolean isLogin(String username, String password) {
 43         if (null == username || username.trim().length() == 0) {
 44             return false;
 45         }
 46         String obj = USERS.get(username);
 47         if (null == obj || !obj.equals(password)) {
 48             return false;
 49         }
 50         return true;
 51     }
 52 
 53     @RequestMapping("/authorization")
 54     @ResponseBody
 55     public Object authorization(HttpServletRequest request) {
 56         // 獲取到頭部的校驗數據
 57         String token = request.getHeader("Authorization");
 58         // 驗證JWT
 59         JWTResult result = JWTUtils.validateJWT(token);
 60         //
 61         JWTResponseData responseData = new JWTResponseData();
 62         // 判斷是否成功
 63         if (result.isSuccess()) {
 64             // 如果成功設置響應碼200
 65             responseData.setCode(200);
 66             // 將獲取到的用戶信息進行設置。
 67             responseData.setData(result.getClaims().getSubject());
 68             // 重新生成token,就是為了重置token的有效期。
 69             String newToken = JWTUtils.createJWT(result.getClaims().getId(), result.getClaims().getIssuer(),
 70                     result.getClaims().getSubject(), 1 * 60 * 1000);
 71             // 設置新的token的有效期。
 72             responseData.setToken(newToken);
 73             return responseData;
 74         } else {
 75             // 如果失敗設置響應碼500
 76             responseData.setCode(500);
 77             responseData.setMsg("用戶未登錄");
 78             return responseData;
 79         }
 80     }
 81 
 82     @RequestMapping("/login")
 83     @ResponseBody
 84     public Object login(String username, String password) {
 85         JWTResponseData responseData = null;
 86         // 認證用戶信息。本案例中訪問靜態數據。
 87         if (JWTController.isLogin(username, password)) {
 88             // 創建一個對象實例,將賬號傳遞進去創建實例對象。
 89             JWTSubject subject = new JWTSubject(username);
 90             // 創建簽名信息token
 91             String jwtToken = JWTUtils.createJWT(UUID.randomUUID().toString(), "bie-test-jwt",
 92                     JWTUtils.generalSubject(subject), 1 * 60 * 1000);
 93             // 創建相應實例
 94             responseData = new JWTResponseData();
 95             responseData.setCode(200);
 96             responseData.setData(null);
 97             responseData.setMsg("登錄成功");
 98             // 設置相應的token
 99             responseData.setToken(jwtToken);
100         } else {
101             responseData = new JWTResponseData();
102             responseData.setCode(500);
103             responseData.setData(null);
104             responseData.setMsg("登錄失敗");
105             responseData.setToken(null);
106         }
107         return responseData;
108     }
109 
110 }

登錄主界面,可以使用登錄,和驗證是否登錄來測試JWT的使用。登錄主界面,如下所示:

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>Insert title here</title>
 8 <script type="text/javascript" src="js/jquery.min.js"></script>
 9 <script type="text/javascript">
10     function login() {
11         var username = $("#username").val();
12         var password = $("#password").val();
13         var params = "username=" + username + "&password=" + password;
14         $.ajax({
15             'url' : '${pageContext.request.contextPath }/login',
16             'data' : params,
17             'success' : function(data) {
18                 if (data.code == 200) {
19                     var token = data.token;
20                     // web storage的查看 - 在瀏覽器的開發者面板中的application中查看。
21                     // local storage - 本地存儲的數據。 長期有效的。
22                     // session storage - 會話存儲的數據。 一次會話有效。
23                     var localStorage = window.localStorage; // 瀏覽器提供的存儲空間。 根據key-value存儲數據。
24                     localStorage.token = token;
25                 } else {
26                     alert(data.msg);
27                 }
28             }
29         });
30     }
31 
32     function setHeader(xhr) { // XmlHttpRequest
33         xhr.setRequestHeader("Authorization", window.localStorage.token);
34     }
35 
36     function testLocalStorage() {
37         $.ajax({
38             'url' : '${pageContext.request.contextPath}/authorization',
39             'success' : function(data) {
40                 if (data.code == 200) {
41                     window.localStorage.token = data.token;
42                     alert(data.data);
43                 } else {
44                     alert(data.msg);
45                 }
46             },
47             'beforeSend' : setHeader
48         });
49     }
50 </script>
51 </head>
52 <body>
53     <center>
54         <table>
55             <caption>登錄測試</caption>
56             <tr>
57                 <td style="text-align: right; padding-right: 5px">登錄名:</td>
58                 <td style="text-align: left; padding-left: 5px"><input
59                     type="text" name="username" id="username" /></td>
60             </tr>
61             <tr>
62                 <td style="text-align: right; padding-right: 5px">密碼:</td>
63                 <td style="text-align: left; padding-left: 5px"><input
64                     type="text" name="password" id="password" /></td>
65             </tr>
66             <tr>
67                 <td style="text-align: right; padding-right: 5px" colspan="2">
68                     <input type="button" value="登錄" onclick="login();" />
69                 </td>
70             </tr>
71         </table>
72     </center>
73     <input type="button" value="驗證是否登錄"
74         onclick="testLocalStorage();" />
75 </body>
76 </html>

可以借助瀏覽器的幫助理解這些知識點。

注意點1:web storage的查看,在瀏覽器的開發者面板中的application中查看。

  a)、local storage:本地存儲的數據,長期有效的。
  b)、session storage:會話存儲的數據,一次會話有效。

注意點2:如果對session和cookie不理解的,還是多搜索一下session和cookie的區別,方便自己的理解,然后在搜索一下單點登錄的實現過程,網上已經很多介紹了,這篇主要是幫助自己理解JWT的使用,包含生成token,驗證token等等知識點。

 

 

6、基於JWT機制的單點登錄。注意事項,如下所示:

  1)、使用JWT實現單點登錄時,需要注意token時效性。token是保存在客戶端的令牌數據,如果永久有效,則有被劫持的可能。token在設計的時候,可以考慮一次性有效或一段時間內有效。如果設置有效時長,則需要考慮是否需要刷新token有效期問題。

  2)、使用JWT技術生成的token,客戶端在保存的時候可以考慮cookie或localStorage。cookie保存方式,可以實現跨域傳遞數據。localStorage是域私有的本地存儲,無法實現跨域。

  3)、關於webstorage的相關知識點。

    a、webstorage可保存的數據容量為5M。且只能存儲字符串數據。
    b、webstorage分為localStorage和sessionStorage。
    c、localStorage的生命周期是永久的,關閉頁面或瀏覽器之后localStorage中的數據也不會消失。localStorage除非主動刪除數據,否則數據永遠不會消失。
    d、sessionStorage是會話相關的本地存儲單元,生命周期是在僅在當前會話下有效。sessionStorage引入了一個“瀏覽器窗口”的概念,sessionStorage是在同源的窗口中始終存在的數據。只要這個瀏覽器窗口沒有關閉,即使刷新頁面或者進入同源另一個頁面,數據依然存在。但是sessionStorage在關閉了瀏覽器窗口后就會被銷毀。同時獨立的打開同一個窗口同一個頁面,sessionStorage也是不一樣的。


 

 

7、Restful接口設計,Rest簡述。

  答:REST(英文:Representational State Transfer,簡稱REST)描述了一個架構樣式的網絡系統,比如 web 應用程序。它首次出現在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規范的主要編寫者之一。在目前主流的三種Web服務交互方案中,REST相比於SOAP(Simple Object Access protocol,簡單對象訪問協議)以及XML-RPC更加簡單明了,無論是對URL的處理還是對Payload的編碼,REST都傾向於用更加簡單輕量的方法設計和實現。值得注意的是REST並沒有一個明確的標准,而更像是一種設計的風格。

 

8、Restful接口設計,Restful簡述。

  答:對應的中文是rest式的,Restful web service是一種常見的rest的應用,是遵守了rest風格的web服務,rest式的web服務是一種ROA(The Resource-Oriented Architecture)(面向資源的架構)。

9、Restful接口設計,Restful特性。

1)、普通架構。

  每次請求的接口或者地址,都在做描述,例如查詢的時候用了query,新增的時候用了save。如:http://127.0.0.1/user/query/1,這個是GET請求,根據用戶id查詢用戶數據。http://127.0.0.1/user/save,這個是POST請求,新增用戶。

2)、Restful架構。

  使用get請求,就是查詢.使用post請求,就是新增的請求,意圖明顯,沒有必要做描述,這就是restful。http://127.0.0.1/user/1,這個是GET請求,根據用戶id查詢用戶數據。http://127.0.0.1/user,這個是POST請求,新增用戶。

3)、Restful操作方式。冪等性:多次訪問,結果資源狀態是否相同。安全:訪問是否會變更服務器資源狀態。

HTTP方法

資源操作

冪等性

是否安全?

GET

查詢

POST

新增

PUT

修改

DELETE

刪除

4)、響應狀態碼。

編碼

HTTP方法

響應體內容

描述

200

get/put

資源數據

操作成功

201

post

源數據

創建成功

202

post/put/delete

請求已接受

204

delete/put

請求已處理,無返回數據

301

get

link 鏈接

資源已被移除

303

get

link

重定向

304

get

資源沒有被修改

400

get/post/put/delete

錯誤提示消息

參數錯誤(缺少,格式錯誤等)

401

get/post/put/delete

錯誤提示消息

未授權

403

get/post/put/delete

錯誤提示消息

訪問受限、授權過期

404

get/post/put/delete

錯誤提示消息

資源、服務未找到

405

get/post/put/delete

錯誤提示消息

不允許的HTTP方法

409

get/post/put/delete

錯誤提示消息

資源沖突或資源被鎖定

415

get/post/put/delete

錯誤提示消息

不支持的數據類型或媒體類型

429

get/post/put/delete

錯誤提示消息

請求過多被限制

500

get/post/put/delete

錯誤提示消息

系統錯誤

501

get/post/put/delete

錯誤提示消息

接口未實現

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

待續......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM