JWT(Json web token)认证详解
JWT的定义:
JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
JWT特点:
简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
JWT结构:
JWT主要包含Header 头部 Payload 负载 Signature 签名,顺序是 header.payload.signature 三部分之间用英语句号’.'隔开
Header头部
头部包含了两部分,token 类型(“JWT”)和采用的加密算法(HMAC SHA256或者RSA等等)
例如:
然后,用Base64对这个JSON编码就得到JWT的第一部分
Payload负载
这部分就是我们存放信息的地方了,你可以把用户 ID 等信息放在这里,JWT 规范里面对这部分有进行了比较详细的介绍,常用的由 iss(签发者),exp(过期时间),sub(面向的用户),aud(接收方),iat(签发时间)。
同样的,它会使用 Base64 编码组成 JWT 结构的第二部分。
Signature签名
前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用Base64编码后的 header 和 payload 以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过。三个部分通过.
连接在一起就是我们的 JWT 了,它可能长这个样子,长度貌似和你的加密算法和私钥有关系。
eyJhbGciOiJIUzI1NiIsIlR5cGUiOiJKd3QiLCJ0eXAiOiJKV1QifQ.eyJsb2dpblRpbWUiOiIyMDIxLTEwLTI3VDE1OjIxOjU4LjU3OCIsInVzZXJOYW1lIjoi5rGf5Y2XIiwiZXhwIjoxNjM1MzIxMTE4fQ.lUojZpIGx0a65s5FrgYlQio0vf3jOuqWOTP-DUjzGh0
JSON Web Tokens是如何工作的?
一般是在请求头里加入Authorization
,并加上Bearer
标注:
fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token } })
服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:
优点
- 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
- 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
- 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
- 它不需要在服务端保存会话信息, 所以它易于应用的扩展
安全相关
- 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
- 保护好secret私钥,该私钥非常重要。
- 如果可以,请使用https协议
最后附上Token工具类
import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.interfaces.DecodedJWT; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.Map; public class TokenUtil { /** * token过期时间 */ private static final long EXPIRE_TIME = 30 * 60 * 1000; /** * token秘钥 */ private static final String TOKEN_SECRET = "secret"; /** * 生成token,30分钟过期 * * @param userName 用户名 * @param loginTime 登录时间 * @return 生成的token */ public static String sign(String userName, LocalDateTime loginTime) { try { // 设置过期时间 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); System.out.println(date); // 私钥和加密算法 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 设置头部信息 Map<String, Object> header = new HashMap<>(3); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) // 设置token中需要加载的用户信息 存储自己想要留给前端的内容 .withClaim("userName", userName) .withClaim("loginTime", loginTime.toString()) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 检验token是否正确 */ public static boolean verify(String token) { try { //设置签名的加密算法:HMAC256 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(token); return true; } catch (Exception e) { return false; } } /** * 获取token中信息 userName */ public static String getUsername(String token) { try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("userName").asString(); } catch (JWTDecodeException e) { e.printStackTrace(); } return null; } }