溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

項(xiàng)目關(guān)于開(kāi)源JWT庫(kù)使用的示例分析

發(fā)布時(shí)間:2021-09-18 10:10:49 來(lái)源:億速云 閱讀:114 作者:柒染 欄目:編程語(yǔ)言

項(xiàng)目關(guān)于開(kāi)源JWT庫(kù)使用的示例分析,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

以前一直使用的是jjwt這個(gè)JWT庫(kù),雖然小巧夠用, 但對(duì)JWT的一些細(xì)節(jié)封裝的不是很好。最近發(fā)現(xiàn)了一個(gè)更好用的JWT庫(kù)nimbus-jose-jwt,簡(jiǎn)單易用,API非常易于理解,對(duì)稱加密和非對(duì)稱加密算法都支持。

簡(jiǎn)介 nimbus-jose-jwt是最受歡迎的JWT開(kāi)源庫(kù),基于Apache 2.0開(kāi)源協(xié)議,支持所有標(biāo)準(zhǔn)的簽名(JWS)和加密(JWE)算法。

JWT概念關(guān)系 這里我們需要了解下JWT、JWS、JWE三者之間的關(guān)系,其實(shí)JWT(JSON Web Token)指的是一種規(guī)范,這種規(guī)范允許我們使用JWT在兩個(gè)組織之間傳遞安全可靠的信息。而JWS(JSON Web Signature)和JWE(JSON Web Encryption)是JWT規(guī)范的兩種不同實(shí)現(xiàn),我們平時(shí)最常使用的實(shí)現(xiàn)就是JWS。

接下來(lái)介紹下nimbus-jose-jwt庫(kù)的使用,主要使用對(duì)稱加密(HMAC)和非對(duì)稱加密(RSA)兩種算法來(lái)生成和解析JWT令牌。

對(duì)稱加密(HMAC) 對(duì)稱加密指的是使用相同的秘鑰來(lái)進(jìn)行加密和解密,如果你的秘鑰不想暴露給解密方,考慮使用非對(duì)稱加密。

要使用nimbus-jose-jwt庫(kù),首先在pom.xml添加相關(guān)依賴;

<!--JWT解析庫(kù)-->

<dependency> <groupId>com.nimbusds</groupId> <artifactId>nimbus-jose-jwt</artifactId> <version>8.16</version> </dependency> Copy to clipboardErrorCopied 創(chuàng)建JwtTokenServiceImpl作為JWT處理的業(yè)務(wù)類,添加根據(jù)HMAC算法生成和解析JWT令牌的方法,可以發(fā)現(xiàn)nimbus-jose-jwt庫(kù)操作JWT的API非常易于理解; /** * Created by macro on 2020/6/22. */ [@Service](https://my.oschina.net/service) public class JwtTokenServiceImpl implements JwtTokenService { [@Override](https://my.oschina.net/u/1162528) public String generateTokenByHMAC(String payloadStr, String secret) throws JOSEException { //創(chuàng)建JWS頭,設(shè)置簽名算法和類型 JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256). type(JOSEObjectType.JWT) .build(); //將負(fù)載信息封裝到Payload中 Payload payload = new Payload(payloadStr); //創(chuàng)建JWS對(duì)象 JWSObject jwsObject = new JWSObject(jwsHeader, payload); //創(chuàng)建HMAC簽名器 JWSSigner jwsSigner = new MACSigner(secret); //簽名 jwsObject.sign(jwsSigner); return jwsObject.serialize(); }

[@Override](https://my.oschina.net/u/1162528)
public PayloadDto verifyTokenByHMAC(String token, String secret) throws ParseException, JOSEException {
    //從token中解析JWS對(duì)象
    JWSObject jwsObject = JWSObject.parse(token);
    //創(chuàng)建HMAC驗(yàn)證器
    JWSVerifier jwsVerifier = new MACVerifier(secret);
    if (!jwsObject.verify(jwsVerifier)) {
        throw new JwtInvalidException("token簽名不合法!");
    }
    String payload = jwsObject.getPayload().toString();
    PayloadDto payloadDto = JSONUtil.toBean(payload, PayloadDto.class);
    if (payloadDto.getExp() < new Date().getTime()) {
        throw new JwtExpiredException("token已過(guò)期!");
    }
    return payloadDto;
}

} Copy to clipboardErrorCopied 創(chuàng)建PayloadDto實(shí)體類,用于封裝JWT中存儲(chǔ)的信息; /**

  • Created by macro on 2020/6/22. / @Data @EqualsAndHashCode(callSuper = false) @Builder public class PayloadDto { @ApiModelProperty("主題") private String sub; @ApiModelProperty("簽發(fā)時(shí)間") private Long iat; @ApiModelProperty("過(guò)期時(shí)間") private Long exp; @ApiModelProperty("JWT的ID") private String jti; @ApiModelProperty("用戶名稱") private String username; @ApiModelProperty("用戶擁有的權(quán)限") private List<String> authorities; } Copy to clipboardErrorCopied 在JwtTokenServiceImpl類中添加獲取默認(rèn)的PayloadDto的方法,JWT過(guò)期時(shí)間設(shè)置為60min; /*

  • Created by macro on 2020/6/22. / @Service public class JwtTokenServiceImpl implements JwtTokenService { @Override public PayloadDto getDefaultPayloadDto() { Date now = new Date(); Date exp = DateUtil.offsetSecond(now, 6060); return PayloadDto.builder() .sub("macro") .iat(now.getTime()) .exp(exp.getTime()) .jti(UUID.randomUUID().toString()) .username("macro") .authorities(CollUtil.toList("ADMIN")) .build(); } } Copy to clipboardErrorCopied 創(chuàng)建JwtTokenController類,添加根據(jù)HMAC算法生成和解析JWT令牌的接口,由于HMAC算法需要長(zhǎng)度至少為32個(gè)字節(jié)的秘鑰,所以我們使用MD5加密下; /**

  • JWT令牌管理Controller

  • Created by macro on 2020/6/22. */ @Api(tags = "JwtTokenController", description = "JWT令牌管理") @Controller @RequestMapping("/token") public class JwtTokenController {

    @Autowired private JwtTokenService jwtTokenService;

    @ApiOperation("使用對(duì)稱加密(HMAC)算法生成token") @RequestMapping(value = "/hmac/generate", method = RequestMethod.GET) @ResponseBody public CommonResult generateTokenByHMAC() { try { PayloadDto payloadDto = jwtTokenService.getDefaultPayloadDto(); String token = jwtTokenService.generateTokenByHMAC(JSONUtil.toJsonStr(payloadDto), SecureUtil.md5("test")); return CommonResult.success(token); } catch (JOSEException e) { e.printStackTrace(); } return CommonResult.failed(); }

    @ApiOperation("使用對(duì)稱加密(HMAC)算法驗(yàn)證token") @RequestMapping(value = "/hmac/verify", method = RequestMethod.GET) @ResponseBody public CommonResult verifyTokenByHMAC(String token) { try { PayloadDto payloadDto = jwtTokenService.verifyTokenByHMAC(token, SecureUtil.md5("test")); return CommonResult.success(payloadDto); } catch (ParseException | JOSEException e) { e.printStackTrace(); } return CommonResult.failed();

    } } Copy to clipboardErrorCopied 調(diào)用使用HMAC算法生成JWT令牌的接口進(jìn)行測(cè)試;

調(diào)用使用HMAC算法解析JWT令牌的接口進(jìn)行測(cè)試。

非對(duì)稱加密(RSA) 非對(duì)稱加密指的是使用公鑰和私鑰來(lái)進(jìn)行加密解密操作。對(duì)于加密操作,公鑰負(fù)責(zé)加密,私鑰負(fù)責(zé)解密,對(duì)于簽名操作,私鑰負(fù)責(zé)簽名,公鑰負(fù)責(zé)驗(yàn)證。非對(duì)稱加密在JWT中的使用顯然屬于簽名操作。

如果我們需要使用固定的公鑰和私鑰來(lái)進(jìn)行簽名和驗(yàn)證的話,我們需要生成一個(gè)證書(shū)文件,這里將使用Java自帶的keytool工具來(lái)生成jks證書(shū)文件,該工具在JDK的bin目錄下;

打開(kāi)CMD命令界面,使用如下命令生成證書(shū)文件,設(shè)置別名為jwt,文件名為jwt.jks; keytool -genkey -alias jwt -keyalg RSA -keystore jwt.jks Copy to clipboardErrorCopied 輸入密碼為123456,然后輸入各種信息之后就可以生成證書(shū)jwt.jks文件了;

將證書(shū)文件jwt.jks復(fù)制到項(xiàng)目的resource目錄下,然后需要從證書(shū)文件中讀取RSAKey,這里我們需要在pom.xml中添加一個(gè)Spring Security的RSA依賴;

<!--Spring Security RSA工具類-->

<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-rsa</artifactId> <version>1.0.7.RELEASE</version> </dependency> Copy to clipboardErrorCopied 然后在JwtTokenServiceImpl類中添加方法,從類路徑下讀取證書(shū)文件并轉(zhuǎn)換為RSAKey對(duì)象; /** * Created by macro on 2020/6/22. */ @Service public class JwtTokenServiceImpl implements JwtTokenService { @Override public RSAKey getDefaultRSAKey() { //從classpath下獲取RSA秘鑰對(duì) KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); KeyPair keyPair = keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray()); //獲取RSA公鑰 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); //獲取RSA私鑰 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); return new RSAKey.Builder(publicKey).privateKey(privateKey).build(); } } Copy to clipboardErrorCopied 我們可以在JwtTokenController中添加一個(gè)接口,用于獲取證書(shū)中的公鑰; /** * JWT令牌管理Controller * Created by macro on 2020/6/22. */ @Api(tags = "JwtTokenController", description = "JWT令牌管理") @Controller @RequestMapping("/token") public class JwtTokenController {

@Autowired
private JwtTokenService jwtTokenService;

@ApiOperation("獲取非對(duì)稱加密(RSA)算法公鑰")
@RequestMapping(value = "/rsa/publicKey", method = RequestMethod.GET)
@ResponseBody
public Object getRSAPublicKey() {
    RSAKey key = jwtTokenService.getDefaultRSAKey();
    return new JWKSet(key).toJSONObject();
}

} Copy to clipboardErrorCopied 調(diào)用該接口,查看公鑰信息,公鑰是可以公開(kāi)訪問(wèn)的;

在JwtTokenServiceImpl中添加根據(jù)RSA算法生成和解析JWT令牌的方法,可以發(fā)現(xiàn)和上面的HMAC算法操作基本一致; /**

  • Created by macro on 2020/6/22. */ @Service public class JwtTokenServiceImpl implements JwtTokenService { @Override public String generateTokenByRSA(String payloadStr, RSAKey rsaKey) throws JOSEException { //創(chuàng)建JWS頭,設(shè)置簽名算法和類型 JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256) .type(JOSEObjectType.JWT) .build(); //將負(fù)載信息封裝到Payload中 Payload payload = new Payload(payloadStr); //創(chuàng)建JWS對(duì)象 JWSObject jwsObject = new JWSObject(jwsHeader, payload); //創(chuàng)建RSA簽名器 JWSSigner jwsSigner = new RSASSASigner(rsaKey, true); //簽名 jwsObject.sign(jwsSigner); return jwsObject.serialize(); }

    @Override public PayloadDto verifyTokenByRSA(String token, RSAKey rsaKey) throws ParseException, JOSEException { //從token中解析JWS對(duì)象 JWSObject jwsObject = JWSObject.parse(token); RSAKey publicRsaKey = rsaKey.toPublicJWK(); //使用RSA公鑰創(chuàng)建RSA驗(yàn)證器 JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey); if (!jwsObject.verify(jwsVerifier)) { throw new JwtInvalidException("token簽名不合法!"); } String payload = jwsObject.getPayload().toString(); PayloadDto payloadDto = JSONUtil.toBean(payload, PayloadDto.class); if (payloadDto.getExp() < new Date().getTime()) { throw new JwtExpiredException("token已過(guò)期!"); } return payloadDto; } } Copy to clipboardErrorCopied 在JwtTokenController類,添加根據(jù)RSA算法生成和解析JWT令牌的接口,使用默認(rèn)的RSA鑰匙對(duì); /**

  • JWT令牌管理Controller

  • Created by macro on 2020/6/22. */ @Api(tags = "JwtTokenController", description = "JWT令牌管理") @Controller @RequestMapping("/token") public class JwtTokenController {

    @Autowired private JwtTokenService jwtTokenService;

    @ApiOperation("使用非對(duì)稱加密(RSA)算法生成token") @RequestMapping(value = "/rsa/generate", method = RequestMethod.GET) @ResponseBody public CommonResult generateTokenByRSA() { try { PayloadDto payloadDto = jwtTokenService.getDefaultPayloadDto(); String token = jwtTokenService.generateTokenByRSA(JSONUtil.toJsonStr(payloadDto),jwtTokenService.getDefaultRSAKey()); return CommonResult.success(token); } catch (JOSEException e) { e.printStackTrace(); } return CommonResult.failed(); }

    @ApiOperation("使用非對(duì)稱加密(RSA)算法驗(yàn)證token") @RequestMapping(value = "/rsa/verify", method = RequestMethod.GET) @ResponseBody public CommonResult verifyTokenByRSA(String token) { try { PayloadDto payloadDto = jwtTokenService.verifyTokenByRSA(token, jwtTokenService.getDefaultRSAKey()); return CommonResult.success(payloadDto); } catch (ParseException | JOSEException e) { e.printStackTrace(); } return CommonResult.failed(); } } Copy to clipboardErrorCopied 調(diào)用使用RSA算法生成JWT令牌的接口進(jìn)行測(cè)試;

調(diào)用使用RSA算法解析JWT令牌的接口進(jìn)行測(cè)試。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

jwt
AI