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 |
错误提示消息 |
接口未实现 |
待续......
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
2018-04-22 执行Spark运行在yarn上的命令报错 spark-shell --master yarn-client