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

错误提示消息

接口未实现

 

待续......

原文地址:https://www.cnblogs.com/biehongli/p/11241223.html