如何通过AAD组对Spring Boot Web API进行权限保护与授权?
如何通过Azure AD组对Spring Boot Web API进行权限保护
问题分析
你当前的核心问题是:Azure AD颁发的访问令牌中已包含用户所属组的OID("groups": ["03652e89-9de8-4ae9-84a5-9798a077d62b"]),但Spring Security未将该组OID转换为ROLE_group1格式的权限,导致@PreAuthorize("hasRole('ROLE_group1')")权限校验失败,返回403错误。
解决方案
方法1:使用Spring Cloud Azure Starter自动配置(推荐)
如果你的项目依赖spring-cloud-azure-starter-active-directory,可通过配置直接完成组到权限的映射:
添加配置文件内容
在application.yml中添加:spring: cloud: azure: active-directory: resource-server: jwt: groups-claim: groups allowed-groups: "03652e89-9de8-4ae9-84a5-9798a077d62b": ROLE_group1或
application.properties格式:spring.cloud.azure.active-directory.resource-server.jwt.groups-claim=groups spring.cloud.azure.active-directory.resource-server.jwt.allowed-groups.03652e89-9de8-4ae9-84a5-9798a077d62b=ROLE_group1该配置会自动将令牌
groups字段中的指定OID,转换为ROLE_group1权限。验证生效
重启API后,用目标令牌请求接口,日志中应显示Granted Authorities=[SCOPE_api.read, ROLE_group1],此时@PreAuthorize校验将正常通过。
方法2:自定义JWT权限转换器(灵活适配场景)
若需批量转换、自定义前缀等灵活逻辑,可自定义权限转换器:
创建自定义转换器类
import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; public class CustomJwtAuthConverter implements Converter<Jwt, AbstractAuthenticationToken> { private final JwtGrantedAuthoritiesConverter defaultConverter = new JwtGrantedAuthoritiesConverter(); private static final String GROUP1_OID = "03652e89-9de8-4ae9-84a5-9798a077d62b"; private static final String GROUP1_ROLE = "ROLE_group1"; @Override public AbstractAuthenticationToken convert(Jwt jwt) { // 获取默认权限(如scope) Collection<GrantedAuthority> authorities = defaultConverter.convert(jwt); // 从令牌中提取groups字段并转换为对应权限 List<String> groups = jwt.getClaimAsStringList("groups"); if (groups != null && !groups.isEmpty()) { Collection<GrantedAuthority> groupAuthorities = groups.stream() .filter(oid -> oid.equals(GROUP1_OID)) .map(oid -> new SimpleGrantedAuthority(GROUP1_ROLE)) .collect(Collectors.toList()); authorities.addAll(groupAuthorities); } return new JwtAuthenticationToken(jwt, authorities); } }配置SecurityFilterChain注册转换器
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(new CustomJwtAuthConverter()) ) ); return http.build(); } }
注意事项
- 确保AAD应用注册已配置安全组作为令牌声明,否则令牌不会包含
groups字段; hasRole('ROLE_group1')等价于hasAuthority('ROLE_group1'),若无需ROLE_前缀,可直接使用hasAuthority('group1')并调整映射规则;- 多组映射只需在配置或转换器中添加对应OID与角色的关联即可。
内容的提问来源于stack exchange,提问作者feng
相关产品推荐
相关产品推荐

