Java JWT生成token信息

什么时候使用 JWT

JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。JWT 虽然对于大多数网站都没有用,但是有几种情况它是很有用的。如果你正在构建从服务器到服务器或客户端到服务器(如:移动应用 APP 或单页面应用)的 API服务,那么使用 JWT 是非常明智的。

JWT使用流程

  1. 初次登录:用户初次登录,输入用户名密码
  2. 密码验证:服务器从数据库取出用户名和密码进行验证
  3. 生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT
  4. 返还JWT:服务器的HTTP RESPONSE中将JWT返还
  5. 带JWT的请求:以后客户端发起请求,HTTP REQUEST
  6. HEADER中的Authorizatio字段都要有值,为JWT
  7. 服务器验证JWT

2. JWT的原则

JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,如下所示。

{
"UserName": "Chongchong",
"Role": "Admin",
"Expire": "2018-08-08 20:15:56"
}
之后,当用户与服务器通信时,客户在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据,服务器将在生成对象时添加签名(有关详细信息,请参阅下文)。服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。

3. JWT的数据结构

典型的,一个JWT看起来如下图。

该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。注意JWT对象为一个长字串,各字串之间也没有换行符,此处为了演示需要,我们特意分行并用不同颜色表示了。每一个子串表示了一个功能块,总共有以下三个部分:

JWT的三个部分如下。JWT头、有效载荷和签名,将它们写成一行如下。我们将在下面介绍这三个部分。

3.1 JWT头

JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。

{
"alg": "HS256",
"typ": "JWT"
}
在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。

最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

3.2 有效载荷

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。

iss:发行人

exp:到期时间

sub:主题

aud:用户

nbf:在此之前不可用

iat:发布时间

jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,如下例:

{
"sub": "1234567890",
"name": "chongchong",
"admin": true
}

请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。JSON对象也使用Base64 URL算法转换为字符串保存。

案例演示

下面显示了一个登录请求成功之后服务端返回的Token,它由编码头部(header)、编码有效载(payload)和签名(signature)通过(.)拼接而成:
 

JWT并不对数据进行加密,而是对数据进行签名,保证不被篡改。除了在登录中可以用到,在进行邮箱校验和图形验证码也可以用到。

JWT缺点及对应的解决方案

登录状态信息续签问题。比如设置token的有效期为一个小时,那么一个小时后,如果用户仍然在这个web应用上,这个时候当然不能指望用户再登录一次。判断还有多久这个token会过期,在token快要过期时,返回一个新的token。

用户主动注销。JWT并不支持用户主动退出登录,当然,可以在客户端删除这个token,但在别处使用的token仍然可以正常访问。为了支持注销,我的解决方案是在注销时将该token加入黑名单。可以给用户加一个登录状态字段。登录之后这个字段为1。然后退出之后这个字段为0。如果退出了,就算有JWT还是退出状态了。

Java maven依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</version>
</dependency>

JWT加解密

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author hudy
 * @date 2024/6/7
 */
public class JWTEncrypt {

  /**
   * 分配给供应商的签发主体
   */
  private static final String JWT_ISSUER = "明文内容加密";
  // 签名密钥
  private static final String APP_SECRET = "33db7be3237f0a29f2150aaca789fa6f";

  // Bearer令牌
  private static final String BEARER = "Bearer ";

  // 日志记录器
  private static final Logger logger = LoggerFactory.getLogger(JWTEncrypt.class);

  // 生成一个长期有效的Token
  public static String genToken() throws JWTCreationException, UnsupportedEncodingException {
    return BEARER + JWT.create()
        .withIssuer(JWT_ISSUER)
        .withIssuedAt(new Date())
        .sign(Algorithm.HMAC256(APP_SECRET));
  }

  // 验证有效信用组织
  public static boolean verifyValidCreditOrg(String jwtSignToken, String appToken) {
    try {
      JWTVerifier verifier = JWT.require(Algorithm.HMAC256(appToken)).build();
      verifier.verify(jwtSignToken);
      return true;
    } catch (Exception e) {
      logger.error("JWT token验证异常,原因:{}", e);
    }
    return false;
  }

  // 获取签发者
  public static String getIssuer(String jwtSignToken) throws RuntimeException {
    try {
      return JWT.decode(jwtSignToken).getIssuer();
    } catch (Exception e) {
      logger.error("获取JWT的appkey发生异常,原因:{}", e);
      throw new RuntimeException("非法请求客户端");
    }
  }

  // 获取签发时间戳
  public static Long getIssuedAt(String jwtSignToken) throws RuntimeException {
    try {
      return JWT.decode(jwtSignToken).getIssuedAt().getTime() / 1000;
    } catch (Exception e) {
      logger.error("获取JWT的appkey发生异常,原因:", e);
      throw new RuntimeException("非法请求客户端");
    }
  }

  public static void main(String[] args) throws UnsupportedEncodingException {
    // 生成token
    String authorization = JWTEncrypt.genToken();
    System.out.println("请求头授权信息,header token=" + authorization);

    // 验证token
    String jwtSignToken = authorization.substring(BEARER.length());
    boolean verified = JWTEncrypt.verifyValidCreditOrg(jwtSignToken, APP_SECRET);
    System.out.println("验证结果=" + verified);

    // 解析签发主体
    String issuer = JWTEncrypt.getIssuer(jwtSignToken);
    System.out.println("签发主体=" + issuer);
  }
}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值