内容提要
本文介绍如何使用OAuth2和JWT保护Spring Boot API。OAuth2负责授权委托,JWT作为紧凑、可验证的令牌格式。文章涵盖认证与授权的区别、JWT结构、OAuth2角色,并通过Kotlin示例展示配置资源服务器、自定义JWT转换器、实施角色和权限验证,以及强化令牌信任(如验证发行者和受众),强调分层安全设计。
延伸解读
OAuth2与JWT的分工
OAuth2和JWT是互补技术,而非竞争关系。OAuth2负责授权委托,允许用户授权应用访问资源,而无需共享密码。JWT则作为紧凑、可验证的令牌格式,携带身份和权限信息。在Spring Boot中,资源服务器只需验证令牌签名、过期时间和作用域,而无需管理用户登录或会话,从而简化API安全实现。
自定义JWT转换器与角色映射
Spring Security默认仅映射OAuth2标准化的作用域(scope)为权限,而角色(roles)通常以自定义声明形式存在,需要自定义JwtAuthenticationConverter来提取并映射为Spring的权限。文章示例展示了如何同时映射作用域、角色和权限,并利用Kotlin的空安全特性避免空指针异常,确保类型安全。
强化令牌信任:发行者与受众验证
仅依赖签名验证不足以防止令牌重放或跨应用使用。通过自定义JwtDecoder,可以强制验证JWT的发行者(iss)和受众(aud)声明,确保令牌来自可信的授权服务器且专用于当前API。这增加了纵深防御,使安全策略在代码中明确可见,避免接受来自其他应用或环境的令牌。
分层授权与避免硬编码
简单作用域检查无法满足复杂系统的细粒度访问控制。文章建议结合作用域、角色和资源检查,实现分层授权。同时强调避免在控制器方法中硬编码安全决策,而应使用@PreAuthorize等注解在方法级别声明权限,以保持关注点分离,降低因业务变更导致的安全漏洞风险。
Q&A
OAuth2和JWT在API安全中分别扮演什么角色?
OAuth2负责授权委托,允许用户授权第三方应用访问其资源,而无需共享密码。JWT则作为一种紧凑、可验证的令牌格式,用于安全地传输身份和权限信息。两者互补,OAuth2管理授权流程,JWT作为访问令牌的载体。
JWT由哪几部分组成?每部分的作用是什么?
JWT由三部分组成:Header(头部)、Payload(载荷)和Signature(签名)。Header包含令牌类型和签名算法;Payload包含声明(如用户ID、角色、过期时间);Signature由Header、Payload和密钥生成,用于验证令牌未被篡改。
OAuth2中的四个角色分别是什么?
OAuth2的四个角色是:资源所有者(Resource Owner)、客户端(Client)、授权服务器(Authorization Server)和资源服务器(Resource Server)。资源所有者授权客户端访问其资源,客户端向授权服务器请求令牌,授权服务器颁发令牌,资源服务器验证令牌并提供资源。
在Spring Boot中配置OAuth2资源服务器时,如何设置JWT验证?
在Spring Boot中,可以通过在application.properties中配置`spring.security.oauth2.resourceserver.jwt.public-key-location`指定公钥位置,然后在安全配置类中启用`oauth2ResourceServer`并配置JWT解码器。默认情况下,Spring会验证签名和过期时间,但也可以自定义JwtDecoder来验证发行者和受众。
为什么需要自定义JwtAuthenticationConverter?
Spring Security默认只将JWT中的scope映射为权限,但角色和权限通常以自定义声明形式存在。自定义JwtAuthenticationConverter可以显式地从JWT中提取scope、roles和permissions,并将它们转换为Spring Security的GrantedAuthority,以便在方法安全中使用hasRole和hasAuthority进行细粒度授权。
如何验证JWT的发行者和受众?
可以通过自定义JwtDecoder来实现。首先使用JwtDecoders.fromIssuerLocation创建解码器,然后创建AudienceValidator和默认的IssuerValidator,将它们组合成DelegatingOAuth2TokenValidator,并设置到解码器上。这样,只有发行者和受众匹配的令牌才会被接受。
在Spring Security中,如何使用@PreAuthorize进行方法级授权?
在配置类上添加@EnableMethodSecurity(prePostEnabled = true),然后在控制器方法上使用@PreAuthorize注解,例如`@PreAuthorize("hasRole('admin') and hasAuthority('documents:read:all')")`。这样可以基于角色和权限进行细粒度的访问控制,避免在业务逻辑中硬编码安全决策。
使用JWT进行API安全有哪些注意事项?
注意事项包括:使用短期令牌并制定刷新和撤销策略;避免将JWT用于会话存储,以免令牌膨胀和撤销困难;始终验证签名、过期时间、发行者和受众;将安全决策集中在专用层,避免硬编码。