Spring Security 6.3基于JWT身份验证与授权开源项目


Java 后端应用程序使用 Spring-security 实现基于 JWT 的身份验证和授权,这是一个概念验证项目,利用 Spring-security 实现基于 JWT 的身份验证、API 访问控制、Token 撤销和泄露密码检测。

新发布的 Spring Security 6.3 版本添加了密码泄露检测功能,本项目提供的默认实现在底层使用了Have I Been Pwned API 。

向安全端点发出的任何请求都会被 JwtAuthenticationFilter.java 拦截,JwtAuthenticationFilter 会添加到安全过滤器链中,
并在 SecurityConfiguration.java 中进行配置。自定义过滤器负责验证传入访问令牌的真实性并填充安全上下文。

关键组件:

公共 API 声明
任何需要公开的 API 都可以用@PublicEndpoint进行注释。自定义安全过滤器不会评估对已配置 API 路径的请求,其逻辑由ApiEndpointSecurityInspector.java控制。

下面是一个声明为公共的示例控制器方法,它将免于身份验证检查:

@PublicEndpoint
@GetMapping(value = "/api/v1/something")
public ResponseEntity<Something> getSomething() {
    var something = someService.fetch();
    return ResponseEntity.ok(something);
}

Token生成和配置
应用程序使用访问令牌 (JWT) 和刷新令牌,身份验证成功后,这两个令牌都会返回给客户端。JWT 使用 RS512 非对称密钥对进行签名和验证,其中私钥(PKCS#8 格式)用于签名,相应的公钥用于在调用私有端点时进行验证,这些操作由JwtUtility.java处理。

刷新令牌是RefreshTokenGenerator生成的随机 256 位值,并由AuthenticationService根据用户标识符存储在缓存中。

可以在活动文件中配置令牌有效性/到期时间(以分钟为单位)和非对称密钥对.yml。配置的值填充在TokenConfigurationProperties中并由应用程序引用。以下是示例代码片段。

com:
  behl:
    cerberus:
      token:
        access-token:
          private-key: ${JWT_PRIVATE_KEY}
          public-key: ${JWT_PUBLIC_KEY}
          validity: 30
        refresh-token:
          validity: 120


API 访问控制
应用程序根据用户在系统中的当前状态实施访问控制。相应的权限嵌入到生成的 JWT 中,从而实现无状态的访问控制和授权过程。

泄露密码检测
为了保护用户帐户免遭在数据泄露中暴露的易受攻击的密码攻击,该项目使用了 中新增的泄露密码检测功能spring-security:6.3。提供的默认实现在底层使用了Have I Been Pwned API 。

泄露密码检查在两个关键场景中进行:

  • 用户创建:通过用户创建 API 注册新用户时,将检查提供的密码。
  • 用户登录:即使在创建用户时密码未被泄露,但密码也可能在之后被泄露。为了解决这个问题,登录 API 还加入了密码泄露检查。
如果在上述任何情况下检测到密码被泄露,服务器将响应以下错误:

{
  "Status": "422 UNPROCESSABLE_ENTITY",
 
"Description": "The provided password is compromised and cannot be used for account creation."
}

为了在登录期间恢复密码泄露的情况,/users/reset-password我们公开了一个新的 API 端点 PUT。此端点接受以下请求主体负载:

{
  "EmailId": "hardik@behl.com",
 
"CurrentPassword": "somethingCompromised",
 
"NewPassword": "somethingSecured"
}

在允许重置密码之前,还会检查新密码是否被破解。