@RestController
@RequestMapping("/api/v2/jwt")
public class JwtApi {
private static Logger logger = LoggerFactory.getLogger(JwtApi.class);
@Autowired
private JwtService jwtService;
@Autowired
private AccountsService accountsService;
/**
* 创建jwt Token
*
* @return
*/
@RequestMapping(path = "/getJwtToken", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public JSONObject getJwtToken(HttpServletRequest httpServletRequest) {
JSONObject jb = new JSONObject();
Long userId = Long.valueOf(httpServletRequest.getAttribute(WebStarterApplicationFilter.USER_ID_KEY).toString());
String loginName = accountsService.findLoginNameByUserId(userId);
if (StringUtils.isEmpty(loginName)) {
jb.put("code", "500");
jb.put("message", "用户未登录");
return jb;
}
// 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
Map<String, Object> claims = new HashMap<String, Object>();
logger.info("getJwtToken loginName is -> " + loginName);
claims.put("loginName", loginName);
return jwtService.createJwtToken(claims);
}
/**
* 验证jwt Token
*
* @return
* @throws Exception
*/
@RequestMapping(path = "/checkJwtToken", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public JSONObject checkJwtToken(String param) throws Exception {
JSONObject jb = new JSONObject();
logger.info("/api/v2/jwt/checkJwtToken[MX-AUTHORIZATION]>>>" + param);
if (StringUtils.isEmpty(param)) {
ErrorJson errorJson = new ErrorJson("20000", "jwt token无效或不存在");
logger.warn("/api/v2/jwt/checkJwtToken>>>" + errorJson.toJson());
}
Claims claims = jwtService.parseJWT(param);
logger.info("/api/v2/jwt/checkJwtToken[MX-AUTHORIZATION]>>>" + JSONObject.toJSONString(claims));
if (StringUtils.isEmpty(claims)) {
ErrorJson errorJson = new ErrorJson("20000", "jwt token无效或不存在");
}
jb.put("code", "200");
jb.put("message", "success");
jb.put("claims", claims);
return jb;
}
}
@Service
public class JwtService {
private static Logger logger = LoggerFactory.getLogger(JwtService.class);
// 密钥所在文件夹路径
private static String caUrl = ApplicationConfig.getConfig("jwt.ca.url") + "/";
/**
* jwt 实现
* 基于需求实现 私钥加密 公钥解密,同时实现 RS256 算法, 注意:此算法要求加密密钥格式为 PKCS8
* <p>
* 生成的 jwt 可去 jwt.io 验证
* <p>
* 此类中提供 加密方法以及解密方法,解密方法中注意如果 jwt 过期会跑出异常 需要处理
*/
public JSONObject createJwtToken(Map<String, Object> claims) {
// 请求时间
Long requestTime = System.currentTimeMillis();
// 过期时长
int overdue = Integer.valueOf(ApplicationConfig.getConfig("jwt.overdue"));
Calendar ca = Calendar.getInstance();
ca.add(Calendar.DATE, overdue);
// 过期差值
long dueTime = ca.getTimeInMillis() - requestTime;
logger.info("createHrJwt is start -> requestTime : " + requestTime + "; overdue : " + overdue);
// // 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
// Map<String, Object> claims = new HashMap<String, Object>();
// // 生成用户的唯一标示,根据用户登录名MD5加密生成
// String login_name = currentUserVo.getLogin_name();
// String sub = AesHelper.doAES(login_name, Cipher.ENCRYPT_MODE);
//
// claims.put("sub", sub);//用户中心中用户的唯一标示
// claims.put("username", login_name);//用户中心中,用户的完整名称(包含所在域名称)
// String preferred_username = login_name.substring(login_name.indexOf("\\") + 1);
// claims.put("preferred_username", preferred_username);//用户名
// claims.put("ds_uid", currentUserVo.getExt());//ENT_USER_DB中,用户的唯一标示
// claims.put("name", currentUserVo.getName());//用户名称
// claims.put("phone_number", currentUserVo.getPhone());//用户注册手机号码
// claims.put("email", currentUserVo.getEmail());//用户注册邮箱
// claims.put("exp", ca.getTimeInMillis());//过期时间
claims.put("iat", requestTime);//签发日期
// // 过网关 追加
// claims.put("email_verified", true);//邮箱是否已验证
// claims.put("roles", "");// jwt 常用的标准定义
// claims.put("iss", "");//谁发的
// claims.put("aud", "");//发给谁
// claims.put("phone_number_verified", true);//电话是否已验证
// claims.put("title", "");//职务id
// claims.put("access_level", "");//谁请求的 客户端or扫码
// 签发时间
Date iat = new Date(requestTime);
// 过期时间
Date exp = new Date(ca.getTimeInMillis());
// 生成JWT
String jwt = null;
logger.info("createHrJwt is start -> iat : " + iat + "; exp : " + exp + "; claims : " + claims.toString());
try {
jwt = createJWT(iat, exp, dueTime, claims);
} catch (Exception e) {
e.printStackTrace();
}
// 返回构造
JSONObject jb = new JSONObject();
jb.put("jwt", jwt);
//jb.put("dueTime", dueTime / 1000);//过期差值,用于前端判断多久过期,单位:秒
jb.put("iatTime", requestTime / 1000);//签发日期,单位:秒
jb.put("code", "200");
jb.put("message", "success");
return jb;
}
/**
* 创建 Jwt
*
* @param iat 签发时间
* @param exp 过期时间
* @param dueTime
* @param claims
* @return
* @throws Exception
*/
private static String createJWT(Date iat, Date exp, long dueTime, Map<String, Object> claims) throws Exception {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256; //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
// long nowMillis = System.currentTimeMillis();//生成JWT的时间
// Date now = new Date(nowMillis);
Map<String, Object> header = new HashMap<String, Object>();
header.put("kid", "JWT_HR");
header.put("typ", "JWT"); //令牌统一写为JWT
header.put("alg", "RS256");//签名算法。默认值是RS256。
//获取私钥
PrivateKey key = getPKCS8PrivateKey();
// SecretKey key = generalKey();//生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
//下面就是在为payload添加各种标准声明和私有声明了
JwtBuilder builder = Jwts.builder() //这里其实就是new一个JwtBuilder,设置jwt的body
.setHeader(header)
.setClaims(claims) //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setIssuedAt(iat) //iat: jwt的签发时间
.signWith(signatureAlgorithm, key);//设置签名使用的签名算法和签名使用的秘钥
// if (dueTime >= 0) {
// long expMillis = nowMillis + dueTime;
// Date exp = new Date(expMillis);
// builder.setExpiration(exp); //设置过期时间
// }
return builder.compact(); //就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
}
/**
* @return
* @throws Exception
*/
private static PrivateKey getPKCS8PrivateKey() throws Exception {
// Remove markers and new line characters in private key
String pem = initPem("");
if (null != pem && !"".equals(pem)) {
String realPK = pem.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] b1 = Base64.decodeBase64(realPK);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
return null;
}
/**
* 解密jwt
*
* @param jwt
* @return
* @throws Exception
*/
public Claims parseJWT(String jwt) throws Exception {
//String publicKeyStr = initPem("publickey.pem");
String publicKeyStr = "-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhfJgMRPQwep1FlXE/RaYumSWWecqUJzSkqNgjAmIxXsyMNguSa0WBiCTAozMvnb55Ck32Mr67ueDu1yoysJIvvS/MSlLSF6nak0GRZA/Ng75Fp5AdNK5dXOyfFSxkzm8BUM96vDq2fpBzLfC\n" +
"-----END PUBLIC KEY-----";
logger.info("publicKeyStr is " + publicKeyStr);
//获取公钥
if (null != publicKeyStr && !"".equals(publicKeyStr)) {
String pem = publicKeyStr.replaceAll("-----BEGIN PUBLIC KEY-----", "")
.replaceAll("-----END PUBLIC KEY-----", "")
.replaceAll("\n", "");
logger.info("pem is " + pem);
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pem));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
logger.info("publicKey is " + publicKey);
//使用公钥,进行验签
Claims claims = Jwts.parser() //得到DefaultJwtParser
.setSigningKey(publicKey) //设置签名的秘钥
.parseClaimsJws(jwt).getBody();//设置需要解析的jwt
return claims;
}
return null;
}
/**
* 如果传入的参数为空, 默认获取私钥加密
* 如果传入的参数为空, 默认获取公钥解密
*/
private static String initPem(String pemName) {
if ("".equals(pemName)) {
pemName = ApplicationConfig.getConfig("jwt.pemName");
} else {
pemName = caUrl + pemName;
}
logger.info("pemName is " + pemName);
String pk = caUrl + pemName;
logger.info("pk is " + pk);
File file = new File(pk);
String pem = null;
// 私钥不存在
if (!file.exists()) {
return pem;
}
String encoding = "UTF-8";
Long filelength = file.length();
byte[] filecontent = new byte[filelength.intValue()];
try {
FileInputStream in = new FileInputStream(file);
in.read(filecontent);
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
pem = new String(filecontent, encoding);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return pem;
}
}
版权声明:本文为changhenshui1990原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。