JWT(JSON Web Token )详解及实例

目录

一、什么是 JWT ?

二、什么时候使用 JWT ?

三、JWT 格式

1、Header

2、Payload

3、Signature

4、 JWT实现:


官网

一、什么是 JWT ?

JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。
JWT可以使用密码(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

加签后的token 能够使用 JWT 里的算法验证 json 的完整性.

二、什么时候使用 JWT ?

  1. 授权
  2. 信息交换
  3. 使用方式:服务端根据规范生成一个令牌(token),并且发放给客户端(保存在客户端)。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。
  4. 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)

JWT优势
    简洁:可以通过URL、POST参数或者在HTTP Header发送,因为数据量小,传输速度也很快
    自包含:负载中包含了所有用户所需要的信息,避免了多次查询数据库
    因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式       都支持
    不需要在服务端保存会话信息,特别适合用于分布式微服务。
    更适合用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时使用token认证方       式会简单很多
     单点登录友好:由于cookie无法跨域,难以实现单点登录。但是,使用token进行认证的话,            token可以被保存在客户端的任意位置的内存中,不一定是cookie,所以不依赖cookie,不会          存在这些问题
 

三、JWT 格式

使用逗号分隔的三部分 :

  • Header
  • Payload
  • Signature
    token 格式:xxxxx.yyyyy.zzzzz

Header 通常由 token 类型和签名算法名两部分组成.是token的第1部分
例如:

{
"alg": "HS256", // 签名算法
"typ": "JWT" // token类型
}

然后, 这个JSON 使用 Base64Url 编码后放到 JWT 的第1部分.

2、Payload

Payload 是token的第2部分.包含了一些声明(claims).声明的名字必须是唯一的.
claims 是包含了 用户数据和其他数据的陈述,

有三种类型的声明:

  1. Registered Claim Name
    预定义好的一些声明(如果有需要就使用,没需要可不使用):
  • "iss"
  • "sub"
  • "aud"
  • "exp"
  • "nbf"
  • "iat"
  • "jti"

更多参见 JSON Web Token (JWT)

  1. Public Claim Names
    公共的声明,可以预先定义在 IANA JSON Web Token Registry 中,或者定义在1个能解决名字冲突的地方.

  2. Private Claim Names
    双方共享数据使用的私有名字.既不在 Registered Claim Name 也不在 Public Claim Names 中.

payload 示例

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

JSON 

然后, 这个JSON 使用 Base64Url 编码后放到 JWT 的第2部分.

3、Signature

拿到编码后的 header 和 编码后的 payload 使用 密码进行签名.

使用 HMAC SHA256 加签示例:

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

Signature需要使用编码后的header和payload以及我们提供的一个秘钥,然后使用header中指定的签名算法进行签名,签名的作用是保证JWT没有被篡改过

HMACSHA256(base64UrlEncode(header)+“.”+base64UrlEncode(payload),secret)

实际上是对头部信息和负载内容进行签名,防止内容被篡改,如果有人对头部以及负载内容解码后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT上附带的签名是不一样的。如果要对新的头部和负载进行签名,由于不知道服务器加密时使用的秘钥,得出来的结果也是不一样的

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。

4、 JWT实现:

  1、依赖引入

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jwt-jsonwebtoken.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>RELEASE</version>
        </dependency>

2、生成Token 

// 签名密钥
 private static final String SECRET = "!Doker$";
public String createToken(Map<String, Object> claims, String subject) {
        final Date createdDate = clock.now();
        final Date expirationDate = calculateExpirationDate(createdDate);
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(createdDate)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, Algorithm.HMAC256(SECRET))
                .compact();
    }

    private Date calculateExpirationDate(Date createdDate) {
        return new Date(createdDate.getTime() + expiration * 1000);
    }

3、刷新Token 

public String RefreshToken(String token) {
        final Date createdDate = clock.now();
        final Date expirationDate = calculateExpirationDate(createdDate);
        final Claims claims = getAllClaimsFromToken(token);
        claims.setIssuedAt(createdDate);
        claims.setExpiration(expirationDate);
        return Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, Secret)
                .compact();
    }

4、token发送给前端
传入当前用户的功能与用户信息,登录名生成token,写入response的返回头中,前端获取后保存在前端的本地缓存中,后续前端请求要把token放在头header里。

//登录成功之后
List<Object> functs=(List<Object>) authResult.getAuthorities();
//当前功能列表
String loginName=authResult.getName();//登录名
Users obj=(Users)authResult.getPrincipal();//用户信息
String token=JwtUtil.createToken(loginName,functs,obj);

//生成token  TOKEN_HEADER= Authorization TOKEN_PREFIX=Bearer token值
response.setHeader(JwtUtil.TOKEN_HEADER,JwtUtil.TOKEN_PREFIX+token);
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK); //个人编写的视图对象
DTO dto=new DTO<>();
dto.setCode("000000");
dto.setMessage("认证通过");

PrintWriter pw=response.getWriter();
pw.write(JsonUtil.set(dto));//写入json
pw.flush();//强制刷新
pw.close();//关闭流

5、验证用户请求携带token

String header = request.getHeader(JwtUtil.TOKEN_HEADER);
 if (null == header || !header.toLowerCase().startsWith(JwtUtil.TOKEN_PREFIX)) { 
  // 如果头部 Authorization 未设置或者不是 basic 认证头部,则当前 
  // 请求不是该过滤器关注的对象,直接放行,继续filter chain 的执行
   chain.doFilter(request, response);
   return;
} 

try {
  String token = header.replace(JwtUtil.TOKEN_PREFIX, ""); 
  // 验证token是否过期
  if (JwtUtil.isExpiration(token)) { 
      throw new javax.security.sasl.AuthenticationException("token 验证不通过");
} 

//檢查token是否能解析
Users user = (Users) JwtUtil.getUser(token); 
if (null == user) { 
    throw new javax.security.sasl.AuthenticationException("token 验证不通过");
} 

//验证成功

猜你喜欢

转载自blog.csdn.net/leesinbad/article/details/128012582
今日推荐