Custom WebSecurityConfigurerAdapter

I am having trouble implementing custom login authentication using SpringBoot and SpringBoot-Security. I created a Bitbucket repository as a link for this stream ( under CustomSecuringWeb ). First of all, most of the comments here should Protect your web application .

The fact is, I was curious how authentication data is now from the database, and not just memory data (which is very common in production line applications).

Throughout the process, I made two attempts (although both attempts are located in the same branch - for me this is bad).

  • Created a custom implementation of UserDetailsService
  • Created a custom implementation of AbstractUserDetailsAuthentictionProvider

I don’t know where the problem is, but when checking the console log, both return a constant (even repository) DI for each user class, where null.

The question is how can I get both attempts to work. And (possibly) which one of the two attempts is better than the other.

+6
source share
3 answers

First of all, both approaches are used for different purposes and are not interchangeable.

Case 1:

UserDetailsService used exclusively as a DAO to search for user information about your authentication and, based on this authenticated user, authentication within the UserDetailsService does not require authentication, but only access to data. Specifications clearly indicate this. This is what you are looking for.

Case 2:

AuthentictionProvider , on the other hand, is used to provide a custom authentication method, for example, if you want to configure authentication in fields other than the username and password that you can do by implementing AuthentictionProvider and providing this object to your AuthenticationManagerBuilder . I do not think that this is what you want to do in the project. You are simply trying to implement your authentication based on users stored in the database using the username and password, which are used by default. In the above example, Case 1 , where you implemented only a UserDetailsService , an AuthentictionProvider instance was created for you in the AuthenticationManager container, and it was a DaoAuthenticationProvider , because you provided a UserDetailsService, which is nothing more than a DAO on your system that is used to fetch the user for authentication.

Now to your implementation, in your configuration instead:

  @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // auth.userDetailsService(new AdminSecurityService()); auth.authenticationProvider(new AdminSecurityAuthenticationProvider()); } 

do something like that

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

and your CustomUserDetailsService should implement org.springframework.security.core.userdetails.UserDetailsService

 @Service public class CustomUserDetailsService implements UserDetailsService { private final AdminRepository userRepository; @Autowired public CustomUserDetailsService(AdminRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Admin user = userRepository.findByLogin(username); if (user == null) { throw new UsernameNotFoundException(String.format("User %s does not exist!", username)); } return new UserRepositoryUserDetails(user); } private final static class UserRepositoryUserDetails extends Admin implements UserDetails { private static final long serialVersionUID = 1L; private UserRepositoryUserDetails(User user) { super(user); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return AuthorityUtils.createAuthorityList("ROLE_USER"); } @Override public String getUsername() { return getLogin();//inherited from user } @Override public boolean isAccountNonExpired() { return true;//not for production just to show concept } @Override public boolean isAccountNonLocked() { return true;//not for production just to show concept } @Override public boolean isCredentialsNonExpired() { return true;//not for production just to show concept } @Override public boolean isEnabled() { return true;//not for production just to show concept } //getPassword() is already implemented in User.class } } 

Of course, the implementation is up to you, but you should be able to provide the user password and other methods in this interface based on the received user (Admin.class in your case). Hope it helps. I did not run this example, so if I made some typos, and ask if something is working. I would also get rid of this "AuthentictionProvider" from your project if you do not need it.

Here you will get documentation: http://docs.spring.io/spring-security/site/docs/4.0.0.RC1/reference/htmlsingle/#tech-userdetailsservice

After comments: You can install PasswordEncoder in your configuration method without any hassle:

  @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Bean public PasswordEncoder passwordEncoder(){ PasswordEncoder encoder = new BCryptPasswordEncoder(); return encoder; } 

This can be done because you are accessing the AbstractDaoAuthenticationConfigurer returned from auth.userDetailsService(userDetailsService) and it allows you to configure the DaoAuthenticationProvider , which is your provider of choice, when you decide to use UserDetailsService . You're right. PasswordEncoder set to AuthenticationProvider , but you don't need to implement AuthenticationProvider just use the convineince object that comes back from auth.userDetailsService(userDetailsService) and set your encoder on that object, which will pass it to AuthenticationPriovider in your case, DaoAuthenticationProvider , which was already created for you. Just as the roadrunner mentioned in the comments, you very rarely need to implement your own AuthenticationProvider , usually most authentication configuration settings can be done using AbstractDaoAuthenticationConfigurer , which, as mentioned above, is returned from auth.userDetailsService(userDetailsService) .

"And if I ever wanted to add password encryption. And if I ever wanted to do another authentication (for example, check if the user is locked, the user is active, is still registered, etc. [excluding password hashing] ) will use AuthenticationProvider. "

No, this is done for you as part of the standard authentication mechanism http://docs.spring.io/autorepo/docs/spring-security/3.2.0.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetails.html If you look at the UserDetails interface, you will see that if any of the above methods returns, then false authentication will fail. The implementation of AuthenticationProvider really necessary in very unusual cases. All standard materials are heavily coated.

+10
source

In JDBC mode http://www.mkyong.com/spring-security/spring-security-form-login-using-database/ basically you should specify a query to retrieve users.

+1
source

First, I would advise you to read about Basic String Security Services .

The key in this situation is the AuthenticationManager , which is responsible for deciding whether or not to authenticate the user. This is what you configure with AuthenticationManagerBuilder . The primary implementation in Spring ProviderManager , which allows you to define multiple authentication mechanisms in a single application. The most common use case is one, but this class is still being processed. Each of these multiple authentication mechanisms is represented by a different AuthenticationProvider . ProviderManager accepts a list of AunthenticationProviders iterating through them to find out if any of them can authenticate the user.

What interests you is DaoAuthenticationProvider . As the name implies, it allows you to use the data access object to authenticate the user. It uses the standard interface for such a DAO - UserDetailsService . Spring Security has a default implementation, but it's usually the bit that you want to implement yourself. Everything else is provided.

Also, the configuration bit you need is completely independent of Spring Boot. Here's how you do it in XML:

 <sec:authentication-manager > <sec:authentication-provider user-service-ref="myUserDetailsService" /> </sec:authentication-manager> 

And in Java it will be:

 @Configuration @EnableWebSecurity public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService myUserDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService); } } 

According to UserDetails implementation commonly provided by Spring Security . But you can also implement your own if necessary.

You will usually also need PasswordEncoder . Good one, for example BCryptPasswordEncoder :

 @Configuration @EnableWebSecurity public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder); } } 

Note that this is a @Bean , so you can @Autowire in your UserRepository encode user passwords when they are stored in the database.

+1
source

Source: https://habr.com/ru/post/980974/


All Articles