• JWT基本概念和使用介绍


    JSON Web Token (JWT)是一个开放标准,它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。JWT主要有两种使用场景。

    • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
    • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

    JWT和银行存款的票据比较相似,在获取token后,后续服务器和客户端的交互都可以通过这个token完成。JWT分为三部分:Header(头部)、Payload(负载)、Signature(签名)。Header部分包括:alg和typ,alg是算法名称,typ统一都是JWT。Payload包括签发人、主题、过期时间等信息,具体如下所示。Signature是数字签名,防止数据被篡改。

    1. iss (issuer):签发人
    2. exp (expiration time):过期时间
    3. sub (subject):主题
    4. aud (audience):受众
    5. nbf (Not Before):生效时间
    6. iat (Issued At):签发时间
    7. jti (JWT ID):编号

    在使用JWT的过程中,需要注意JWT的以下这些特点:

    • JWT默认是不加密,但也是可以加密的,生成原始Token以后,可以用密钥再加密一次
    • JWT不加密的情况下,不能将敏感数据写入JWT。
    • JWT可以用于认证,也可以用于交换信息。有效使用JWT可以降低服务器查询数据库的次数。
    • JWT的最大缺点是,由于服务器不保存session状态,因此无法在使用过程中废止某个token,或者更改token的权限。也就是说,一旦JWT签发了,在到期之前就会始终有效。
    • JWT本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短
    • 对于一些比较重要的权限,使用时应该再次对用户进行认证,为了减少盗用,JWT不应该使用HTTP协议明码传输,要使用HTTPS协议传输

    上面介绍了JWT的一些概念信息,下面就通过实际例子来演示JWT的使用,Demo地址。Demo里面包含了jwt认证授权服务器和资源服务器两部分,资源服务器的代码和之前的例子相同,只是多了在application.properties中设置jwt签名的key,所以重点看看jwt认证授权服务器即可。内容主要包含两部分,jwt的配置,这个基本都是固定配置,具体内容如下所示:

    另外还有授权服务器部分的配置,和前面博客介绍的内容相同。通过这两项配置,生成的token就是jwt了,即包含了自认证的token。

    启动jwt服务和资源服务后,因为授权服务配置了password和authorization_code两种授权模式,这里就尝试password模式,可以看到返回的response body中包含token和refresh_token.解析返回的jwt,可以看到有header,payload和signature三部分。从这里也可以看到,自包含令牌即授权服务器颁发的令牌包含关于用户或者客户的元数据和声明(Claims),通过检查签名,期望的颁发着(issuer),期望的接收人aud(audience),或者scope,资源服务器可以在本地校验令牌。通常的实现为签名的JSON Web Token(JWT)。

    可以看到如果要支持生成JWT,实际很简单,只需要在授权认证服务器上进行一些jwt的固定配置即可。接下来看看一个完善的前后端分离的项目,如何通过jwt完成认证和授权,Demo地址。Demo启动成功后,可以通过swagger发送请求,先调用regiser接口,这样在数据库中就存入了user信息,存入数据库的用户密码信息已经进行了加密处理。

    接着调用登陆认证接口,获取token信息,可以看到,token信息用“.”号区分,将token进行解析,也可以看到token被解析为header,payload,signature三部分。解析出来的user信息也就是jwtUser。

    这样带着token访问其他接口,例如获取用户信息接口则能成功,否则会返回401的错误。上面展示了实际的效果,接下来看看代码上是如何实现的。与授权认证相关的有3个核心Class。SecurityConfiguration配置类继承了Spring Security的WebSecurityConfigurerAdapter类,这里设置了哪些请求可以不经过认证,哪些请求必须经过认证。将自定义的JwtAuthorizationFilter过滤器添加到Spring Security机制中。而JwtAuthorizationFilter是用户请求授权过滤器,用于从用户请求中获取token信息,并对其进行验证,同时加载与token相关联的用户身份认证信息,并添加到Spring Security上下文中。JwtConfigurer是固定写法。

     上一篇博客中,生产Jwt时借助OAuth2来生成的授权的,这里的Demo代码则是通过JWT的算法来生成token,具体代码如下所示,包括验证token等也都封装在JwtUtil类中。

    剩下的一些类如Repository、Entity主要是为了对数据库进行增删改查,因为user信息和user_role等信息是存入数据库的。另外,还有DTO(数据传输对象),Exception(异常类定义),Constant(一些常量定义),UserService和UserRoleService主要是调用Repository的一些方法对数据进行处理,AuthService调用其他类的方法组合了authLogin方法。最后是Configure里面的一些配置,除了配置Swagger外,还配置了springframework.data的审计功能。配置审计功能后,相关的数据库表就会自动添加上createBy,createDate等信息。以下是审计的配置类

    定义审计Entity,普通Entity继承AbstractAuditingEntity即可自动进行审计功能。

    总结而言,对于JWT的使用,最关键的还是生成Token,使用Token,使用Token就包括验证Token的正确性、存储Token,filter配置等。上面的例子中数据库表中只存放了username/password等信息,token是没有进行存放的,那如果要用redis来存放token应该如何实现呢?上面的demo代码中refresh-token分支上就有相关的实现,接下来看看如何通过redis来刷新token。首先在pom文件中引入redis的依赖,且在application.properties中添加redis相关配置。

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-data-redis</artifactId>
    4. <scope>provided</scope>
    5. </dependency>

    接着配置RedisTemplate,实现JwtRedisCacheService,主要包含getValue和setValue方法。

    在AuthService中,当生成jwt后,会先存入redis中,然后再写入安全上下文中。

    在JwtAutherizationFilter中判断token的有效性,如果token过期,那么会读取缓存的token进行对比,如果缓存中的token和过期token一致,那么会刷新token,并把刷新的token写入redis以及写入接口的responseHeader中。

     需要注意一点:这里刷新的token是直接调用JwtUtil重新生成的token,并不是借助refresh_token来获取新的token。

    可以看到如果要将token等信息存入redis中并不难,主要是调用set,get方法即可。以上就是对JWT使用的基本介绍。

  • 相关阅读:
    插件机工作台设计(Solidworks)
    基础算法(一)
    git 上拉下来的新项目web文件夹没有被idea管理,导致启动不了
    Go 以小端字节序修改文件
    DATA AI Summit 2022提及到的对 aggregate 的优化
    win11 已经配置好环境变量,但在命令行输入python/pip仍然出错
    redis 集群(cluster)
    详解StringBuilder和StringBuffer(区别,使用方法,含源码讲解)
    实战 target 选择器,解放生产力!
    OceanBase 数据库入门知识
  • 原文地址:https://blog.csdn.net/qiaotl/article/details/126472226