先给出JWT的 官方文档
JSON Web Token(JWT)是一个开放标准( RFC 7519 ),它定义了一种紧凑且独立的方式,用于在各方之间作为JSON对象安全地传输信息。
JWT令牌由Header、Payload、Signature三部分组成,每部分之间用点号分隔,通常的形式为 xxxxx.yyyyy.zzzzz
,下面分别对每部分做详细介绍。
Header通常由两部分组成:令牌的类型,即JWT,以及使用的签名算法,例如HMAC SHA256或RSA。
{ "alg": "HS256", "typ": "JWT" }
这个JSON被编码为 Base64Url ,形成JWT的第一部分。
JWT的第二部分是有效载荷,其中包含声明( claims)。声明包含实体(通常是用户)和其他自定义信息。声明有三种类型: registered , public 和 private claims 。
请注意:声明名称只有三个字符,因为JWT需要保持简洁。
Payload经过 Base64Url 编码,形成JWT的第二部分。
请注意:对于JWT令牌,虽然可以防止被篡改,但任何人都可以读取。除非加密,否则不要将秘密信息放在JWT的有效载荷或头元素中。
Signature由Base64Url加密的Header、Payload再使用Header中指定的算法加密之后再和secret组成。
如果要使用HMACSHA256算法,将按以下方式创建签名:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Signature用于验证消息在此过程中未被更改,并且,在使用私钥签名的令牌的情况下,它还可以验证JWT的发件人的身份。
最后将JWT的三部分由点号分隔作为一个字符串,其可以在HTML和HTTP环境中轻松传递,并且比基于XML的标准的Token (如SAML) 更加简洁。
在身份验证中,当用户使用其凭据成功登录时,将返回JSON Web Token。由于令牌是凭证,因此必须非常小心以防止出现安全问题。一般情况下,您不应该将令牌保留的时间超过要求。
每当用户想要访问受保护的路由或资源时,用户发送JWT到相应的地址,通常在 Authorization 标头中。请求头的的内容应如下所示:
Authorization: Bearer <token>
在某些情况下,这可以是无状态授权机制。服务器的受保护路由将检查 Authorization
标头中的有效JWT ,如果存在,则允许用户访问受保护资源。如果JWT包含必要的数据,则可以减少查询数据库以进行某些操作的需要,尽管可能并非总是如此。
如果在标 Authorization
头中发送Token,则跨域资源共享(CORS)将不会成为问题,因为它不使用cookie。
下图显示了如何获取JWT并用于访问API或资源:
请注意:使用JWT时,Token中包含的所有信息都会向用户或其他方公开,即使他们无法更改。所以您不应该在令牌中放置秘密信息。
JWT的java实现非常多,详细何以查看 官方文档 。
其中常用的有 com.auth0.java-jwt 和 io.jsonwebtoken.jjwt
这里我采用 jjwt 作为演示,因为他的Github中的Star比较多。
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.10.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.10.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.10.5</version> <scope>runtime</scope> </dependency> <!-- Uncomment this next dependency if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms: <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.60</version> <scope>runtime</scope> </dependency> -->
//Sample method to construct a JWT public static String createJWT(String id, String issuer, String subject, long ttlMillis) { //The JWT signature algorithm we will be using to sign the token SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); //We will sign our JWT with our ApiKey secret byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary( APP_ID + APP_SECRET); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); //Let's set the JWT Claims JwtBuilder builder = Jwts.builder().setId(id) .setIssuedAt(now) .setSubject(subject) .setIssuer(issuer) .signWith(signatureAlgorithm, signingKey); //if it has been specified, let's add the expiration if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } //Builds the JWT and serializes it to a compact, URL-safe string return builder.compact(); }
//Sample method to validate and read the JWT public static Claims parseJWT(String jwt) { //This line will throw an exception if it is not a signed JWS (as expected) Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(APP_ID + APP_SECRET)) .parseClaimsJws(jwt).getBody(); // System.out.println("ID: " + claims.getId()); // System.out.println("Subject: " + claims.getSubject()); // System.out.println("Issuer: " + claims.getIssuer()); // System.out.println("Expiration: " + claims.getExpiration()); return claims; }
其中 APP_ID
和 APP_SECRET
可以自定义为你想要的任何值,但是不能过于简单。
你会发现我们再使用的时候没有设置Header的值,因为jjwt为了我们使用方便会根据使用的签名算法或压缩算法自动设置它们。
@Test public void createJWT(){ String jwt = JWTUtil.createJWT("1", "111", "admin", JWTUtil.DAY_TTL); System.out.println(jwt); } @Test public void parseJWT(){ Claims claims = JWTUtil.parseJWT("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxIiwiaWF0IjoxNTQ4Mjk1NjQ0LCJzdWIiOiJhZG1pbiIsImlzcyI6IjExMSIsImV4cCI6MTU0ODMzODg0NH0.WRkyeG3MfVor02Ya4732fgGydXhtkkKSDwbxOIZ2i9Y"); System.out.println(claims); }
如果想使用jjwt更复杂的功能或者其他的Java实现可以去他们相应的Github上学习。