为Spring Boot REST API配置AAD与AWS Cognito多认证授权
我现有一个Spring Boot REST API,当前配置包含两类路由:
- 未授权路由
- 通过AAD/Entra的Bearer令牌授权的路由
当前的configure方法配置如下:
@Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.csrf().disable(); http.authorizeRequests(requests -> requests .antMatchers(HttpMethod.OPTIONS, "/**/**").permitAll() .antMatchers("/api/protected/**").fullyAuthenticated() .anyRequest().permitAll() ); }
该配置类继承自AadResourceServerWebSecurityConfigurerAdapter。
通过此配置,我们可以像这样保护路由:
@PreAuthorize("hasAuthority('APPROLE_AppName.RoleName')") @GetMapping(value = "/some-method", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<List<String>> getStrings() { return ResponseEntity.ok(...); }
现在API需要扩展,允许由AWS Cognito管理的新用户访问授权端点。请问如何配置WebSecurityConfigurerAdapter,使某些路径无需授权、部分通过AAD保护、部分通过AWS Cognito保护?
我遇到的主要问题是AadResourceServerWebSecurityConfigurerAdapter配置的JWT验证仅适用于微软颁发的Bearer令牌。
理想情况下,我希望实现如下配置:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Configuration @Order(1) public static class AzureAdSecurityConfig extends AadResourceServerWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests(requests -> requests .antMatchers("/api/aad/**").fullyAuthenticated() ); http.oauth2ResourceServer().jwt([使用AAD JWT验证]); } } @Configuration @Order(2) public static class AwsCognitoSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests(requests -> requests .antMatchers("/api/cognito/**").fullyAuthenticated() ); http.oauth2ResourceServer().jwt([使用AWS Cognito JWT验证]); } } @Configuration @Order(3) public static class DefaultSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.authorizeRequests(requests -> requests .antMatchers(HttpMethod.OPTIONS, "/**/**").permitAll() .anyRequest().permitAll() ); } } }
另一个问题是AadResourceServerWebSecurityConfigurerAdapter会自动为JwtClaimNames的"roles"和"scp"设置前缀"SCOPE_"和"APPROLE_"。我希望为AAD和AWS Cognito设置不同的前缀,比如"AAD_SCOPE_"、"AAD_APPROLE_"和"COGNITO_GROUP_"。
我找到一些关于Spring Boot实现多租户JWT验证的资料,但它们都仅基于SQL数据库实现密码/用户认证。
有没有办法无需重新实现所有AAD逻辑,就能加入AWS Cognito颁发的JWT验证,或者能否基于路由进行区分?我知道可以通过HttpSecurity的oauth2ResourceServer()函数配置JWT使用,但仅找到单租户的实现信息。
如果有人已成功实现此类或类似案例,或能提供方向指引,我将不胜感激。或者我的思路完全错误,也请告知。
感谢帮助,我已实现功能,具体如下:
application.yml
com: c4-soft: springaddons: oidc: ops: - iss: https://cognito-idp.<region>.amazonaws.com/<cognito-pool> authorities: - path: $.cognito:groups prefix: COGNITO_GROUP_ - iss: https://sts.windows.net/<entra objectid>/ authorities: - path: $.roles.* prefix: AAD_APPROLE_ - path: $.scp prefix: AAD_SCOPE_ aud: <enterprise application id> resource-server: permit-all: - /api/route/noauth
SecurityConfig
package some.package; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @EnableWebSecurity @EnableMethodSecurity @Configuration public class SecurityConfig { }
控制器代码
package some.package; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/route") public class JwtTestController { @GetMapping("/aadauth") @PreAuthorize("hasAuthority('AAD_APPROLE_GrantedApprole.XXX')") public ResponseEntity<String> aadauthRole() { Jwt jwt = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return ResponseEntity.ok(jwt.getClaims().toString()); } @GetMapping("/aadauth") @PreAuthorize("hasAuthority('AAD_SCOPE_GrantedScope.XXX')") public ResponseEntity<String> aadauthScope() { Jwt jwt = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return ResponseEntity.ok(jwt.getClaims().toString()); } @PreAuthorize("hasAuthority('COGNITO_GROUP_SomeGroup')") @GetMapping("/cognitoauth") public ResponseEntity<String> cognitoauth() { Jwt jwt = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return ResponseEntity.ok(jwt.getClaims().toString()); } @GetMapping("/noauth") public ResponseEntity<String> noauth() { return ResponseEntity.ok("Hello World!"); } }
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' implementation 'com.c4-soft.springaddons:spring-addons-starter-oidc:7.3.5'
这并非Spring官方starter,而是一个开源实现。目前运行正常,若遇到其他问题我会再次更新。
内容的提问来源于stack exchange,提问作者Fabian Schmidt

