Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configure Basic Auth using UserDetailsService for multiple uses in application properties

I've multiple users which I want to configure into spring-boot's UserDetailsService for basic authentication.

User has a additional field id tagged with it.

import org.springframework.security.core.userdetails.UserDetails;

public class User implements UserDetails {
    private final String username;
    private final String password;
    private final String id;

    private static final String ROLE_USER = "ROLE_USER";

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(ROLE_USER);
        return Stream.of(simpleGrantedAuthority).collect(Collectors.toList());
    }

    // Getter & setter
}

Properties yml looks like:

basic-auth:
    oliver:
        password: twist
        id: 1
    ruskin:
        password: bond
        id: 2
    mark:
        password: twain
        id: 3

In UserDetailsService, I'm not sure how to configure users using application properties dynamically.

public class UserService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) {

        String encodedPassword = passwordEncoder.encode( // How to fetch password );

        String id = // How to fetch id

        return new User(username, encodedPassword, id);
    }
}
like image 415
rahul s Avatar asked Oct 21 '25 11:10

rahul s


2 Answers

You can do this in different ways.

For example, let's assume your Basic Authentication properties YAML file, with minimum tweaks for simplicity:

basic-auth:
    users:
        oliver:
            password: twist
            id: 1
        ruskin:
            password: bond
            id: 2
        mark:
            password: twain
            id: 3

To make this information accessible to your UserDetailsService service you can use encapsulate this information in form of @ConfigurationProperties, something like the following:

@Component
@ConfigurationProperties(prefix = "basic-auth")
public class BasicAuthProperties {
  private Map<String, Credential> users = new HashMap<>();

  // Getter & setter

  // Convenient method
  public Optional<Credential> getCredential(String username) {
    Optional.ofNullable(users.get(username)); 
  }

  public static class Credential {
        
    private int id;
    private String password;
        
    // Getter & setter
        
  }
}

And use this @ConfigurationProperties in your UserDetailsService:

public class UserService implements UserDetailsService {

  private final BasicAuthProperties basicAuthProperties;

  public UserService(BasicAuthProperties basicAuthProperties) {
    this.basicAuthProperties = basicAuthProperties;
  }

  @Override
  public UserDetails loadUserByUsername(String username) {
    Optional<BasicAuthProperties.Credential> optCredential = basicAuthProperties.getCredential(username);
    if (!optCredential.isPresent()) {
      // handle as appropriate, consider at least logging it
      return null;
    }

    BasicAuthProperties.Credential credential = optCredential.get();
    String encodedPassword = passwordEncoder.encode(credential.getPassword());
    // I assume int, the value is numeric in your YAML file, but modify it
    // if necessary
    int id = credential.getId();
    return new User(username, encodedPassword, id);
  }
}
like image 71
jccampanero Avatar answered Oct 23 '25 01:10

jccampanero


This is my solution for Basic Authentication with yaml from scratch.

1.You need to put this file to the resource folder

appUserList.yml:

constants:
appUserList:
- userId: 1
  userName: oliver
  password: twist
  role: USER

- userId: 2
  userName: ruskin
  password: bond
  role: USER

- userId: 3
  userName: mark
  password: twain
  role: ADMIN

2.You need to make an AppUser class:

public class AppUser {

    private String userId;
    private String userName;
    private String password;
    private String role;

    public AppUser() {
    }

    public AppUser(String userId, String userName, String password, String role) {
        this.userId = userId;
        this.userName = userName;
        this.password = password;
        this.role = role;
    }

    //Getter and Setters
}

3.You need to make a YamlPropertySourceFactory class:

public class YamlPropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(encodedResource.getResource());

        Properties properties = factory.getObject();

        return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
    }
}

4.Now you can use the yaml file and the other two classes to create the AppUserProperties class:

@Component
@PropertySource(value = "classpath:appUserList.yml", factory = YamlPropertySourceFactory.class)
@ConfigurationProperties(prefix = "constants")
public class AppUserProperties {

    private List<AppUser> appUserList;

    public List<AppUser> getAppUserList() {
        return appUserList;
    }

    public void setAppUserList(List<AppUser> appUserList) {
        this.appUserList = appUserList;
    }
}

5.Now you need to make a SecurityConf class where you will get the users with this method: appUserProperties.getAppUserList(). If you want use other identifiers for the users, just create the parameters in the AppUser class.

@EnableGlobalMethodSecurity(securedEnabled = true)
@Configuration
public class SecurityConf extends WebSecurityConfigurerAdapter {

    @Autowired
    AppUserProperties appUserProperties;

    @Autowired
    public void configureAuth(AuthenticationManagerBuilder auth) throws Exception {

        List<AppUser> appUserList = appUserProperties.getAppUserList();

        for (AppUser appUser : appUserList) {
            auth
                    .inMemoryAuthentication()
                    .withUser(appUser.getUserName())
                    .password(appUser.getPassword())
                    .roles(appUser.getRole());
        }
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .logoutSuccessUrl("/login?logout")
                .permitAll();
    }

}

These are all the important steps for it to work.

Otherwise, here is the fully working code: https://github.com/FerencHrutka/Basic-Auth-with-yaml-file

I hope this answers your question.

like image 35
Ferenc Hrutka Avatar answered Oct 23 '25 00:10

Ferenc Hrutka



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!