zoukankan      html  css  js  c++  java
  • 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

    错误提示消息

    接口未实现

     

    待续......

  • 相关阅读:
    js中箭头函数和普通函数this的区别
    jquery DOM操作(一)
    jquery选择器详细说明
    javascript 中关于function中的prototype
    html5一些容易忽略的细节
    javascript 原生得到document.Element的方法
    关于spring autowrie的5种方式
    关于FileSystemXmlApplicationContext和ClassPathXmlApplicationContext路径问题
    【Unity Shader】(十) ------ UV动画原理及简易实现
    【Unity Shader】(九) ------ 高级纹理之渲染纹理及镜子与玻璃效果的实现
  • 原文地址:https://www.cnblogs.com/biehongli/p/11241223.html
Copyright © 2011-2022 走看看