JJWT:Java json web token ,就是基于Java实现的JWT。

首先说一下什么是JWT?

其实就是一个字符串:由三部分组成,头部、载荷与签名。

头部:(header)

一般放一些声明信息,比如:用什么加密,用什么编码。

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。

这也可以被表示成一个JSON对象:

         {"typ":"JWT","alg":"HS256"}

使用base64进行编码之后:(至于为什么要用base64,这个我也不知道,好像jwt默认就是用base64编码的)

         eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

 

载荷(playload)

这里面放的是主要的有用的信息;

比如说:登陆的用户名,登录时间,登录的过期时间,

还可以放一些自己自定义的一些信息,不限制。

一般载荷这块个性化比较多。

 

载荷就是存放有效信息的地方,这些有效信息包含三个部分:

(1)标准中注册的声明(建议但不强制使用)

                    iss: jwt签发者

                    sub: jwt所面向的用户     //这个以后就是放我们登录的用户名

                    aud: 接收jwt的一方

                    exp: jwt的过期时间,这个过期时间必须要大于签发时间    //过期时间也可以放

                    nbf: 定义在什么时间之前,该jwt都是不可用的.

                    iat: jwt的签发时间

                   jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

(2)公共的声明

            公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.

该部分在客户端可解密。

(3)私有的声明

           私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,

           因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

           这个指的就是自定义的claim。

                       定义一个payload:

                                     {"sub":"1234567890","name":"John Doe","admin":true}

                       然后将其进行base64编码,得到Jwt的第二部分。

                                    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

 

签名(signature)

就是把头部和载荷加到一起,进行加密(加密之前先加个盐进去)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

             header (base64后的)

             payload (base64后的)

             secret (盐)

这个部分需要base64加密后的header和base64加密后的payload使用。

连接组成的字符串,

然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

            TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
            eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
            TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:这里面有几个要注意的地方:

secret 是保存在服务器端的,jwt的签发生成也是在服务器端的。

secret 就是用来进行 jwt 的签发和 jwt 的验证。

所以,它是服务器端的私钥,在任何场景都不应该流露出去,不能被泄露。

一但客户端知道了这个secret ,那就意味着客户端可以自我签发 jwt 了。

因此要注意 secret 的保密安全性。

(有的公司隔一段时间换一次,不同的微服务使用不同的 secret ,因此最好写在配置文件中,方便修改)

 

使用步骤:

pom引入依赖:

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.6.0</version>
</dependency>

demo:

生成token方法:

public static void main(String[] args) {
		String secretKey = "123";
		
		JwtBuilder jwtBuilder = Jwts.builder()
			.setId("666")
			.setSubject("小马")
			.setIssuedAt(new Date())//登录时间-也就是签发时间(签发给你token的时间)
			.setExpiration(new Date(new Date().getTime() + (60 * 1000)))//设置过期时间-一分钟
			.signWith(SignatureAlgorithm.HS256, secretKey.getBytes())//设置签名秘钥
			
            /** token添加自定义属性 **/
			.claim("role", "admin");//设置用户角色

		System.out.println(jwtBuilder.compact());
	}

解析 jwt :

public static void main(String[] args) {
		String secretKey = "123";
		String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjYiLCJzdWIiOiLlsI_pqawiLCJpYXQiOjE1NDg5MTYxMDQsImV4cCI6MTU0ODkxNjE2NCwicm9sZSI6ImFkbWluIn0.kvuuavo0VoZkvgz8naQ13BQ7uIXjNs0A23I_Wi5AbLk";
		
		Claims claims = Jwts.parser()
			.setSigningKey(secretKey.getBytes())//设置对应的盐
			.parseClaimsJws(token)
			.getBody();
		//Claims 这个claims你就可以认为它是一个map,里面是一个个key-value
		
		System.out.println("用户id:" + claims.getId());
		System.out.println("用户名称:" + claims.getSubject());
		System.out.println("登录时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(claims.getIssuedAt()));
		System.out.println("过期时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(claims.getExpiration()));
		System.out.println("用户角色:" + claims.get("role"));
		
		
		//如果过期的token请求过来,它是直接报错的,
		//所以一般我们解析token这块,会加上try-catch
	}

 

业务中如何使用呢?

一般我们会把token的验证放在拦截器中。

1:创建拦截器类

@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {
	@Autowired
	private JwtUtil jwtUtil;
	
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)throws Exception {
	            System.out.println("经过了拦截器");
		return true;
	}
}

2:配置拦截器类

@Configuration
public class ApplicationConfig extends WebMvcConfigurationSupport {
	@Autowired
	private JwtInterceptor jwtInterceptor;
	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(jwtInterceptor).
		addPathPatterns("/**").
		excludePathPatterns("/**/login");
	}
}

 

3:最后在具体实现自己的 拦截器方法:

public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{
        if(!(handler instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod= (HandlerMethod) handler;
        HttpRequestInfo requestInfo = new HttpRequestInfo(request);
        String url = request.getRequestURL().toString();
        String tokenHeader = request.getHeader("Authorization");
        .......
        .......
        //做一些自己的检验

}