目录

一、什么是 JWT ?

二、什么时候使用 JWT ?

三、JWT 格式

1、Header

2、Payload

3、Signature

4、 JWT实现:


官网

  • 官网JSON Web Tokens – jwt.io
  • RFC 7519文档RFC 7519: JSON Web Token (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

1、Header

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、依赖引入

io.jsonwebtokenjjwt${jwt-jsonwebtoken.version}com.alibabafastjsonRELEASE

2、生成Token

// 签名密钥 private static final String SECRET = "!Doker$";public String createToken(Map 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 functs=(List) authResult.getAuthorities();//当前功能列表String loginName=authResult.getName();//登录名Users obj=(Users)authResult.getPrincipal();//用户信息String token=JwtUtil.createToken(loginName,functs,obj);//生成tokenTOKEN_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));//写入jsonpw.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 验证不通过");} //验证成功

Copyright © maxssl.com 版权所有 浙ICP备2022011180号