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);
}
}
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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With