相同 API 的 Spring Security Basic Auth 和 Form 登录

Posted

技术标签:

【中文标题】相同 API 的 Spring Security Basic Auth 和 Form 登录【英文标题】:Spring security Basic Auth and Form login for the same API 【发布时间】:2019-01-20 05:22:13 【问题描述】:

我想通过两种身份验证机制访问我的所有 API,基本身份验证和表单登录。我知道存在一些问题,但是答案对我不起作用,而且我的用例有点不同。

我的配置:

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

    @Configuration
    @Order(1)
    public static class SecurityConfigBasicAuth extends WebSecurityConfigurerAdapter 

        final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

        @Autowired
        public SecurityConfigBasicAuth(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
                                       @Qualifier("customUserDetailsService") UserDetailsService userDetailsService) 
            this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
            this.userDetailsService = userDetailsService;
        

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception 
            auth.userDetailsService(userDetailsService);
            auth.authenticationProvider(authenticationProvider());
        

        // @Bean authenticationProvider()

        // @Bean passwordEncoder()

        @Override
        protected void configure(HttpSecurity http) throws Exception 
            http.authorizeRequests().anyRequest().authenticated()
                    .and()
                    .cors()
                    .and()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .csrf().disable()
                    .httpBasic()
                    .authenticationEntryPoint(restAuthenticationEntryPoint)
                    .and()
                    .formLogin().disable()
                    .logout().disable();
        
    

    @Configuration
    public static class SecurityConfigFormLogin extends WebSecurityConfigurerAdapter 

        final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
        final private RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler;
        final private CustomAuthenticationProvider customAuthenticationProvider;

        @Autowired
        public SecurityConfigFormLogin(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
                                       RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler,
                                       CustomAuthenticationProvider hashAuthenticationProvider) 
            this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
            this.restfulSavedRequestAwareAuthenticationSuccessHandler = restfulSavedRequestAwareAuthenticationSuccessHandler;
            this.customAuthenticationProvider = customAuthenticationProvider;
        

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception 
            auth.authenticationProvider(customAuthenticationProvider);
        

        @Override
        protected void configure(HttpSecurity http) throws Exception 
            http.authorizeRequests().anyRequest().authenticated()
                    .and()
                    .cors()
                    .and()
                    .csrf().disable()
                    .exceptionHandling()
                    .authenticationEntryPoint(restAuthenticationEntryPoint)
                    .and()
                    .csrf().disable()
                    .httpBasic().disable()
                    .formLogin()
                    .usernameParameter("id1")
                    .passwordParameter("Id2")
                    .loginProcessingUrl("/test/login")
                    .successHandler(restfulSavedRequestAwareAuthenticationSuccessHandler)
                    .failureHandler(myFailureHandler())
                    .and()
                    .logout();
        

        // @Bean myFailureHandler()
    

如您所见,我定义了两个“WebSecurityConfigurerAdapters”,一个用于基本身份验证,一个用于表单登录。表单登录与 REST 兼容(不重定向,但提供 HTTP 响应)。

问题如下:加载的第一个“WebSecurityConfigurerAdapter”工作并覆盖第二个。上面的示例可以使用基本身份验证,但我无法登录 POST '/test/login',我得到:


    "timestamp": 1534164906450,
    "status": 401,
    "error": "Unauthorized",
    "message": "Unauthorized",
    "path": "/test/login"

更新已修复:关键是使用“requestMatchers()”,请参阅答案部分以获取解决方案(如 jzheaux 所建议)

【问题讨论】:

在同一端点上配置两组 Web 安全过滤器不会有任何好处。我唯一可以建议的是使用不同的 URL,一个用于表单登录,一个用于 BASIC auth - 一旦通过身份验证,那么您应该能够使用会话。 我在做什么不同于:github.com/spring-projects/spring-security-javaconfig/blob/…?除了没有定义 url 路径。 使用不同的 URL 是什么意思?两种身份验证都会产生特定的角色,在这些角色中他们可以访问相同的 API 这正是您正在做的不同之处,以及为什么它不起作用。 如果你真的想要两个不同的过滤器链,那么先看看http.requestMatchers()。这将允许您通过 url 相当干净地拆分配置。您(可能)在当前配置中遇到的问题是,它们并没有精确地覆盖它们彼此合并(如果您不执行我上面推荐的操作)。我很想亲自尝试一下一个做 httpBasic 和另一个做 httpBasic().disable 的合并配置。 【参考方案1】:

好的,我就是这样解决这个问题的:

我将基本身份验证配置配置为:

protected void configure(HttpSecurity http) throws Exception 
    http.requestMatchers()
            .antMatchers("/api/**")
            .and()
            .cors()
            .and()
            .csrf().disable()
            .httpBasic()
            .authenticationEntryPoint(restAuthenticationEntryPoint)
            .and();

如果您不希望基本身份验证返回带有新 JSESSIONID 的新 cookie,请添加:

            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER)
            .sessionFixation()
            .migrateSession()

Form 登录配置为:

 protected void configure(HttpSecurity http) throws Exception 
            http.requestMatchers()
                    .antMatchers(HttpMethod.POST, "/test/login")
                    .and()
                    .cors()
                    .and()
                    .csrf().disable()
                    .exceptionHandling()
                    .authenticationEntryPoint(restAuthenticationEntryPoint)
                    .and()
                    .formLogin()
                    .usernameParameter("id1")
                    .passwordParameter("id2")
                    .loginProcessingUrl("/test/login")
                    .successHandler(authenticationSuccessHandler)
                    .failureHandler(myFailureHandler())
                    .and()
                    .logout();
        

现在,我可以通过表单登录配置进行身份验证,并使用 cookie 会话 ID 调用 /api/**(在基本身份验证配置中配置)。当然,我也可以只使用 Basic Auth 身份验证。

【讨论】:

以上是关于相同 API 的 Spring Security Basic Auth 和 Form 登录的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security + MVC:相同的@RequestMapping,不同的@Secured

spring security oauth2 禁用基于 jsessionid 的会话

grails spring security rest /api/login 401 Unauthorized

使用 Spring Security 保护 REST API

使用 Spring Security Rest 插件保护 Grails Rest Api

用于非授权连接的 Spring Security REST Api