有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java我什么时候应该在Spring启动应用程序中覆盖Spring Security中的配置(AuthenticationManagerBuilder auth)?

我正在一个Spring Boot应用程序中学习Spring安全性,我有一个非常简单的例子。我发现,如果我对configure(AuthenticationManagerBuilder auth)进行注释,没有什么区别。如果我使用或不使用它,我有相同的输出,我需要用硬编码的凭证登录

@Configuration
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

//    private final MyUserDetailsService myUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
                http
                .csrf().disable()
                        .authorizeRequests().anyRequest().authenticated()
                .and()
                        .httpBasic();
    }

//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.userDetailsService(myUserDetailsService);
//    }

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

MyUserDetails服务类:

@Service
public class MyUserDetailsService implements UserDetailsService {

    private static final String USERNAME = "john";
    private static final String PASSWORD = "$2a$10$fDDUFA8rHAraWnHAERMAv.4ReqKIi7mz8wrl7.Fpjcl1uEb6sIHGu";

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

        if (!userName.equals(USERNAME)) {
            throw new UsernameNotFoundException(userName);
        }

        return new User(USERNAME, PASSWORD, new ArrayList<>());
    }
}

RestController:

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}

我想知道实现UserDetailsService接口是否等同于重写configure(AuthenticationManagerBuilder auth)。谢谢大家!


共 (6) 个答案

  1. # 1 楼答案

    不,实现UserDetailsService接口并不等同于覆盖配置(AuthenticationManagerBuilder验证)

    如果覆盖UserDetailsService并通过覆盖loadUserByUsername()验证用户名和密码,那么在您的情况下,它是静态值(我建议静态用户使用inMemoryAuthentication)

    您需要自动连接UserDetailsService

    @Autowired
    UserDetailsService userDetailsService;
    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }
    

    这将告诉您的authenticationManager使用已实现用于身份验证的userDetailsService

  2. # 2 楼答案

    要完全关闭默认的web应用程序安全配置,可以添加一个带有@EnableWebSecurity的bean,如spring boot documentation(第4.10.1.MVC安全部分)中所述

        @Configuration
        @EnableWebSecurity
        public class SecurityConfig extends WebSecurityConfigurerAdapter {
        
            @Autowired
            public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
                auth.authenticationProvider(MyAuthenticationProvider);
            }
        }
    

    @EnableWebSecurity是一个标记注释。它允许Spring查找(它是一个@Configuration,因此,@Component)并自动将类应用于全局WebSecurity

    To switch off the default web application security configuration completely you can add a bean with @EnableWebSecurity (this does not disable the authentication manager configuration or Actuator’s security). To customize it you normally use external properties and beans of type WebSecurityConfigurerAdapter (e.g. to add form-based login).

    ...

    If you add @EnableWebSecurity and also disable Actuator security, you will get the default form-based login for the entire application unless you add a custom WebSecurityConfigurerAdapter.

    ...

    If you define a @Configuration with @EnableWebSecurity anywhere in your application it will switch off the default webapp security settings in Spring Boot (but leave the Actuator’s security enabled). To tweak the defaults try setting properties in security.* (see SecurityProperties for details of available settings) and SECURITY section of Common application properties.

  3. # 3 楼答案

    您正在使用@Service注释,该注释在组件扫描时创建UserDetailsService的bean。无需在AuthenticationManagerBuilder中再次指定它

    如果不使用@Service注释,则可以在WebSecurityConfigurerAdapter中通过跨过AuthenticationManagerBuilder来手动配置它

  4. # 4 楼答案

    不,不一样

    应用程序中作为bean提供的用户详细信息服务在global身份验证管理器(^{})中注册,并且是所有本地身份验证管理器的回退

    根据应用程序设置,可以有多个本地身份验证管理器。每个本地身份验证管理器将使用配置为configure(AuthenticationManagerBuilder auth)的默认用户详细信息服务

    When should I override the configure(AuthenticationManagerBuilder auth) from Spring Security in a Spring Boot app?

    如果您有不同的授权/身份验证要求,并且希望插入自己的身份验证提供程序以满足该要求,或者添加任何内置提供程序(如ldap和内存中提供程序),则应该重写。您还可以直接使用如下所示的http安全bean来实现

    所有身份验证提供程序都将添加到^{},并一直尝试,直到找到一个

    默认情况下,在不提供任何内容的情况下(即不提供用户详细信息服务或不覆盖身份验证管理器),您将拥有默认的全局身份验证管理器和自动配置的用户详细信息管理器(即用户密码InMemoryUserDetailsManager实现,如UserDetailsServiceAutoConfiguration自动配置中配置的)

    因此,当您提供用户详细信息服务应用程序bean时,自动配置将退出,现在您的全局身份验证管理器将使用提供的bean进行配置

    更多详情^{}

    ^{}很好地解释了这一切是如何结合在一起的

    我还想对SpringSecurityAuthenticationManager进行更详细的介绍,这很容易被忽视

    正如我前面提到的,有全局身份验证管理器和本地身份验证管理器。如果需要,在配置每一个时都要特别小心

    这在全局身份验证管理器注释的java文档中进行了解释

    The EnableGlobalAuthentication annotation signals that the annotated class can be used to configure a global instance of AuthenticationManagerBuilder. For example:

    @Configuration 
    @EnableGlobalAuthentication  
    public class MyGlobalAuthenticationConfiguration {
    
       @Autowired
       public void configureGlobal(AuthenticationManagerBuilder auth) {
                auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
                                .and().withUser("admin").password("password").roles("USER", "ADMIN");}} 
    

    Annotations that are annotated with EnableGlobalAuthentication also signal that the annotated class can be used to configure a global instance of AuthenticationManagerBuilder. For example:

     @Configuration  
     @EnableWebSecurity  
     public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) {
                auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
                                .and().withUser("admin").password("password").roles("USER", "ADMIN");
        }
    
        // Possibly overridden methods ...  }   
    

    The following annotations are annotated with EnableGlobalAuthentication EnableWebSecurity EnableWebMvcSecurity EnableGlobalMethodSecurity

    Configuring AuthenticationManagerBuilder in a class without the EnableGlobalAuthentication annotation has unpredictable results.

    EnableGlobalAuthentication导入配置AuthenticationConfiguration负责为全局身份验证管理器设置默认配置

    AuthenticationConfiguration配置两个关键部分,以创建身份验证管理器-用户详细信息和身份验证提供程序

    使用InitializeUserDetailsBeanManagerConfigurer配置用户详细信息,使用InitializeAuthenticationProviderBeanManagerConfigurer配置身份验证提供程序。这两个必需的bean都是在应用程序上下文中查找的——这就是您的用户详细信息服务在全局身份验证管理器中注册的方式

    GlobalMethodSecurityConfigurationWebSecurityConfigurerAdapter是全局身份验证管理器的使用者

    WebSecurityConfigurerAdapter可用于创建和配置本地身份验证管理器(添加新的身份验证提供程序),也通常用于在应用程序中具有不同的身份验证/授权要求,如mvc与rest和public与admin端点

    单独使用spring security @EnableWebSecurity触发上述流,作为spring security过滤器链设置的一部分。使用spring引导时,相同的流由spring security自动配置触发

    在SpringSecurity5.4版本中,您可以将http安全性定义为bean,而无需扩展WebSecurityConfigureAdapter类。Spring boot在2.4.0版本中将对此提供支持。更多详情^{}

        @Bean
        SecurityFilterChain configure(HttpSecurity http) throws Exception 
          {
             http
                .authenticationProvider(custom authentication provider)
                .userDetailsService( custom user details service)
                .csrf().disable()
                        .authorizeRequests().anyRequest().authenticated()
                .and()
                        .httpBasic();
            return http.build();
          }
    
  5. # 5 楼答案

    我想知道实现UserDetailsService接口是否等同于覆盖configure(AuthenticationManagerBuilder auth)

    不,它们是不可比较的

    UserDetailsService is core interface which loads user-specific data. It is used throughout the framework as a user DAO and is the strategy used by the DaoAuthenticationProvider. *

    AuthenticationManagerBuilder allows for easily building in memory authentication, JDBC based authentication, adding UserDetailsService, and adding AuthenticationProvider's.

    因此,很明显,当您使用UserDetailsService时,这意味着您正在使用DaoAuthenticationProvider从底层数据库获取用户详细信息

    注意:AuthenticationProvider是从不同来源/存储库获取用户信息的抽象,并验证检索到的信息是否与用户提供的信息相似

    让我们看一个示例,配置如下所示:

    @Autowired
    YourUserDetailServiceImpl userDetailsService;
    .....
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }
    ...
    @Bean
    public DaoAuthenticationProvider authenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); //a provider 
        provider.setUserDetailsService(userDetailsService); //user details service
        provider.setPasswordEncoder(encoder()); //you can add password encoders too
        
        return provider;
    }
    

    而且YourUserDetailServiceImpl必须重写loadUserByUsername()以获取使用过的详细信息

    @Override
    public UserDetails loadUserByUsername(String email) {
        final Account acc = accRepository.findByEmail(email);
        if (acc == null)
            throw new UsernameNotFoundException("Account not found");
        
        //this can be a custom Object of your choice that `extends User`
        return new UserPrincipal(
                    acc.getEmail(), 
                    acc.getPassword(), 
                    acc.isEnabled(), 
                    true, true, true, 
                    acc.getEpsRoles().stream()
                            .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
                            .collect(Collectors.toList()));
    }
    
  6. # 6 楼答案

    UserDetailsService

    UserDetailsService is used by DaoAuthenticationProvider for retrieving a username, password, and other attributes for authenticating with a username and password. Spring Security provides in-memory and JDBC implementations of UserDetailsService.

    You can define custom authentication by exposing a custom UserDetailsService as a bean. For example, the following will customize authentication assuming that CustomUserDetailsService implements UserDetailsService

    UserDetailsService界面用于检索与用户相关的数据。它有一个名为loadUserByUsername()的方法,可以覆盖来定制查找用户的过程。为了提供我们自己的用户服务,我们需要实现用户详细信息服务接口

    loadUserByUsername(String username)返回用户详细信息,它是org.springframework.security.core.userdetails的一部分,由{}方法组成,进一步用于spring安全性

    我们还可以通过实现UserDetails接口来定制org.springframework.security.core.userdetails.User(这里用作new User(USERNAME, PASSWORD, new ArrayList<>())

    在这里,我分享使用UserDetailsService服务的理想方式

    @Component("userDetailsService")
    public class DomainUserDetailsService implements UserDetailsService {
    
        private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);
    
        private final UserRepository userRepository;
    
        public DomainUserDetailsService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        @Override
        @Transactional
        public UserDetails loadUserByUsername(final String login) {
            log.debug("Authenticating {}", login);
    
            if (new EmailValidator().isValid(login, null)) {
                return userRepository.findOneWithAuthoritiesByEmailIgnoreCase(login)
                    .map(user -> createSpringSecurityUser(login, user))
                    .orElseThrow(() -> new UsernameNotFoundException("User with email " + login + " was not found in the database"));
            }
    
            String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
            return userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin)
                .map(user -> createSpringSecurityUser(lowercaseLogin, user))
                .orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));
    
        }
    
        private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
            if (!user.getActivated()) {
                throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
            }
            List<GrantedAuthority> grantedAuthorities = user.getAuthorities().stream()
                .map(authority -> new SimpleGrantedAuthority(authority.getName()))
                .collect(Collectors.toList());
            return new org.springframework.security.core.userdetails.User(user.getLogin(),
                user.getPassword(),
                grantedAuthorities);
        }
    }
    

    when loadUserByUsername is invoked?

    如上所述,DAOAuthenticationProvider实例通常会调用它来验证用户。例如,当提交用户名和密码时,将调用UserdetailsService查找该用户的密码,以查看其是否正确。它通常还会提供一些关于用户的其他信息,例如权限和您可能希望登录用户访问的任何自定义字段(例如电子邮件)

    In-Memory Authentication

    在这里,您使用了用户名和密码的静态值,理想情况下可以使用In-Memory Authentication进行如下配置

    Spring Security的InMemoryUserDetailsManager实现UserDetailsService以支持在内存中检索的基于用户名/密码的身份验证InMemoryUserDetailsManager通过实现UserDetailsManager接口,提供对UserDetails的管理^当Spring Security配置为接受用户名/密码进行身份验证时,它使用基于{}的身份验证

    @Bean
    public UserDetailsService users() {
        UserDetails user = User.builder()
            .username("user")
            .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
            .roles("USER")
            .build();
        UserDetails admin = User.builder()
            .username("admin")
            .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
            .roles("USER", "ADMIN")
            .build();
        return new InMemoryUserDetailsManager(user, admin);
    }
    

    配置(AuthenticationManagerBuilder验证)

    此方法使用AuthenticationManagerBuilder,它在内部使用SecurityBuilder创建AuthenticationManager。允许轻松构建内存身份验证、LDAP身份验证、基于JDBC的身份验证、添加UserDetailsService和添加 AuthenticationProvider

    How Spring Security add/configure AuthenticationManagerBuilder?

    UserDetailsService interface is equivalent with overriding the configure(AuthenticationManagerBuilder auth)