本文着重讲解在Java和Python中Jwt的使用,Jwt的加密/解密,同时能Java能解密Python的token。
这里使用 jjwt的方式
首先引入maven依赖
<dependency>
<groupid>io.jsonwebtokengroupid>
<artifactid>jjwtartifactid>
<version>0.7.0version>
dependency>
Java第二版代码如下:(第一版为了大家的方便,放再最后。)1
import io.jsonwebtoken.*;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author 慌途L
*/
@Component
public class JwtUtilsHelper {
private static final Logger logger = LoggerFactory.getLogger(JwtUtilsHelper.class);
private static final String SECRET_KEY = "adsfkoemejn13443#@^*(%$-=4=+*&%fe";
private static final String SECRET_KEY_REF = "adsfkoemejn13443#@^*(%$-=4=+*&%fe";
private static Integer ACCESS_EXPIRES = 60 * 60 * 4; //access_token有效时间,4hour
private static Integer REFRESH_EXPIRES = 60 * 60 * 8; //refresh_token有效时间,8hour
private static final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512;//指定签名的时候使用的签名算法
/**
* 获取加密key
* @param keys 过期时间变量
* @return
*/
private static Key getEncryptedKey(String keys){
Key key = new SecretKeySpec(keys.getBytes(), signatureAlgorithm.getJcaName());
return key;
}
/**
* 生成Jwt Token
* @param data JWT payload负载(自己需要封装的参数放这里,json格式的字符串)
* @param expiresDate 过期时间
* @param secretKey 签名的秘钥,和生成的签名的秘钥一模一样
* @return
*/
private static String createToken(String data, Integer expiresDate, String secretKey){
/** header BEGIN */
Map<String,Object> header = new HashMap<String, Object>();//JWT header头部
long time = new Date().getTime();
header.put("alg","HS512");//表示签名的算法,默认也是这个,可选
header.put("iat", time/1000);//签发时间(除以1000,得到十位毫秒值,去除微秒,因为python是十位)
header.put("exp", (time/1000) + expiresDate);//过期时间
/** header END */
JwtBuilder builder = Jwts.builder()
.setHeader(header) //new一个JwtBuilder,设置jwt的body
.setPayload(data) //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的payload赋值,
// 一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.signWith(signatureAlgorithm, getEncryptedKey(secretKey));//设置签名使用的签名算法和签名使用的秘钥
return builder.compact(); //开始压缩为xxx.xxx.xxx格式的jwt
}
/**
* 解密Jwt得到payload负载的数据
* @param jwt
* @param secretKey 签名的秘钥,和生成的签名的秘钥一模一样
* @return
*/
private static Claims decodeTokenPart(String jwt, String secretKey){
try {
Claims claims = Jwts.parser()
.setSigningKey(getEncryptedKey(secretKey))
.parseClaimsJws(jwt).getBody();
return claims;
} catch (Exception e) {
logger.debug("validate is Jwt token payload error :", e);
return null;
}
}
/**
* 解密Jwt得到全部数据
* @param jwt
* @param secretKey 签名的秘钥,和生成的签名的秘钥一模一样
* @return
*/
private static Jwt decodeTokenAll(String jwt, String secretKey){
try {
Jwt parse = Jwts.parser()
.setSigningKey(getEncryptedKey(secretKey))
.parse(jwt);
return parse;
} catch (Exception e) {
logger.debug("validate is Jwt token error :", e);
return null;
}
}
/**
* 生成Jwt token
* @param data JWT payload负载
* @return
*/
public static String accessTonken(String data) {
return createToken(data, ACCESS_EXPIRES, SECRET_KEY);
}
/**
* 生成Jwt token
* @param data JWT payload负载
* @return
*/
public static String refreshToken(String data) {
return createToken(data, REFRESH_EXPIRES, SECRET_KEY_REF);
}
/**
* 解密JWT (得到包含header,payload,signature全部数据)
* @param jwt
* @return
*/
public static Jwt accessTonkenDecodeAll(String jwt) {
return decodeTokenAll(jwt, SECRET_KEY);
}
/**
* 解密JWT (得到包含header,payload,signature全部数据)
* @param jwt
* @return
*/
public static Jwt refreshTokenDecodeAll(String jwt) {
return decodeTokenAll(jwt, SECRET_KEY_REF);
}
/**
* 解密jwt (得到payload部分的数据)
*
* @param jwt
* @return
*/
public static Claims accessTonkenDecodePart(String jwt) {
return decodeTokenPart(jwt, SECRET_KEY);
}
/**
* 解密jwt (得到payload部分的数据)
*
* @param jwt
* @return
*/
public static Claims refreshTokenDecodePart(String jwt){
return decodeTokenPart(jwt, SECRET_KEY_REF);
}
/**
* 判断token是否过期
*
* @param expiration token过期时间
* @return true:过期 false:未过期
*/
public static boolean isTokenExpired(Long expiration){
long nowDate = new Date().getTime() / 1000;
return (nowDate > expiration) ? true : false;
}
public static void main(String[] args) {
/** payload BEGIN */
int userId = 169061;
String userName = "admin";
String ip = "192.168.110.555";
Map<String,Object> payload = new HashMap<String, Object>();
payload.put("user_id",userId);
payload.put("user_name",userName);
payload.put("ip",ip);
/** payload END */
//得到jwt
String jwt = accessTonken(JSONObject.fromObject(payload).toString());
String jwt2 = refreshToken(JSONObject.fromObject(payload).toString());
System.out.println(jwt);
System.out.println(jwt2);
System.out.println("\n");
//解析jwt
Jwt parseJwt = accessTonkenDecodeAll(jwt);//四小时过期
Jwt parseJwt2 = refreshTokenDecodeAll(jwt2);//八小时过期
System.out.println("parseJwt=="+parseJwt);
System.out.println("parseJwt2=="+parseJwt2);
}
}
jwt==eyJleHAiOjE1NDI4NTAyMTUsImlhdCI6MTU0Mjg1MDIwMSwiYWxnIjoiSFM1MTIifQ.eyJ1c2VyX2lkIjoxNjkwNjEsInVzZXJfbmFtZSI6ImFkbWluIiwiaXAiOiIxOTIuMTY4LjExMC41NTUifQ.-BGabALIWolNBVqCsjwsmC-lE7AnJYe20MXWK2wEvbCGHdEiX9UStvmHguadyiSlksJmqghNHzLA4KcRuqnniw
claims=={user_id=169061, user_name=admin, ip=192.168.110.555}
parseJwt==header={exp=1542850215, iat=1542850201, alg=HS512},body={user_id=169061, user_name=admin, ip=192.168.110.555},signature=-BGabALIWolNBVqCsjwsmC-lE7AnJYe20MXWK2wEvbCGHdEiX9UStvmHguadyiSlksJmqghNHzLA4KcRuqnniw
解码网址:https://www.sojson.com/base64.html
eyJleHAiOjE1NDI4NTAyMTUsImlhdCI6MTU0Mjg1MDIwMSwiYWxnIjoiSFM1MTIifQ
eyJ1c2VyX2lkIjoxNjkwNjEsInVzZXJfbmFtZSI6ImFkbWluIiwiaXAiOiIxOTIuMTY4LjExMC41NTUifQ
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
def generate_auth_token(key, data, expiration=60 * 60 * 2):
"""获取token"""
# data = {'user_id': user_id}
s = Serializer(key, expires_in=expiration)
return s.dumps(data).decode('utf-8')
if __name__ == "__main__":
SECRET_KEY = "adsfkoemejn13443#@^*(%$-=4=+*&%fe" #key
ACCESS_EXPIRES = 60 * 60 * 4 # access_token有效时间,4hour
user_id = 169061
user_name = 'admin'
ip = '192.168.110.555'
access_tonken = generate_auth_token(
key=SECRET_KEY,
data={'user_id': user_id, 'user_name': user_name, 'ip': ip},#封装进payload的数据
expiration=ACCESS_EXPIRES #过期时间设置
)
print("%s\n%s" % (access_tonken))
quit(0)
eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MjgwMzg3MiwiZXhwIjoxNTQyODE4MjcyfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.klw-jeFDpN_WVq4VkYmkYsHNrxErbmrnBBUdgGLRfLW_5gTnIdT1-8p4i_ZDAM6BloD0DVcZOuazSLtMrz0NdQ
eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MjgwMzg3MiwiZXhwIjoxNTQyODMyNjcyfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.vRBhW37d5jck7PocGX7O9usZ96XsxKytWkFqdbflcfKeAXfqHTxul78Z3JSB4YkFL93VsW8eJiV9H3GPnz6Usw
eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MjgwMzg3MiwiZXhwIjoxNTQyODE4MjcyfQ
eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25h
bWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ
注:由于python中输出的时间戳是十位的毫秒值,而Java中时间戳为13位,所以注意转换。
个人没有涉及到,可以参考:https://www.cnblogs.com/byxxw/p/6742230.html
此文写的比较简单,各位可以多到网上参考参考。
可能描述和注释的思维比较跳,有不理解和写的不对的地方可以留言,及时更正
下面是第一版Java代码:
package test;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author 慌途L
*/
public class JWTTest {
private static final Logger logger = LoggerFactory.getLogger(JWTTest.class);
/**
* 秘钥key(跟python一样)
*/
private static String SECRET_KEY = "adsfkoemejn13443#@^*(%$-=4=+*&%fe";
/**
* access_token有效时间,4hour
*/
private static Integer ACCESS_EXPIRES = 60 * 60 * 4;
/**
* 使用什么加密算法,这里我使用HMacSHA512,Jwt默认HMacSHA256
*/
private static final String MAC_INSTANCE_NAME = "HMacSHA512";
/**
* 获取加密key
* (为什么还要加密key呢?)
* 因为Python里面已经将它加密再做的签名
* (这里被坑了一把)
* @return
*/
public static SecretKey getEncryptedKey(String keys){
SecretKey key = new SecretKeySpec(keys.getBytes(), MAC_INSTANCE_NAME);
return key;
}
/**
* 创建jwt
* @param payload JWT payload负载数据
* @return
* @throws Exception
*/
public static String accessTonken(Map<String,Object> payload) throws Exception {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512; //指定签名的时候使用的签名算法
/** header BEGIN */
/**
* 这里我覆盖了默认的签发时间和过期时间,所以下面异常里的token过期不会提示
*/
Map<String,Object> header = new HashMap<String, Object>();//JWT header头部
long time = new Date().getTime();
header.put("alg","HS512");//表示签名的算法,默认也是这个,可选
header.put("iat", time/1000);//签发时间(除以1000,得到十位毫秒值,去除微秒,因为python是十位)
header.put("exp", (time/1000) + expiresDate);//过期时间
/** header END */
JwtBuilder builder = Jwts.builder()
.setHeader(header) //new一个JwtBuilder,设置jwt的body
// .setIssuedAt(now) //签发时间(去除默认的设置体位置,我在header BEGIN里面封装了 )
.setClaims(payload) //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.signWith(signatureAlgorithm, getEncryptedKey(SECRET_KEY));//设置签名使用的签名算法和签名使用的秘钥
// builder .setExpiration(new Date(nowMillis + 1)); //设置过期时间(去除默认的设置体位置,我在header BEGIN里面封装了 )
return builder.compact(); //转为xxx.xxx.xxx格式的jwt
}
/**
* 解密jwt
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT_Claims(String jwt) throws Exception{
Claims claims = Jwts.parser() //得到DefaultJwtParser
.setSigningKey(getEncryptedKey(SECRET_KEY)) //设置签名的秘钥,和生成的签名的秘钥一模一样
.parseClaimsJws(jwt).getBody();//设置需要解析的jwt,body相对于payload
return claims;
}
/**
* 解密jwt
* @param jwt
* @return
* @throws Exception
*/
public static Jwt parseJWT(String jwt) throws Exception{
Jwt parse = Jwts.parser() //得到DefaultJwtParser
.setSigningKey(getEncryptedKey(SECRET_KEY)) //设置签名的秘钥,和生成的签名的秘钥一模一样
.parse(jwt); //设置需要解析的jwt
return parse;
}
public static void main(String[] args) {
/** payload BEGIN */
//封装数据
int userId = 169061;
String userName = "admin";
String ip = "192.168.110.555";
Map<String,Object> payload = new HashMap<String, Object>();
payload.put("user_id",userId);
payload.put("user_name",userName);
payload.put("ip",ip);
/** payload END */
String jwt = null;
try {
jwt = accessTonken(payload);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("jwt=="+jwt);
/** python中生成的数据 进行java解密 */
String jwt2 = "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0Mjc5MzcxOSwiZXhwIjoxNTQyNzkzNzIwfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.4ozHmr8PcxEFBchU10_27VSNob1iWLcttguTQk8FPTPtSfR2G4-jSWVuEVOrJESyES-7iza8f1nJb4vCc-UTKA";
String jwt3 = "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0Mjc5OTE4MywiZXhwIjoxNTQyODEzNTgzfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.sPcjFHbdq9PxAOhDsfq1TOYINB-tkLnXQOSz4KSkgZkkMt74TlgX8stBzpgyRTNihq8QadOyGscYHnebiu5WGQ";
/** payload中的数据 */
Claims claims = null;
/** 整个jwt的数据,包括header,payload ,signature*/
Jwt parseJwt = null;
try {
claims = parseJWT_Claims(jwt);
parseJwt = parseJWT(jwt);
} catch (Exception e) {
/**
* 如果使用默认的builder .setExpiration(new Date(nowMillis + 1))方法可生效
* 如果报错,则是token过期
* 前提是不能只能用上面的builder.setIssuedAt(now)方法和builder .setExpiration(new Date(nowMillis + 1))方法
* 不能覆盖
* 如果覆盖,则需要自己解析header里面的封装参数
* (这里过期时间我是放在header里面,如果用了自带的方法,则是在payload里面)
*/
System.out.println("token过期");//使用默认过期方法才有效
e.printStackTrace();
}
System.out.println("claims=="+claims);
System.out.println("parseJwt=="+parseJwt);
}
}
第一版 ↩︎