spring-security 正在使用InMemoryUserDetailsManager将WebSecurityConfigurerAdapter转换为新的5.7.0语法,但未找到任何验证提供程序

2w3rbyxf  于 2022-11-11  发布在  Spring
关注(0)|答案(2)|浏览(149)

我正在尝试将一个应用程序更新到 Boot ,它将SpringSecurity升级到了5.7,并弃用了WebSecurityConfigurerAdapter
下面是旧代码的一个简化版本,它创建一个用户,并且可以工作。注意,这个代码只用于本地开发和测试,使用一个简单的表单登录:

@Configuration
@Profile("local-auth")
class LocalAuth {
    @Bean
    public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter(
            PasswordEncoder passwordEncoder) {
        return new WebSecurityConfigurerAdapter() {
            @Override
            public void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.inMemoryAuthentication()
                        .withUser("testuser")
                        .password(passwordEncoder.encode("a"))
                        .authorities(toAuthorities(List.of("SOME_AUTHORITY")));
            }

            private List<? extends GrantedAuthority> toAuthorities(List<String> roles) {
                return roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
            }

            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http
                        .authorizeRequests(authorizeRequests ->
                                authorizeRequests
                                        .antMatchers("/login", "/actuator/**", "/publico/**").permitAll()
                                        .anyRequest().authenticated()
                        )
                        .formLogin(formLogin -> formLogin.defaultSuccessUrl("http://localhost:4200/app/").permitAll())
                        .logout(logout -> logout.logoutUrl("/logout"))
                        .csrf(AbstractHttpConfigurer::disable);
            }
        };
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

按照https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter处的指南,我将代码更改为

@Configuration
@Profile("local-auth")
class LocalAuth {
    @Bean
    public InMemoryUserDetailsManager userDetailService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.builder()
                .username("testuser")
                .password(passwordEncoder.encode("a"))
                .authorities(toAuthorities(List.of("SOME_AUTHORITY")))
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    private List<? extends GrantedAuthority> toAuthorities(List<String> roles) {
        return roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests
                                .antMatchers("/login", "/actuator/**", "/publico/**").permitAll()
                                .anyRequest().authenticated()
                )
                .formLogin(formLogin -> formLogin.defaultSuccessUrl("http://localhost:4200/app/").permitAll())
                .logout(logout -> logout.logoutUrl("/logout"))
                .csrf(AbstractHttpConfigurer::disable);
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

显示登录表单,但当我尝试登录时,在登录页面中出现以下错误:找不到org.springframework.security.authentication.UsernamePasswordAuthenticationToken的验证提供者。奇怪的是,应用程序日志中没有任何内容被打印出来。
我还尝试提供一个AuthenticationManager,将第一个bean替换为

@Bean
    public AuthenticationManager authenticationManager(AuthenticationManagerBuilder authBuilder,
            PasswordEncoder passwordEncoder) throws Exception {
        UserDetails user = User.builder()
                .username("govbr.20821922459")
                .password(passwordEncoder.encode("a"))
                .authorities(toAuthorities(List.of("LU_NVL_CONTA_BASICA")))
                .build();
        return authBuilder.inMemoryAuthentication().withUser(user).and().build();
    }

但随后应用程序无法启动,并显示无法将org.springframework.security.config.annotation.authentication.configuration.authenticationConfiguration$EnableGlobalauthenticationAutowiredConfigurer@313c3cb应用于已生成的对象
有人知道转换旧WebSecurityConfigurerAdapter的正确方法吗?

Edit:即使将日志级别增加到DEBUG,我在登录尝试中得到的只是

2022-07-27 14:22:26.012 [                    ] DEBUG o.s.security.web.FilterChainProxy             - Securing POST /login
2022-07-27 14:22:26.013 [                    ] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter    - Set SecurityContextHolder to empty SecurityContext
2022-07-27 14:22:26.015 [                    ] DEBUG o.s.security.web.DefaultRedirectStrategy      - Redirecting to /svr/rest/login?error
2022-07-27 14:22:26.015 [                    ] DEBUG .s.s.w.c.HttpSessionSecurityContextRepository - Did not store empty SecurityContext
2022-07-27 14:22:26.015 [                    ] DEBUG .s.s.w.c.HttpSessionSecurityContextRepository - Did not store empty SecurityContext
2022-07-27 14:22:26.015 [                    ] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter    - Cleared SecurityContextHolder to complete request
2022-07-27 14:22:26.041 [                    ] DEBUG o.s.security.web.FilterChainProxy             - Securing GET /login?error
2022-07-27 14:22:26.041 [                    ] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter    - Set SecurityContextHolder to empty SecurityContext
2022-07-27 14:22:26.042 [                    ] DEBUG .s.s.w.c.HttpSessionSecurityContextRepository - Did not store empty SecurityContext
2022-07-27 14:22:26.042 [                    ] DEBUG .s.s.w.c.HttpSessionSecurityContextRepository - Did not store empty SecurityContext
2022-07-27 14:22:26.042 [                    ] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter    - Cleared SecurityContextHolder to complete request

并且对/login的POST的响应是302重定向到/login?error,而不引用任何特定的错误消息或代码(除非错误消息是从会话中确定的)。

wz1wpwve

wz1wpwve1#

我发现了问题:这样ApplicationContext中就有两个bean,这必须禁用Spring自动配置代码,该代码创建内存中身份验证工作所必需的任何东西。
解决方案是在选择本地概要文件时禁用生产bean,这似乎是唯一可能的解决方案:添加@Primary和添加优先级高于生产bean的@Order都不起作用。

Edit:实际上,配置是由InitializeUserDetailsBeanManagerConfigurer类完成的,它检查是否只有一个类型为UserDetailsService的bean(它甚至在其Javadoc中进行了说明)。

另一种可能是手动创建一个AuthenticationProvider,指向InMemoryUserDetailsManager。为此,只需注册一个GlobalAuthenticationConfigurerAdapter bean(这是在构建AuthenticationManager时配置AuthenticationManager的一种通用方法):

@Bean
GlobalAuthenticationConfigurerAdapter adapter(InMemoryUserDetailsManager inMemoryUserDetails, PasswordEncoder passwordEncoder) {
    return new GlobalAuthenticationConfigurerAdapter() {
        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
            provider.setUserDetailsService(inMemoryUserDetails);
            provider.setUserDetailsPasswordService(inMemoryUserDetails);
            provider.setPasswordEncoder(passwordEncoder);
            auth.authenticationProvider(provider);
        }
    };
}
vatpfxk5

vatpfxk52#

在非WebSecurityConfigurerAdapter配置中,我在获取身份验证管理器的句柄时遇到了问题,除非我在dsl中使用apply()。但我认为您可以在没有身份验证管理器的情况下使用此方法。警告:这个例子没有使用在任何生产环境中都应该使用的密码编码器。

@Bean
    public UserDetailsService userDetailsService() {
        UserDetails admin = User.builder()
                                .username("admin")
                                .password("{noop}" + "password")
                               .roles("ADMIN")
                               .build();
        return new InMemoryUserDetailsManager(admin);
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService) {

        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);

        return provider;
    }

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {

        http.httpBasic(withDefaults())
            .authenticationProvider(daoAuthenticationProvider(userDetailsService()));

        return http.build();    
    }

相关问题