Java实现JWT

JWT(JSON Web Token)

        JWT在web环境中通常作为一种用户凭证,它是一个以.分隔,由heaer.payload.signature组成的字符串,其中每个部分都经过Base64编码。header包含token采用的算法、类型(JWT)、KeyId等信息,payload中包含token的发行人iss、过期时间exp、业务信息等(比如用户ID、昵称等),signature是对(header.payload)的Base64组合串用密钥加密得到的摘要,代表header.payload的签名结果。

JWT生成/验证流程

       生成:对header、payload分别进行Base64编码,得到两个base64串,以.拼接这两个串,将该串通过MD5/SHA/MAC等算法生成签名信息得到signature。再以.拼接header.payload.signature,得到token    
       验证:以.为分隔符拆分token串,得到三个子部分,将header.payload采用相同的算法生成新的签名,对比签名来判断token是否被修改、如果有设置过期时间,则进行过期检测(最终通常会从payload中提取信息存储到一个ThreadLocal对象中)。 (注意由于这些摘要算法是不可逆的,所以signature是不能解密的)

JWT代码实现

         实际开发中你可以自己实现,也可以采用第三方库,但通常都会采用Mac算法(Message Authentication Codes消息认证码算法), Mac兼容了MD5及SHA的特性,并且添加了密钥(这里也可称为盐值),能更好的降低碰撞机率,防止暴力破解。

         这里两份代码都贴一下,自己写的话,可以参考下面这份比较粗糙的代码,主要是为了说明流程。第三方库的底层实现流程也类似,通常也是采用javax.crypto.mac算法


import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Objects;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import com.auth0.jwt.exceptions.SignatureGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JWTDemo {
	
	// 密钥(或者叫盐值)
	private String saltSecret = "saltSecret";
	
	// 算法
	private String algorithm = "HmacSHA256";
	
	public String sign(Map<String,Object> headerMap,Map<String,Object> payloadMap) throws SignatureGenerationException, JsonProcessingException, InvalidKeyException, NoSuchAlgorithmException {
		// 0、得到header.payload的Base64串
		ObjectMapper objMapper = new ObjectMapper();
		String headerJson = objMapper.writeValueAsString(headerMap);
		String payloadJson = objMapper.writeValueAsString(payloadMap);
        String headerB64 = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
        String payloadB64 = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
        String content = String.format("%s.%s", headerB64, payloadB64);
        // 1、生成摘要信息
        byte[] signatureBytes = createSignature(algorithm,saltSecret.getBytes(StandardCharsets.UTF_8),content.getBytes(StandardCharsets.UTF_8));
        String signature = Base64.encodeBase64URLSafeString((signatureBytes));
        return String.format("%s.%s", content, signature);
    }
	
	private byte[] createSignature(String algorithm, byte[] secretBytes, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException {
        final Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(secretBytes, algorithm));
        return mac.doFinal(contentBytes);
    }
	
	@SuppressWarnings("unchecked")
	public void verifySignature(String token) throws InvalidKeyException, NoSuchAlgorithmException, JsonMappingException, JsonProcessingException {
		// 0、解析token,得到header.payload的Base64串
		String[] parts = token.split("\\.");
		String headerB64 = parts[0];
        String payloadB64 = parts[1];
        String signatureB64 = parts[2];
        byte[] contentBytes = String.format("%s.%s", headerB64, payloadB64).getBytes(StandardCharsets.UTF_8);
        
        // 1、生成摘要,进行摘要对比
        byte[] signatureBytes = Base64.decodeBase64(signatureB64);
        boolean validResult = this.verifySignature(algorithm, saltSecret.getBytes(StandardCharsets.UTF_8), contentBytes, signatureBytes);
        if(!validResult) {
        	// ...验签失败
        }
        // 2、过期判断
        ObjectMapper objMapper = new ObjectMapper();
        String payloadJson = new String(Base64.decodeBase64(payloadB64),StandardCharsets.UTF_8);
        Map<String,Object> payloadMap = objMapper.readValue(payloadJson, Map.class);
        if(Objects.nonNull(payloadMap.get("exp"))){
        	long endTime = Long.parseLong(String.valueOf(payloadMap.get("exp")));
        	long currSecond = System.currentTimeMillis()/1000;
        	if(currSecond > endTime) {
        		// ...超时异常
        	}
        }
	}
    
    private boolean verifySignature(String algorithm, byte[] secretBytes, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException {
    	// 生成新的签名,对比签名
    	return MessageDigest.isEqual(createSignature(algorithm, secretBytes, contentBytes), signatureBytes);
    }
}

    关于第三方库可以使用,可以使用Java中比较推荐的JJWT(java-jwt)    

//gradle引入
implementation 'com.auth0:java-jwt:3.4.0'  

// maven引入
<dependency>
		<groupId>com.auth0</groupId>
		<artifactId>java-jwt</artifactId>
		<version>3.4.0</version>
</dependency>
public class JWTMaker {
	
	@Value("${xxxxx}")
    private String tokenSalt;
    
    private final String ISSUER_NAME = "xxxx";
    
    private final Integer JWT_EXPIRE_TIME = 8;
    
    public String sign(UserInfo user) {
        String token = null;
        try {
            Date expireAt = Date.from(LocalDateTime.now().plusHours(JWT_EXPIRE_TIME).atZone(ZoneId.systemDefault()).toInstant());
            token = JWT.create()
                    .withIssuer(ISSUER_NAME)
                    .withClaim("userId", user.getUserId())
                    .withClaim("name", user.getName())
                    .withExpiresAt(expireAt)
                    .sign(Algorithm.HMAC256(tokenSalt));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return token;
    }

    /**
     * 签名验证
     * @param token
     * @return
     */
    public boolean verify(String token) {
        try {
        	// 0、验签
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(tokenSalt)).withIssuer(ISSUER_NAME).build();
            DecodedJWT jwt = verifier.verify(token);
            
            // 1、提取信息
            UserInfo user = new UserInfo();
            user.setUserId(jwt.getClaim("userId").asString());
            user.setName(jwt.getClaim("name").asString());
            
            // 2、放入到ThreadLocal中,与当前线程绑定。
            // ...
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值