1. 一個JWT實際上就是一個字符串,由三部分組成 頭部,載荷,簽名
頭部:事描述類型,簽名,算法等 可以被表示成一個JSON對象
載荷:存放有效信息的地方 包含三個部分
(1)標准注冊中的聲明-建議但不強制使用
iss:jwt簽發者
sub:jwt所面向的用戶
aud:接收jwt的一方
exp:jwt的過期時間,時間必須大於簽發時間
nbf:定義在什么時間以前,這個jwt都是不可用的
iat:jwt的簽發時間
jti:jwt的唯一身份標識,用來作為token
(2)公共的聲明
一般是公司相關的信息,由於這部分可以被解密,所以不要添加敏感信息
(3)私有的聲明
可以被解密,,
可以自定義claim
載荷的例子 {"sub":"1233","name":"John","admin":true}
簽證:三部分
header(base64后的)
payload(base64后的)
secret
2. 名詞解釋 Base64
64個可打印二進制數據 2^6為64 每6個比特是一個單元,打印一個字符,3個字節有24個比特
對應4個Base64 JDK中提供了BASE64Encoder BASE64Decoder 編碼,解碼
3. java的jjwt
JJWT是一個提供端到端的JWT創建和驗證的Java庫
4. 使用
(一)引入依賴
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.6.0</version> </dependency>
(二)測試使用

package BigTest.jwt; import com.JwtBootApplication; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.text.SimpleDateFormat; import java.util.Date; @RunWith(SpringRunner.class) @SpringBootTest(classes = JwtBootApplication.class) public class CreateJwtTest { /* * java.lang.IllegalArgumentException: * secret key byte array cannot be null or empty. * 是因為secret太簡單了 沒有加時間 * */ @Test public void testJwt(){ JwtBuilder jwtBuilder = Jwts.builder().setId("888") .setSubject("小白") .setIssuedAt(new Date()) //setIssuedAt用於設置簽發時間 .signWith(SignatureAlgorithm.HS256, "onetwothree"); //signWith用於設置簽名秘鑰 System.out.println(jwtBuilder.compact()); //eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzMyOTAwOTh9.-lqlLqmAG2qQ15Ge7IlcgIAgj0V54L0GCA5dJfM6Lw4 } @Test public void ParseJwtTest(){ String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzMyOTAwOTh9.-lqlLqmAG2qQ15Ge7IlcgIAgj0V54L0GCA5dJfM6Lw4"; Claims claims = Jwts.parser().setSigningKey("onetwothree").parseClaimsJws(token).getBody(); System.out.println("id="+claims.getId()); System.out.println("subject="+claims.getSubject()); System.out.println("IssueAt="+claims.getIssuedAt()); } /** * 帶過期時間的jwt-token * ExpiredJwtException: token過期報錯 */ @Test public void testJwt_time(){ long now = System.currentTimeMillis(); long exp = now + 1000*60;//設置時間為1分鍾 JwtBuilder jwtBuilder = Jwts.builder().setId("888") .setSubject("小白") .setIssuedAt(new Date()) //setIssuedAt用於設置簽發時間 .setExpiration(new Date(exp))//setExpiration設置token過期時間 .signWith(SignatureAlgorithm.HS256, "onetwothree"); //signWith用於設置簽名秘鑰 System.out.println(jwtBuilder.compact()); } @Test public void ParseJwtTest_time(){ String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzMyOTg0MDUsImV4cCI6MTU3MzI5ODQ2NX0.Rx-0BrJ2032aUz_vrttXw-9_idfQe3LHzCnLOWpsaqs"; Claims claims = Jwts.parser().setSigningKey("onetwothree").parseClaimsJws(token).getBody(); System.out.println("id="+claims.getId()); System.out.println("subject="+claims.getSubject()); System.out.println("IssueAt="+claims.getIssuedAt()); SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss"); System.out.println("簽發時間:"+sdf.format(claims.getIssuedAt())); System.out.println("過期時間:"+sdf.format(claims.getExpiration())); System.out.println("當前時間:"+sdf.format(new Date()) ); } /* * 自定義claims,上面的只存儲了id和subject 自定義可以存儲自己向存儲的內容 * */ @Test public void testJwt_claims(){ long now = System.currentTimeMillis(); long exp = now + 1000*60; //設置過期時間為1分鍾 JwtBuilder claim = Jwts.builder().setId("9768") .setSubject("zhangsan") .setIssuedAt(new Date()) .signWith(SignatureAlgorithm.HS256,"onetwothree") .setExpiration(new Date(exp)) .claim("roles", "admin") .claim("address", "beijing"); System.out.println(claim.compact()); } @Test public void ParseJwtTest_claim(){ String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5NzY4Iiwic3ViIjoiemhhbmdzYW4iLCJpYXQiOjE1NzMzMDg0ODUsImV4cCI6MTU3MzMwODU0NSwicm9sZXMiOiJhZG1pbiIsImFkZHJlc3MiOiJiZWlqaW5nIn0.Y-8hZIAlWzegSadQWSQGRc9gUBGlBznRT984jtqQZ-s"; Claims claims = Jwts.parser().setSigningKey("onetwothree").parseClaimsJws(token).getBody(); System.out.println("id:"+claims.getId()); System.out.println("subject:"+claims.getSubject()); System.out.println("roles:"+claims.get("roles")); System.out.println("logo:"+claims.get("address")); SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss"); System.out.println("簽發時間:"+sdf.format(claims.getIssuedAt())); System.out.println("過期時間:"+sdf.format(claims.getExpiration())); System.out.println("當前時間:"+sdf.format(new Date()) ); } }
(三)在springboot中的使用
在application.yml添加配置
jwt: config: key: zhedoumeishenme ttl: 360000
編寫utils
package com.xxy.server.common.utils; import io.jsonwebtoken.*; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.Date; @ConfigurationProperties("jwt.config")//拿到 .yml中的參數 public class JwtUtils { private String key; private long ttl; //一個小時 public String getKey() { return key; } public void setKey(String key) { this.key = key; } public long getTtl() { return ttl; } public void setTtl(long ttl) { this.ttl = ttl; } /** * 生成jwt * @param id * @param subject * @param roles * @return */ public String createJWT(String id, String subject, String roles){ long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); JwtBuilder builder = Jwts.builder().setId(id) .setSubject(subject) .signWith(SignatureAlgorithm.HS256, key) .setIssuedAt(now) .claim("roles", roles); if(ttl > 0){ builder.setExpiration(new Date(nowMillis+ttl)); } return builder.compact(); } /** * 解析JWT * @param jwtStr * @return */ public Claims parseJWT(String jwtStr){ return Jwts.parser() .setSigningKey(key) .parseClaimsJws(jwtStr) .getBody(); } }
Jwt請求攔截器

package com.xxy.server.common.interceptors; import com.xxy.server.common.utils.JwtUtils; import io.jsonwebtoken.Claims; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @Description: Jwt請求攔截器 * @Author: xuxiaoyu * @Create: 2019-11-10 20:45 */ @Component public class JwtFilter extends HandlerInterceptorAdapter { @Autowired private JwtUtils jwtUtils; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("經過了jwtfilter攔截器"); final String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { Claims claims = jwtUtils.parseJWT(authHeader); if(claims != null){ if("admin".equals(claims.get("roles"))){ request.setAttribute("admin_claims", claims); } if("user".equals(claims.get("roles"))){ request.setAttribute("user_claims", claims); } } } return true; } }
配置到mvc中

package com.xxy.server.common.config; import com.xxy.server.common.interceptors.JwtFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; /** * description:該類可以來擴展Spirng MVC的功能 */ @Configuration public class MvcConfig extends WebMvcConfigurationSupport { @Autowired private JwtFilter jwtFilter; /** * description:添加默認視圖映射,當訪問localhost:8080/時,跳轉到index.html頁面 * params : registry * @return void */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); } /** * description: Jwt請求攔截器,任何請求都會經過這個攔截器 * @param registry */ @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtFilter) .addPathPatterns("/**") .excludePathPatterns("/login","/captcha"); } }
然后在方法中就可以判斷使用了
Claims claims = (Claims) request.getAttribute("admin_claims"); if (claims == null) { return HttpResultUtil.error("權限不足"); }