You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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,可通过配置直接完成组到权限的映射:

  1. 添加配置文件内容
    在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权限。

  2. 验证生效
    重启API后,用目标令牌请求接口,日志中应显示Granted Authorities=[SCOPE_api.read, ROLE_group1],此时@PreAuthorize校验将正常通过。

方法2:自定义JWT权限转换器(灵活适配场景)

若需批量转换、自定义前缀等灵活逻辑,可自定义权限转换器:

  1. 创建自定义转换器类

    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);
        }
    }
    
  2. 配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:34:56