何时应在Spring Boot应用程序中重写configure(AuthenticationManagerBuilder auth)方法?

huangapple go评论88阅读模式
英文:

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

问题

我正在学习在Spring Boot应用中使用Spring Security并且有一个非常简单的示例我注意到如果我注释掉`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();
    }
}

MyUserDetailsService类

@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)`方法谢谢
英文:

I'm learning about Spring Security in a Spring Boot app and I have a very simple example. And I see that if I comment the configure(AuthenticationManagerBuilder auth) there is no difference. If I use it or not I have the same output, and I need to login with the hardcoded credentials.

@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();
}
}

MyUserDetailsService class:

@Service
public class MyUserDetailsService implements UserDetailsService {
private static final String USERNAME = &quot;john&quot;;
private static final String PASSWORD = &quot;$2a$10$fDDUFA8rHAraWnHAERMAv.4ReqKIi7mz8wrl7.Fpjcl1uEb6sIHGu&quot;;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
if (!userName.equals(USERNAME)) {
throw new UsernameNotFoundException(userName);
}
return new User(USERNAME, PASSWORD, new ArrayList&lt;&gt;());
}
}

RestController:

@RestController
public class HelloController {
@GetMapping(&quot;/hello&quot;)
public String hello() {
return &quot;Hello World!&quot;;
}
}

I want to know if implementing the UserDetailsService interface is equivalent with overriding the configure(AuthenticationManagerBuilder auth). Thank you!

答案1

得分: 9

UserDetailsService

UserDetailsService被DaoAuthenticationProvider用于检索用于使用用户名和密码进行身份验证的用户名、密码和其他属性。Spring Security提供了内存JDBC实现的UserDetailsService。

您可以通过将自定义UserDetailsService公开为bean来定义自定义身份验证。例如,以下内容将自定义身份验证视为CustomUserDetailsService实现UserDetailsService

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

loadUserByUsername(String username)返回UserDetails,它是org.springframework.security.core.userdetails的一部分,其中包含***getUsername(),getPassword(),getAuthorities()***方法,这些方法在Spring Security中进一步使用。

我们还可以通过实现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);
    }
}

当loadUserByUsername何时被调用?

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

内存中的身份验证

在这里,您使用了用户名和密码的静态值,可以使用以下方法进行理想配置,即内存中的身份验证

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

以下是配置内存中用户的示例:

@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);
}

configure(AuthenticationManagerBuilder auth)

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

Spring Security如何添加/配置AuthenticationManagerBuilder?

UserDetailsService接口等同于覆盖configure(AuthenticationManagerBuilder auth)

英文:

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

The UserDetailsService interface is used to retrieve user-related data. It has one method named loadUserByUsername() which can be overridden to customize the process of finding the user. In order to provide our own user service, we will need to implement the UserDetailsService interface.

loadUserByUsername(String username) returns the UserDetails which is part of org.springframework.security.core.userdetails which consists of getUsername(), getPassword(), getAuthorities() methods which is used further for spring security.

We can also customize the org.springframework.security.core.userdetails.User (here used as new User(USERNAME, PASSWORD, new ArrayList&lt;&gt;())) by implemeting the UserDetails interface.

Here, I am sharing the ideal way to use the UserDetailsService service

@Component(&quot;userDetailsService&quot;)
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(&quot;Authenticating {}&quot;, login);
if (new EmailValidator().isValid(login, null)) {
return userRepository.findOneWithAuthoritiesByEmailIgnoreCase(login)
.map(user -&gt; createSpringSecurityUser(login, user))
.orElseThrow(() -&gt; new UsernameNotFoundException(&quot;User with email &quot; + login + &quot; was not found in the database&quot;));
}
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
return userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin)
.map(user -&gt; createSpringSecurityUser(lowercaseLogin, user))
.orElseThrow(() -&gt; new UsernameNotFoundException(&quot;User &quot; + lowercaseLogin + &quot; was not found in the database&quot;));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
if (!user.getActivated()) {
throw new UserNotActivatedException(&quot;User &quot; + lowercaseLogin + &quot; was not activated&quot;);
}
List&lt;GrantedAuthority&gt; grantedAuthorities = user.getAuthorities().stream()
.map(authority -&gt; new SimpleGrantedAuthority(authority.getName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getLogin(),
user.getPassword(),
grantedAuthorities);
}
}

when loadUserByUsername is invoked?

As described above, It is typically called by DaoAuthenticationProvide instance in order to authenticate a user. For example, when a username and password is submitted, a UserdetailsService is called to find the password for that user to see if it is correct. It will also typically provide some other information about the user, such as the authorities and any custom fields you may want to access for a logged in user (email, for instance)

In-Memory Authentication

Here you have used the static values for username and password which can be ideally configured using the In-Memory Authentication as follow.

Spring Security’s InMemoryUserDetailsManager implements UserDetailsService to provide support for username/password based authentication that is retrieved in memory. InMemoryUserDetailsManager provides management of UserDetails by implementing the UserDetailsManager interface. UserDetails based authentication is used by Spring Security when it is configured to accept a username/password for authentication.

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

configure(AuthenticationManagerBuilder auth)

This method uses AuthenticationManagerBuilder which internally use SecurityBuilder to create an AuthenticationManager. Allows for easily building in memory authentication, LDAP authentication, JDBC based authentication, adding UserDetailsService, and adding
AuthenticationProvider.

How Spring Security add/configure AuthenticationManagerBuilder?

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

No

答案2

得分: 5

No, it's not same. 

应用程序中提供的用户详细信息服务作为 bean 注册到全局认证管理器([`details`](https://github.com/spring-projects/spring-security/blob/master/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/InitializeUserDetailsBeanManagerConfigurer.java)),并且作为所有本地认证管理器的后备。

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

&gt; 在 Spring Boot 应用程序中,应该在什么时候覆盖 Spring Security 中的 `configure(AuthenticationManagerBuilder auth)` 方法?

如果您有不同的授权/认证要求,并且希望插入自己的认证提供程序以满足要求,或者添加任何内置提供程序,如 LDAP 和内存提供程序,那么您应该进行覆盖。您还可以直接使用下面显示的 http 安全 bean 进行操作。

所有的认证提供程序都会添加到 [`Provider Manager`](https://docs.spring.io/spring-security/site/docs/4.2.18.RELEASE/apidocs/org/springframework/security/authentication/ProviderManager.html) 中,并且会逐个尝试,直到找到一个可用的为止。

默认情况下,如果不提供任何内容(即没有用户详细信息服务或未覆盖认证管理器),则会使用默认的全局认证管理器,其中包括自动配置的用户详细信息管理器(即 `UserDetailsServiceAutoConfiguration` 自动配置中配置的基于内存的 `InMemoryUserDetailsManager` 实现)。

因此,当您提供用户详细信息服务应用程序 bean 时,自动配置会被取消,现在您的全局认证管理器将配置为所提供的 bean。

更多详情请查看 [`这里`](https://spring.io/guides/topicals/spring-security-architecture)。

[`这里`](https://stackoverflow.com/a/54603001/2683814) 提供了关于如何整合这一切的很好的解释。

我还想进一步介绍一下 Spring Security 认证管理器,这是非常容易被忽视的。

正如我之前提到的,有全局认证管理器和本地认证管理器。在需要的情况下,配置每个管理器时需要特别小心。

这在全局认证管理器注解的 Java 文档中有解释。

&gt; `EnableGlobalAuthentication` 注解表示带有注解的类可用于配置全局的 `AuthenticationManagerBuilder` 实例。
&gt; 例如:
&gt;
&gt;     @Configuration 
&gt;     @EnableGlobalAuthentication  
&gt;     public class MyGlobalAuthenticationConfiguration {
&gt; 
&gt;        @Autowired
&gt;        public void configureGlobal(AuthenticationManagerBuilder auth) {
&gt;                 auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
&gt;                                 .and().withUser("admin").password("password").roles("USER", "ADMIN");
&gt;        } 
&gt;     }

&gt; 带有 `EnableGlobalAuthentication` 注解的注解也表示带有注解的类可用于配置全局的 `AuthenticationManagerBuilder` 实例。
&gt; 例如:
&gt;
&gt;      @Configuration  
&gt;      @EnableWebSecurity  
&gt;      public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
&gt; 
&gt;         @Autowired
&gt;         public void configureGlobal(AuthenticationManagerBuilder auth) {
&gt;                 auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
&gt;                                 .and().withUser("admin").password("password").roles("USER", "ADMIN");
&gt;         }
&gt; 
&gt;         // 可能被覆盖的方法 ...  
&gt;      }   
&gt; 带有以下注解的注解都带有 `EnableGlobalAuthentication`:
&gt; EnableWebSecurity、EnableWebMvcSecurity、EnableGlobalMethodSecurity
&gt;
&gt; 在不带有 `EnableGlobalAuthentication` 注解的类中配置 `AuthenticationManagerBuilder` 会产生不可预测的结果。

`EnableGlobalAuthentication` 导入了配置 `AuthenticationConfiguration`,该配置负责设置全局认证管理器的默认配置。

`AuthenticationConfiguration` 配置了两个关键部分来创建认证管理器 - 用户详细信息和认证提供程序。

用户详细信息是使用 `InitializeUserDetailsBeanManagerConfigurer` 进行配置的,认证提供程序是使用 `InitializeAuthenticationProviderBeanManagerConfigurer` 进行配置的。这两个所需的 bean 都会在应用程序上下文中查找 - 这就是您的用户详细信息服务是如何注册到全局认证管理器的方式。

`GlobalMethodSecurityConfiguration` 和 `WebSecurityConfigurerAdapter` 是全局认证管理器的消费者。

`WebSecurityConfigurerAdapter` 可用于创建和配置本地认证管理器(添加新的认证提供程序),并且通常用于在应用程序中具有不同的认证/授权要求,例如 MVC vs REST 和公共 vs 管理员端点。

仅使用 Spring Security 时,`@EnableWebSecurity` 会作为 Spring Security 过滤器链设置的一部分触发上述流程。在使用 Spring Boot 时,相同的流程会被 Spring Security 自动配置触发。

在 Spring Security 5.4 版本中,您可以定义 http 安全性 bean,无需扩展 `WebSecurityConfigurerAdapter` 类。Spring Boot 在 2.4.0 版本中将对此提供支持。更多详情请查看 [`这里`](https://github.com/spring-projects/spring-boot/issues/23421)。

```java
@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();
}
英文:

No, it's not same.

User details service provided in the application as bean is registered with global authentication manager (details) and is fallback for all the local authentication manager.

Depending on application set up can have multiple local authentication managers. Each local authentication manager will use the default user details service configured with configure(AuthenticationManagerBuilder auth).

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

You should override if you have different authorization/authentication requirements and would you like to plugin your own authentication provider to satisfy the requirement or add any built in provider like ldap and in memory providers. You can also do it directly using http security bean shown below.

All the authentication providers are added to Provider Manager and are tried until one is found.

By default without providing anything ( i.e. without user details service or without overriding authentication manager ) you would have the default global authentication manager with auto configured user details manager ( i.e user password InMemoryUserDetailsManager implementation as configured in UserDetailsServiceAutoConfiguration auto configuration ).

So when you provide user details service application bean the auto configuration backs off and now your global authentication manager now is configured with the provided bean.

More details here

Here is the good explanation how it all comes together.

I would also like to expand little bit more on spring security authentication manager in general which is very easy to overlook.

As I previously noted there is global authentication manager and local authentication managers. There is special care to be taken when configuring each if needed.

This is explained in the java doc for the global authentication manager annotation.

> 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 imports configuration AuthenticationConfiguration responsible for setting up the default configuration for global authentication manager.

AuthenticationConfiguration configures two key pieces to make the authentication manager - user details and authentication provider.

User details is configured using InitializeUserDetailsBeanManagerConfigurer and authentication provider is configured using InitializeAuthenticationProviderBeanManagerConfigurer. Both of required beans are looked up in application context - that is how your user detail service is registered with global authentication manager.

GlobalMethodSecurityConfiguration and WebSecurityConfigurerAdapter are consumers of global authentication managers.

WebSecurityConfigurerAdapter can be used to create and configure local authentication manager (add new authentication providers) and also typically used to have different authentication/authorization requirements in application like mvc vs rest and public vs admin endpoints.

With spring security alone @EnableWebSecurity triggers the above flow as part of spring security filter chain set up. With spring boot the same flow is triggered by spring security auto configuration.

In spring security 5.4 version you can define http security as beans without needing to extend WebSecurityConfigurerAdapter class. Spring boot will have support for this in 2.4.0 release. More details here

    @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();
}

答案3

得分: 0

你正在使用 @Service 注解,该注解会在组件扫描时创建 UserDetailsService 的 Bean。在 AuthenticationManagerBuilder 中再次指定它是不必要的。

如果你不使用 @Service 注解,那么你可以通过覆盖 WebSecurityConfigurerAdapter 中的 AuthenticationManagerBuilder 来手动配置它。

英文:

You are using the @Service annotation which creates the bean of the UserDetailsService at the time of the component scan. There is no need to specify it again in the AuthenticationManagerBuilder.

If you don't use the @Service annotation, then you can configure it manually in the WebSecurityConfigurerAdapter by over-riding the AuthenticationManagerBuilder.

答案4

得分: 0

要完全关闭默认的Web应用程序安全配置,您可以添加一个带有@EnableWebSecurity注解的Bean,如Spring Boot文档(第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

要完全关闭默认的Web应用程序安全配置,您可以添加一个带有@EnableWebSecurity注解的Bean(这不会禁用身份验证管理器配置或Actuator的安全性)。要自定义它,通常使用外部属性和类型为WebSecurityConfigurerAdapter的Bean(例如,要添加基于表单的登录)。

如果您添加了@EnableWebSecurity并禁用了Actuator安全性,除非添加自定义的WebSecurityConfigurerAdapter,否则将为整个应用程序获取默认的基于表单的登录。

如果在应用程序的任何地方定义了带有@EnableWebSecurity@Configuration,它将关闭Spring Boot中的默认Web应用程序安全设置(但保留Actuator的安全性)。要调整默认值,请尝试在security.*中设置属性(有关可用设置的详细信息,请参阅SecurityProperties)以及常见应用程序属性的SECURITY部分。

英文:

To switch off the default web application security configuration completely you can add a bean with @EnableWebSecurity as explained in the spring boot documentation (section 4.10.1. MVC Security),

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

The @EnableWebSecurity is a marker annotation. It allows Spring to find (it's a @Configuration and, therefore, @Component) and automatically apply the class to the global 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.

答案5

得分: 0

不,实现UserDetailsService接口并不等同于重写configure(AuthenticationManagerBuilder auth)方法。

如果你通过重写loadUserByUsername()方法来重写UserDetailsService并验证用户名和密码,在你的情况下是静态值(我建议对于静态用户,请使用inMemoryAuthentication)。

你需要自动装配UserDetailsService:

@Autowired
UserDetailsService userDetailsService;

以及:

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

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

英文:

No, implementing the UserDetailsService interface is not equivalent to overriding the configure(AuthenticationManagerBuilder auth).

If you override UserDetailsSeervice and verify the username and password by override loadUserByUsername(), in your case it is static values(I would recommend for static users use inMemoryAuthentication).

You need to Autowired UserDetailsService

@Autowired
UserDetailsService userDetailsService;

And

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

this will tell your authenticationManager to use userDetailsService which is been implemented for authentication.

答案6

得分: 0

I want to know if implementing the UserDetailsService interface is equivalent with overriding the configure(AuthenticationManagerBuilder auth).

不,它们不能进行比较。

> UserDetailsService 是一个核心接口,用于加载特定于用户的数据。它在整个框架中被用作用户的数据访问对象(DAO),并且是 DaoAuthenticationProvider 使用的策略。*

> AuthenticationManagerBuilder 允许轻松地构建内存中身份验证、基于 JDBC 的身份验证、添加 UserDetailsService,以及添加 AuthenticationProvider

因此,当您使用 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(); //一个提供者
	provider.setUserDetailsService(userDetailsService); //用户详细信息服务
	provider.setPasswordEncoder(encoder()); //您还可以添加密码编码器
	
	return provider;
}

而且 YourUserDetailServiceImpl 必须覆盖 loadUserByUsername() 方法以获取用户详细信息。

@Override
public UserDetails loadUserByUsername(String email) {
	final Account acc = accRepository.findByEmail(email);
	if (acc == null)
		throw new UsernameNotFoundException("账户未找到");
	
	//这可以是您选择的扩展了 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()));
}
英文:

I want to know if implementing the UserDetailsService interface is equivalent with overriding the configure(AuthenticationManagerBuilder auth).

No, they are not comparable.

> 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. *

and

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

So it is evident that when you use UserDetailsService, it means you are using DaoAuthenticationProvider for fetching user details from your underlying database.

NOTE: AuthenticationProvider is an abstraction for fetching user information from different sources/repositories and validates if the retrieved information is similar to the one provided by users.

Let's see an example, the configuration looks like this:

@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;
}

And YourUserDetailServiceImpl has to override loadUserByUsername() to fetch used details.

@Override
public UserDetails loadUserByUsername(String email) {
final Account acc = accRepository.findByEmail(email);
if (acc == null)
throw new UsernameNotFoundException(&quot;Account not found&quot;);
//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 -&gt; new SimpleGrantedAuthority(&quot;ROLE_&quot; + role.getName()))
.collect(Collectors.toList()));
}

huangapple
  • 本文由 发表于 2020年10月26日 00:55:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/64526372.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定