自定义基于 jwt token的二次摘要签名认证
一:依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0</version>
</dependency>
二:jwt认证结果
package com.auth;
import java.util.Map;
/**
* @author Administrator
*/
public class JwtVerifyResult{
/** 1 是否成功 **/
private Boolean success;
/** 2 返回消息 **/
private String msg;
/** 3 jwt负载 **/
private Map<String,String> payload;
public JwtVerifyResult() {
}
public JwtVerifyResult(Boolean success, String msg) {
this.success = success;
this.msg = msg;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, String> getPayload() {
return payload;
}
public void setPayload(Map<String, String> payload) {
this.payload = payload;
}
@Override
public String toString() {
return "JwtResult{" +
"success=" + success +
", msg='" + msg + '\'' +
", payload=" + payload +
'}';
}
}
三:认证工具类
package com.sdhttp.util.auth;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.RegisteredPayload;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author Administrator
*/
@Component
public class UtilJwt {
/**
* jwt 签名秘钥,可以更换其他不常用的自定义签名器: JWTSignerUtil
*/
static final JWTSigner jwtSigner = JWTSignerUtil.hs256("!34ADASfhsrujwstrfhwdg@#$$@#efh3".getBytes());
/**
* 自定义二重认证 token key
*/
static final String AUTHTOKEN_KEY = "at";
public static void main(String[] args) {
Map<String,String> payload = new HashMap<>();
payload.put("uuid","345345");
payload.put("ts",DateUtil.current()+"");
String testAppkey = "testAppkey";
String testAppsecret = "*************************";
String token = createToken(payload,testAppkey,testAppsecret);
System.out.println(token);
JwtVerifyResult jwtVerifyResult = validateToken(token,testAppkey,testAppsecret);
if(jwtVerifyResult.getSuccess()){
payload = jwtVerifyResult.getPayload();
System.out.println("负载:"+payload.toString());
}else{
System.out.println("验证失败:"+jwtVerifyResult.toString());
}
}
/**
* 生成安全的 jwt token
* 理论上:内部系统交互:每隔3个月,更新 appkey,appsecret
* 外部系统交互:不用更新 appkey,appsecret,但必须要求其验证负载 ts 和 at(authToken)
* <p>
* 时间校验具有多种方法:生效时间、失效时间、签发时间
* 此处只用失效时间校验:https://www.hutool.cn/docs/#/jwt/JWT验证-JWTValidator
* <p>
* 负载签名时间:iat(签发时间-强制)、exp(过期时间校验-强制)、at(内嵌再次token校验),已被占用
* 可能使用的高级占用参数:详见 hutool 的RegisteredPayload 接口
*
* @param payload jwt负载
* @param appkey 签名key
* @param appsecret 签名秘钥
* @return
*/
public static String createToken(Map<String, String> payload, String appkey, String appsecret) {
Date dateNow = new Date();
//1.构建自定义的签名摘要(安全2)
System.out.println(dateNow.getTime());
String authToken = SecureUtil.md5(SecureUtil.sha256(appkey + "_" + DateUtil.formatDateTime(dateNow) + "_" + appsecret));
payload.put(AUTHTOKEN_KEY, authToken);
//3.创建 jwt 签名,存放参数;2.签名时间 3。设置过期时间为当天结束(validateToken方法:容忍校验2分钟),4.hash256签名为(安全4)
return JWT.create()
.addPayloads(payload)
.setIssuedAt(dateNow)
.setExpiresAt(DateUtil.endOfDay(dateNow))
.sign(jwtSigner);
}
/**
* jwt 验证,1)hash256签名,2)失效时间,3)自定义二重安全校验
*
* @param token 生成的jwt token
* @param appkey 签名key
* @param appsecret 签名秘钥
* @return map :当error = "ok",会额外多出 payload
*/
public static JwtVerifyResult validateToken(String token, String appkey, String appsecret) {
try {
JWT jwt = JWT.of(token);
jwt.setSigner(jwtSigner);
//1.验证hash256签名
Boolean hs256Bool = jwt.verify();
if (!hs256Bool) {
return new JwtVerifyResult(false, "hash256签名校验失败");
}
//2.验证签名的时间,容忍度为2分钟
Boolean validateBool = jwt.validate(120L);
if (!validateBool) {
return new JwtVerifyResult(false, "时间负载校验失败");
}
//3.获取二重 authToken 认证校验
JWTPayload jwtPayload = jwt.getPayload();
Date subAtDate = jwtPayload.getClaimsJson().getDate(RegisteredPayload.ISSUED_AT);
String atScource = String.valueOf(jwtPayload.getClaim(AUTHTOKEN_KEY));
String authToken = SecureUtil.md5(SecureUtil.sha256(appkey + "_" +DateUtil.formatDateTime(subAtDate) + "_" + appsecret));
if (StrUtil.isEmpty(atScource) || !atScource.equals(authToken)) {
return new JwtVerifyResult(false, "二重身份认证校验失败");
}
//4.将负载参数转换成map返回
JSONObject payloadJson = jwtPayload.getClaimsJson();
payloadJson.remove(AUTHTOKEN_KEY);
JwtVerifyResult jwtVerifyResult = new JwtVerifyResult(true, "签名有效");
jwtVerifyResult.setPayload(JSONUtil.toBean(payloadJson, new TypeReference<Map<String, String>>() {
}, true));
return jwtVerifyResult;
} catch (ValidateException e) {
return new JwtVerifyResult(true, "无效的签名");
}
}
}