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

Spring Boot应用配置AzureAD OAuth2与/rest/api/**路径Basic认证问题

问题根因

Spring Security 多配置类按照@Order注解的数值从小到大匹配优先级,且每个配置类默认处理所有请求,一旦请求匹配上优先级更高的配置规则,就不会再进入后续低优先级配置执行逻辑。
你现有配置的问题:

  • 优先级更高的WebSecurityAzureConfig处理全量请求,你要么把/rest/api/**设为permitAll直接放行,要么删除该行后要求该路径走OAuth2认证,始终轮不到优先级更低的Basic认证配置生效。

可行修改方案

核心调整点

  1. 调整两个配置类的优先级:将处理/rest/api/**的Basic认证配置优先级调到最高(@Order(1)),Azure OAuth2配置调整为次优先级(@Order(2))
  2. 给Basic认证配置添加路径限定,仅处理/rest/api/**开头的请求,其余请求自动跳过该配置,走后续OAuth2认证逻辑
  3. 删除Azure OAuth2配置中/rest/api/**的permitAll规则
  4. 补充Basic认证的用户校验逻辑(示例提供内存用户配置,可根据业务需求替换为数据库查询实现)

修改后完整配置代码

package com.test.security;

import com.azure.spring.aad.webapp.AADWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {

    @Autowired
    private AzureAuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private AzureOidcUserService oidcUserService;

    @SuppressWarnings("deprecation")
    @Bean
    public NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

    // Basic认证配置,优先级最高,仅处理/rest/api/**路径
    @Configuration
    @Order(1)
    public class WebSecurityBasicConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 限定该配置仅处理/rest/api/**路径的请求
            http.antMatcher("/rest/api/**")
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .httpBasic()
                    .and()
                    // API接口一般无状态,禁用CSRF和session
                    .csrf().disable()
                    .sessionManagement().disable();
        }

        // 示例:内存存储Basic认证的用户名密码,可替换为自定义UserDetailsService对接数据库查询
        @Bean
        @Override
        public UserDetailsService userDetailsService() {
            UserDetails user = User.withUsername("apiUser")
                    .password("apiPassword")
                    .roles("API_USER")
                    .build();
            return new InMemoryUserDetailsManager(user);
        }
    }

    // Azure AD OAuth2认证配置,次优先级,处理除/rest/api/**外的所有剩余请求
    @Configuration
    @Order(2)
    public class WebSecurityAzureConfig extends AADWebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
            http.headers().frameOptions().sameOrigin()
                    .and()
                        .csrf().disable()
                        .authorizeRequests()
                        .antMatchers("/css/**").permitAll()
                        .antMatchers("/fonts/**").permitAll()
                        .antMatchers("/images/**").permitAll()
                        .antMatchers("/scripts/**").permitAll()
                        .antMatchers("/swagger-resources/**", "/swagger-ui.html", "/webjars/**", "/v2/api-docs", "/csrf").permitAll()
                        .antMatchers("/version").permitAll()
                        .antMatchers("/healthCheck").permitAll()
                        .antMatchers("/h2-console/**").permitAll()
                        .antMatchers("/", "/login", "/oauth/**").permitAll()
                        .anyRequest().authenticated()
                    .and()
                        .formLogin()
                        .loginPage("/login")
                    .and()
                        .oauth2Login()
                        .userInfoEndpoint()
                        .oidcUserService(oidcUserService)
                        .and()
                        .successHandler(authenticationSuccessHandler)
                    .and()
                        .logout()
                        .invalidateHttpSession(true)
                        .clearAuthentication(true)
                        .logoutSuccessUrl("/logoutSuccess")
                        .permitAll();
        }
    }
}

注意事项

示例中使用的NoOpPasswordEncoder为明文密码编码器,仅适合测试使用,生产环境建议替换为BCryptPasswordEncoder等加密编码器。

内容的提问来源于stack exchange,提问作者Patrick C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 23:45:03