JWT是什么:
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间作为JSON对象安全地传输信息。
Ø 官方网址:https://jwt.io/
Ø 调试页面:https://jwt.io/
Ø 学习文档:https://jwt.io/introduction/
用途:
Ø 授权:这是我们使用JWT最广泛的应用场景。一次用户登录,后续请求将会包含JWT,对于那些合法的token,允许用户连接路由,服务和资源。目前JWT广泛应用在SSO(Single Sign On)(单点登录)上。因为他们开销很小并且可以在不同领域轻松使用。
Ø 信息交换:JSON Web Token是一种在各方面之间安全信息传输的好的方式 因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。 此外,由于使用标头和有效负载计算签名,您还可以验证内容是否未被篡改。
组成:
一个JWT由三部分组成,各部分以点分隔:
| Header(头部)——-base64编码的Json字符串 Payload(载荷)—-base64编码的Json字符串 Signature(签名)—-使用指定算法,通过Header和Playload加盐计算的字符串 |
|---|
一个JWT看起来像下面这样:
| xxxxx.yyyyy.zzzzz |
|---|

header
此部分有两部分组成:
Ø 一部分是token的类型,目前只能是JWT
Ø 另一部分是签名算法,比如HMAC 、 SHA256 、 RSA
示例:
| { “alg”:”HS256”, “typ”:”JWT” } |
|---|
base64编码命令:
| echo -n ‘{“alg”:”HS256”,”typ”:”JWT”}’ | base64 或者 echo -n “{\“alg\“:\“HS256\“,\“typ\“:\“JWT\“}” | base64 |
|---|
Payload
token的第二部分是payload(有效负载),其中包含claims(声明)。Claims是关于一个实体(通常是用户)和其他数据类型的声明。
claims有三种类型:registered,public,and private claims。
Ø Registered(已注册的声明):这些是一组预定义声明,不是强制性的,但建议使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行人),exp(到期时间),sub(主题),aud(观众)and others。(请注意,声明名称只有三个字符,因为JWT意味着紧凑。)
Ø Public(公开声明):这些可以由使用JWT的人随意定义。 但为避免冲突,应在IANA JSON Web Token Registry中定义它们,或者将其定义为包含防冲突命名空间的URI。
Ø private (私人声明):这些声明是为了在同意使用它们的各方之间共享信息而创建的,并且既不是注册声明也不是公开声明。
Signature
要创建签名部分,您需要使用base64编码后的header,base64编码后的payload,a secret,标头中指定的算法,并对其进行签名。
示例:
| HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) |
|---|
Base64和Base64Url 的区别
Base64Url在Base64的基础上进行以下处理:
- 去除了尾部填充的“=”
- 把“+”替换成“-”
- 斜线“/”替换成下划线“_”
Spring-boot集成
导入依赖:
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.0.0</version></dependency>
编写JwtUtil:
@Component@Slf4jpublic class JwtUtil {/*** 创建token* @return*/public String createToken() {// token 密钥String secret = "123456";//签名算法Algorithm algorithm = Algorithm.HMAC256(secret);// 头部信息Map<String, Object> map = new HashMap<>();//JWT 的签名算法map.put("alg", "HS256");//token的类型map.put("typ", "JWT");//获取当前时间long currentTime = System.currentTimeMillis();// + 30 * 60 * 1000;//获取当前日期Date nowDate = new Date(currentTime);//当前时间加上30分钟Date expireDate = new Date(currentTime + 30 * 60 * 1000);// 半小时过期//创建tokenString token = JWT.create().withHeader(map)// 设置头部信息 Header.withIssuer("Thomas")//设置 载荷 签名是有谁生成 例如 服务器.withSubject("this is test token")//设置 载荷 签名的主题// .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的..withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的.withIssuedAt(nowDate) //设置 载荷 生成签名的时间.withExpiresAt(expireDate)//设置 载荷 签名过期的时间.sign(algorithm);//签名 Signaturelog.info("生成的JWT 为:"+token);return token;}/*** 校验token* @param token* @throws UnsupportedEncodingException*/public void verifyToken(String token) throws UnsupportedEncodingException {// token 密钥String secret = "123456";Algorithm algorithm = Algorithm.HMAC256(secret);JWTVerifier verifier = JWT.require(algorithm).withIssuer("Thomas").build(); // Reusable verifier instancetry {DecodedJWT jwt = verifier.verify(token);String subject = jwt.getSubject();List<String> audience = jwt.getAudience();Map<String, Claim> claims = jwt.getClaims();for (Map.Entry<String, Claim> entry : claims.entrySet()) {String key = entry.getKey();Claim claim = entry.getValue();System.out.println("key:" + key + " value:" + claim.asString());}// Claim claim = claims.get("loginName");//// System.out.println(claim.asString());// System.out.println(subject);// System.out.println(audience.get(0));}catch(Exception ex){System.out.println("验证失败!!");}}public String createTokenWithChineseClaim() {long currentTime = System.currentTimeMillis();// + 30 * 60 * 1000;Date nowDate = new Date(currentTime);Date expireDate = new Date(currentTime+ 30 * 60 * 1000);// 半小时过期Map<String, Object> map = new HashMap<String, Object>();map.put("alg", "HS256");map.put("typ", "JWT");Algorithm algorithm = Algorithm.HMAC256("secret");String token = JWT.create().withHeader(map)/* 设置 载荷 Payload */.withClaim("loginName", "zhuoqianmingyue").withClaim("userName", "张三").withClaim("deptName", "技术部").withIssuer("SERVICE")// 签名是有谁生成 例如 服务器.withSubject("this is test token")// 签名的主题// .withNotBefore(new Date())//定义在什么时间之前,该jwt都是不可用的.withAudience("APP")// 签名的观众 也可以理解谁接受签名的.withIssuedAt(nowDate) // 生成签名的时间.withExpiresAt(expireDate)// 签名过期的时间/* 签名 Signature */.sign(algorithm);return token;}// public String createTokenWithChineseClaim2() throws UnsupportedEncodingException {//// long currentTime = System.currentTimeMillis();// + 30 * 60 * 1000;//// Date nowDate = new Date(currentTime);////// Date expireDate = new Date(currentTime+ 30 * 60 * 1000);// 半小时过期// //生产头部信息// Map<String, Object> map = new HashMap<String, Object>();// map.put("alg", "HS256");// map.put("typ", "JWT");//// Member member = new Member();// member.setMemberAccount("admin");// member.setMemberName("张三");//// Gson gson = new Gson();////// String userJson = gson.toJson(member);////// String userJsonBase64 = BaseEncoding.base64().encode(userJson.getBytes());//// Algorithm algorithm = Algorithm.HMAC256("secret");// String token = JWT.create().withHeader(map)//// .withClaim("loginName", "zhuoqianmingyue").withClaim("user", userJsonBase64).withIssuer("SERVICE")// 签名是有谁生成// .withSubject("this is test token")// 签名的主题// // .withNotBefore(new Date())//该jwt都是不可用的时间// .withAudience("APP")// 签名的观众 也可以理解谁接受签名的// .withIssuedAt(nowDate) // 生成签名的时间// .withExpiresAt(expireDate)// 签名过期的时间// .sign(algorithm);//签名 Signature//// return token;//// }}
编写测试类:
@SpringBootTest@RunWith(SpringRunner.class)public class JwtUtilTest {@Resourceprivate JwtUtil jwtUtil;@Testpublic void createToken() {jwtUtil.createToken();}@Testpublic void verifyToken() {String token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0aGlzIGlzIHRlc3QgdG9rZW4iLCJhdWQiOiJBUFAiLCJpc3MiOiJUaG9tYXMiLCJleHAiOjE2MTkwNzM5NTAsImlhdCI6MTYxOTA3MjE1MH0.7Os9SyBdsFbEgMZoG6ZwOlqqgx0qpbk36B2AOKKixXY";try {jwtUtil.verifyToken(token);} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}
