热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jwttoken长度_听说你的JWT库用起来特别扭,推荐这款贼好用的

以前一直使用的是jjwt这个JWT库,虽然小巧够用,但对JWT的一些细节封装的不是很好。最近发现了一个更好用的JWT库nimbus-jose-jwt,简

以前一直使用的是jjwt这个JWT库,虽然小巧够用, 但对JWT的一些细节封装的不是很好。最近发现了一个更好用的JWT库nimbus-jose-jwt,简单易用,API非常易于理解,对称加密和非对称加密算法都支持,推荐给大家!

简介

nimbus-jose-jwt是最受欢迎的JWT开源库,基于Apache 2.0开源协议,支持所有标准的签名(JWS)和加密(JWE)算法。

JWT概念关系

这里我们需要了解下JWT、JWS、JWE三者之间的关系,其实JWT(JSON Web Token)指的是一种规范,这种规范允许我们使用JWT在两个组织之间传递安全可靠的信息。而JWS(JSON Web Signature)和JWE(JSON Web Encryption)是JWT规范的两种不同实现,我们平时最常使用的实现就是JWS。

使用

接下来我们将介绍下nimbus-jose-jwt库的使用,主要使用对称加密(HMAC)和非对称加密(RSA)两种算法来生成和解析JWT令牌。

对称加密(HMAC)

对称加密指的是使用相同的秘钥来进行加密和解密,如果你的秘钥不想暴露给解密方,考虑使用非对称加密。

  • 要使用nimbus-jose-jwt库,首先在pom.xml添加相关依赖;

    com.nimbusds    nimbus-jose-jwt    8.16

  • 创建JwtTokenServiceImpl作为JWT处理的业务类,添加根据HMAC算法生成和解析JWT令牌的方法,可以发现nimbus-jose-jwt库操作JWT的API非常易于理解;

/** * Created by macro on 2020/6/22. */@Servicepublic class JwtTokenServiceImpl implements JwtTokenService {    @Override    public String generateTokenByHMAC(String payloadStr, String secret) throws JOSEException {        //创建JWS头,设置签名算法和类型        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256).                type(JOSEObjectType.JWT)                .build();        //将负载信息封装到Payload中        Payload payload = new Payload(payloadStr);        //创建JWS对象        JWSObject jwsObject = new JWSObject(jwsHeader, payload);        //创建HMAC签名器        JWSSigner jwsSigner = new MACSigner(secret);        //签名        jwsObject.sign(jwsSigner);        return jwsObject.serialize();    }    @Override    public PayloadDto verifyTokenByHMAC(String token, String secret) throws ParseException, JOSEException {        //从token中解析JWS对象        JWSObject jwsObject = JWSObject.parse(token);        //创建HMAC验证器        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() 

  • 创建PayloadDto实体类,用于封装JWT中存储的信息;

/** * Created by macro on 2020/6/22. */@Data@EqualsAndHashCode(callSuper = false)@Builderpublic class PayloadDto {    @ApiModelProperty("主题")    private String sub;    @ApiModelProperty("签发时间")    private Long iat;    @ApiModelProperty("过期时间")    private Long exp;    @ApiModelProperty("JWT的ID")    private String jti;    @ApiModelProperty("用户名称")    private String username;    @ApiModelProperty("用户拥有的权限")    private List authorities;}

  • 在JwtTokenServiceImpl类中添加获取默认的PayloadDto的方法,JWT过期时间设置为60s;

/** * Created by macro on 2020/6/22. */@Servicepublic class JwtTokenServiceImpl implements JwtTokenService {    @Override    public PayloadDto getDefaultPayloadDto() {        Date now = new Date();        Date exp = DateUtil.offsetSecond(now, 60*60);        return PayloadDto.builder()                .sub("macro")                .iat(now.getTime())                .exp(exp.getTime())                .jti(UUID.randomUUID().toString())                .username("macro")                .authorities(CollUtil.toList("ADMIN"))                .build();    }}

  • 创建JwtTokenController类,添加根据HMAC算法生成和解析JWT令牌的接口,由于HMAC算法需要长度至少为32个字节的密钥,所以我们使用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("使用对称加密(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("使用对称加密(HMAC)算法验证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();    }}

  • 调用使用HMAC算法生成JWT令牌的接口进行测试;
aae481fcb60bcc2ac8384c2ce2534016.png
  • 调用使用HMAC算法解析JWT令牌的接口进行测试。
4edca066d466b8dbe1f782602af2249a.png

非对称加密(RSA)

非对称加密指的是使用公钥和私钥来进行加密解密操作。对于加密操作,公钥负责加密,私钥负责解密,对于签名操作,私钥负责签名,公钥负责验证。非对称加密在JWT中的使用显然属于签名操作。

  • 如果我们需要使用固定的公钥和私钥来进行签名和验证的话,我们需要生成一个证书文件,这里将使用Java自带的keytool工具来生成jks证书文件,该工具在JDK的bin目录下;
5b0ac4f572f7928ed3a53abc3d3e05c7.png
  • 打开CMD命令界面,使用如下命令生成证书文件,设置别名为jwt,文件名为jwt.jks;

keytool -genkey -alias jwt -keyalg RSA -keystore jwt.jks

  • 输入密码为123456,然后输入各种信息之后就可以生成证书jwt.jks文件了;
a390e2e959a88eb2d5fc457d57865852.png
  • 将证书文件jwt.jks复制到项目的resource目录下,然后需要从证书文件中读取RSAKey,这里我们需要在pom.xml中添加一个Spring Security的RSA依赖;

    org.springframework.security    spring-security-rsa    1.0.7.RELEASE

  • 然后在JwtTokenServiceImpl类中添加方法,从类路径下读取证书文件并转换为RSAKey对象;

/** * Created by macro on 2020/6/22. */@Servicepublic class JwtTokenServiceImpl implements JwtTokenService {    @Override    public RSAKey getDefaultRSAKey() {        //从classpath下获取RSA秘钥对        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();    }}

  • 我们可以在JwtTokenController中添加一个接口,用于获取证书中的公钥;

/** * 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("获取非对称加密(RSA)算法公钥")    @RequestMapping(value = "/rsa/publicKey", method = RequestMethod.GET)    @ResponseBody    public Object getRSAPublicKey() {        RSAKey key = jwtTokenService.getDefaultRSAKey();        return new JWKSet(key).toJSONObject();    }}

  • 调用该接口,查看公钥信息,公钥是可以公开访问的;
7536cd4140bc55e859d0c6cc7b6019cb.png
  • 在JwtTokenServiceImpl中添加根据RSA算法生成和解析JWT令牌的方法,可以发现和上面的HMAC算法操作基本一致;

/** * Created by macro on 2020/6/22. */@Servicepublic class JwtTokenServiceImpl implements JwtTokenService {    @Override    public String generateTokenByRSA(String payloadStr, RSAKey rsaKey) throws JOSEException {        //创建JWS头,设置签名算法和类型        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256)                .type(JOSEObjectType.JWT)                .build();        //将负载信息封装到Payload中        Payload payload = new Payload(payloadStr);        //创建JWS对象        JWSObject jwsObject = new JWSObject(jwsHeader, payload);        //创建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对象        JWSObject jwsObject = JWSObject.parse(token);        RSAKey publicRsaKey = rsaKey.toPublicJWK();        //使用RSA公钥创建RSA验证器        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() 

  • 在JwtTokenController类,添加根据RSA算法生成和解析JWT令牌的接口,使用默认的RSA钥匙对;

/** * 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("使用非对称加密(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("使用非对称加密(RSA)算法验证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();    }}

  • 调用使用RSA算法生成JWT令牌的接口进行测试;
3247c276818cf01f9e5e71bf872ff8fd.png
  • 调用使用RSA算法解析JWT令牌的接口进行测试。
1b4fa900bc81e050b063852be146b577.png

参考资料

官方文档:https://connect2id.com/products/nimbus-jose-jwt

作 者:macrozheng

原文链接:https://mp.weixin.qq.com/s/Jo3PZoa7nL99c8UCxPiTTA




推荐阅读
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • 本文介绍了NetCore WebAPI开发的探索过程,包括新建项目、运行接口获取数据、跨平台部署等。同时还提供了客户端访问代码示例,包括Post函数、服务器post地址、api参数等。详细讲解了部署模式选择、框架依赖和独立部署的区别,以及在Windows和Linux平台上的部署方法。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Win10 64位旗舰版的优势及特点详解
    本文详细介绍了Win10 64位旗舰版的优势及特点,包括更安全的源安装盘、永久激活方式、稳定性和硬件驱动的集成,以及人性化的维护工具和分区功能。通过阅读本文,您将了解到Win10 64位旗舰版相比其他版本的优势和特点。 ... [详细]
  • 本文介绍了响应式页面的概念和实现方式,包括针对不同终端制作特定页面和制作一个页面适应不同终端的显示。分析了两种实现方式的优缺点,提出了选择方案的建议。同时,对于响应式页面的需求和背景进行了讨论,解释了为什么需要响应式页面。 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 本文分享了一位Android开发者多年来对于Android开发所需掌握的技能的笔记,包括架构师基础、高级UI开源框架、Android Framework开发、性能优化、音视频精编源码解析、Flutter学习进阶、微信小程序开发以及百大框架源码解读等方面的知识。文章强调了技术栈和布局的重要性,鼓励开发者做好学习规划和技术布局,以提升自己的竞争力和市场价值。 ... [详细]
author-avatar
神的禁卫军
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有