forked from fanruan/finekit
richie
5 years ago
2 changed files with 290 additions and 0 deletions
@ -0,0 +1,213 @@ |
|||||||
|
package com.fanruan.api.security; |
||||||
|
|
||||||
|
import com.fanruan.api.log.LogKit; |
||||||
|
import com.fr.cert.token.JwtBuilder; |
||||||
|
import com.fr.cert.token.Jwts; |
||||||
|
import com.fr.cert.token.SignatureAlgorithm; |
||||||
|
import com.fr.cert.token.SignatureException; |
||||||
|
import com.fr.cert.token.impl.DefaultClaims; |
||||||
|
import com.fr.security.KeySecretSeedConfig; |
||||||
|
import com.fr.security.SecurityToolbox; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.security.Key; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-09-03 |
||||||
|
* 用于生成和解析基于JWT的token |
||||||
|
*/ |
||||||
|
public class JwtKit { |
||||||
|
|
||||||
|
private static final String JWT_ID = "jwt"; |
||||||
|
private static final String ISSUER = "fanruan"; |
||||||
|
private static final SignatureAlgorithm DEFAULT_ALGORITHM = SignatureAlgorithm.HS256; |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建完整token |
||||||
|
* |
||||||
|
* @param issuer jwt签发者 |
||||||
|
* @param subject jwt所面向的用户 |
||||||
|
* @param audience 接收jwt的一方 |
||||||
|
* @param expiration jwt的过期时间,这个过期时间必须要大于签发时间 |
||||||
|
* @param notBeforeTime 定义在什么时间之前,该jwt都是不可用的. |
||||||
|
* @param issuerAtTime jwt的签发时间 |
||||||
|
* @param jwtId jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 |
||||||
|
* @param algorithm 加密算法 |
||||||
|
* @param secretKey 秘钥 |
||||||
|
* @return 签名后的token |
||||||
|
*/ |
||||||
|
public static String createJWT(String issuer, String subject, String audience, Date expiration, Date notBeforeTime, |
||||||
|
Date issuerAtTime, String jwtId, SignatureAlgorithm algorithm, Key secretKey) { |
||||||
|
JwtBuilder builder = Jwts.builder() |
||||||
|
.setIssuer(issuer) |
||||||
|
.setSubject(subject) |
||||||
|
.setAudience(audience) |
||||||
|
.setExpiration(expiration) |
||||||
|
.setNotBefore(notBeforeTime) |
||||||
|
.setIssuedAt(issuerAtTime) |
||||||
|
.setId(jwtId) |
||||||
|
.signWith(algorithm, secretKey); |
||||||
|
return builder.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建默认的token,用不超时 |
||||||
|
* |
||||||
|
* @param subject 主题 |
||||||
|
* @return token |
||||||
|
*/ |
||||||
|
public static String createDefaultJWT(String subject) { |
||||||
|
JwtBuilder builder = Jwts.builder() |
||||||
|
.setIssuer(ISSUER) |
||||||
|
.setIssuedAt(new Date()) |
||||||
|
.setSubject(subject) |
||||||
|
.setId(JWT_ID) |
||||||
|
.signWith(DEFAULT_ALGORITHM, getKeyBytes()); |
||||||
|
return builder.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建默认的token |
||||||
|
* |
||||||
|
* @param subject 主题 |
||||||
|
* @param description 描述 |
||||||
|
* @return token |
||||||
|
*/ |
||||||
|
public static String createDefaultJWT(String subject, String description) { |
||||||
|
JwtBuilder builder = Jwts.builder() |
||||||
|
.setIssuer(ISSUER) |
||||||
|
.setIssuedAt(new Date()) |
||||||
|
.setSubject(subject) |
||||||
|
.setDescription(description) |
||||||
|
.setId(JWT_ID) |
||||||
|
.signWith(DEFAULT_ALGORITHM, getKeyBytes()); |
||||||
|
return builder.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建默认token |
||||||
|
* |
||||||
|
* @param subject 主题 |
||||||
|
* @param description 描述 |
||||||
|
* @param timeout 超时时长 |
||||||
|
* @return token |
||||||
|
*/ |
||||||
|
public static String createDefaultJWT(String subject, String description, long timeout) { |
||||||
|
Date currentTime = new Date(); |
||||||
|
Date timeoutTime = new Date(currentTime.getTime() + timeout); |
||||||
|
JwtBuilder builder = Jwts.builder() |
||||||
|
.setIssuer(ISSUER) |
||||||
|
.setIssuedAt(currentTime) |
||||||
|
.setExpiration(timeoutTime) |
||||||
|
.setSubject(subject) |
||||||
|
.setDescription(description) |
||||||
|
.setId(JWT_ID) |
||||||
|
.signWith(DEFAULT_ALGORITHM, getKeyBytes()); |
||||||
|
return builder.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建有超时时限的token |
||||||
|
* |
||||||
|
* @param subject 主题 |
||||||
|
* @param timeout 超时时长 |
||||||
|
* @return token |
||||||
|
*/ |
||||||
|
public static String createDefaultJWT(String subject, long timeout) { |
||||||
|
Date currentTime = new Date(); |
||||||
|
Date timeoutTime = new Date(currentTime.getTime() + timeout); |
||||||
|
JwtBuilder builder = Jwts.builder() |
||||||
|
.setIssuer(ISSUER) |
||||||
|
.setIssuedAt(currentTime) |
||||||
|
.setExpiration(timeoutTime) |
||||||
|
.setSubject(subject) |
||||||
|
.setId(JWT_ID) |
||||||
|
.signWith(DEFAULT_ALGORITHM, getKeyBytes()); |
||||||
|
return builder.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解析token |
||||||
|
* |
||||||
|
* @param jwt token字符串 |
||||||
|
* @return 签名通过之后的结果集 |
||||||
|
* @throws SignatureException 签名错误异常 |
||||||
|
*/ |
||||||
|
public static Map<String, Object> parseJWT(String jwt) { |
||||||
|
try { |
||||||
|
return Jwts.parser().setSigningKey(getKeyBytes()).parseClaimsJws(jwt).getBody(); |
||||||
|
} catch (SignatureException e) { |
||||||
|
LogKit.warn(e.getMessage()); |
||||||
|
} |
||||||
|
return new DefaultClaims(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 根据自定义的key,创建token |
||||||
|
* |
||||||
|
* @param signatureKey 秘钥 |
||||||
|
* @param claims token携带的信息 |
||||||
|
* @return token |
||||||
|
*/ |
||||||
|
public static String createVariedJWT(String signatureKey, Map<String, Object> claims) { |
||||||
|
JwtBuilder builder = Jwts.builder() |
||||||
|
.setIssuer(ISSUER) |
||||||
|
.setIssuedAt(new Date()) |
||||||
|
.setClaims(claims) |
||||||
|
.setId(JWT_ID) |
||||||
|
.signWith(DEFAULT_ALGORITHM, signatureKey); |
||||||
|
return builder.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据自定义的key,创建token |
||||||
|
* |
||||||
|
* @param signatureKey 秘钥 |
||||||
|
* @param timeout 有效期 |
||||||
|
* @param claims token携带的信息 |
||||||
|
* @return token |
||||||
|
*/ |
||||||
|
public static String createVariedJWT(String signatureKey, long timeout, Map<String, Object> claims) { |
||||||
|
Date currentTime = new Date(); |
||||||
|
Date timeoutTime = new Date(currentTime.getTime() + timeout); |
||||||
|
JwtBuilder builder = Jwts.builder() |
||||||
|
.setIssuer(ISSUER) |
||||||
|
.setIssuedAt(new Date()) |
||||||
|
.setExpiration(timeoutTime) |
||||||
|
.setClaims(claims) |
||||||
|
.setId(JWT_ID) |
||||||
|
.signWith(DEFAULT_ALGORITHM, signatureKey); |
||||||
|
return builder.compact(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解析token, 先对key进行utf-8解码 |
||||||
|
* |
||||||
|
* @param jwt token字符串 |
||||||
|
* @param signatureKey 秘钥 |
||||||
|
* @return 签名通过之后的结果集 |
||||||
|
* @throws SignatureException 签名错误异常 |
||||||
|
*/ |
||||||
|
public static Map<String, Object> parseJWT(String jwt, String signatureKey) { |
||||||
|
try { |
||||||
|
return Jwts.parser().setSigningKey(signatureKey).parseClaimsJws(jwt).getBody(); |
||||||
|
} catch (SignatureException e) { |
||||||
|
LogKit.warn(e.getMessage()); |
||||||
|
} |
||||||
|
return new DefaultClaims(); |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] getKeyBytes() { |
||||||
|
try { |
||||||
|
return SecurityToolbox.base642Byte(KeySecretSeedConfig.getInstance().getTrustSeed()); |
||||||
|
} catch (IOException e) { |
||||||
|
LogKit.error("key secret seed base64 decode error"); |
||||||
|
} |
||||||
|
return new byte[0]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
package com.fanruan.api.security; |
||||||
|
|
||||||
|
import com.fanruan.api.Prepare; |
||||||
|
import com.fr.cert.token.JwtBuilder; |
||||||
|
import com.fr.cert.token.Jwts; |
||||||
|
import com.fr.cert.token.SignatureAlgorithm; |
||||||
|
import com.fr.security.SecurityToolbox; |
||||||
|
import com.fr.stable.CodeUtils; |
||||||
|
import org.junit.Assert; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
import java.util.Calendar; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019-09-03 |
||||||
|
*/ |
||||||
|
public class JwtKitTest extends Prepare { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCreateDefaultJWT() throws Exception { |
||||||
|
String tokenCN = JwtKit.createDefaultJWT("你好,我是中国人"); |
||||||
|
Map<String, Object> claims = JwtKit.parseJWT(tokenCN); |
||||||
|
Assert.assertEquals("你好,我是中国人", CodeUtils.cjkDecode(String.valueOf(claims.get("sub")))); |
||||||
|
|
||||||
|
String tokenEN = JwtKit.createDefaultJWT("Hello, world"); |
||||||
|
Map<String, Object> claims2 = JwtKit.parseJWT(tokenEN); |
||||||
|
Assert.assertEquals("Hello, world", CodeUtils.cjkDecode(String.valueOf(claims2.get("sub")))); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testClaims() throws Exception { |
||||||
|
String token = JwtKit.createDefaultJWT("千万", "我是千万的爹"); |
||||||
|
Map<String, Object> claims2 = JwtKit.parseJWT(token); |
||||||
|
String text = CodeUtils.cjkDecode(String.valueOf(claims2.get("description"))); |
||||||
|
Assert.assertEquals("我是千万的爹", text); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test1() throws UnsupportedEncodingException { |
||||||
|
String token = createToken().compact(); |
||||||
|
Assert.assertEquals(Jwts.parser().setSigningKey("abc=啊").parseClaimsJws(token).getBody().getSubject(), "hello.cpt"); |
||||||
|
|
||||||
|
token = createToken().signWithBase64SecretKey(SignatureAlgorithm.HS256, SecurityToolbox.byte2Base64("abc=啊".getBytes())).compact(); |
||||||
|
Assert.assertEquals(Jwts.parser() |
||||||
|
.setBase64SigningKey(SecurityToolbox.byte2Base64("abc=啊".getBytes())) |
||||||
|
.parseClaimsJws(token).getBody().getSubject(), "hello.cpt"); |
||||||
|
|
||||||
|
token = createToken() |
||||||
|
.signWith(SignatureAlgorithm.HS256, "abc=啊".getBytes("GBK")) |
||||||
|
.compact(); |
||||||
|
Assert.assertEquals("hello.cpt", Jwts.parser().setSigningKey("abc=啊".getBytes("GBK")).parseClaimsJws(token).getBody().getSubject()); |
||||||
|
} |
||||||
|
|
||||||
|
private JwtBuilder createToken() { |
||||||
|
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; |
||||||
|
|
||||||
|
Calendar calendar = Calendar.getInstance(); |
||||||
|
calendar.set(2019, Calendar.FEBRUARY, 20, 18, 0, 0); |
||||||
|
Date currentTime = calendar.getTime(); |
||||||
|
calendar.set(2029, Calendar.FEBRUARY, 20, 18, 0, 0); |
||||||
|
Date expirationTime = calendar.getTime(); |
||||||
|
return Jwts.builder() |
||||||
|
.setHeaderParam("typ", "JWT") |
||||||
|
.setIssuer("fanruan") |
||||||
|
.setSubject("hello.cpt") |
||||||
|
.setExpiration(expirationTime) |
||||||
|
.setIssuedAt(currentTime) |
||||||
|
.setId("01") |
||||||
|
.signWith(signatureAlgorithm, "abc=啊"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue