How to Configure Passwords With Spring Security
Learn more about the authentication architecture within the Spring Framework.
Join the DZone community and get the full member experience.
Join For FreeIn this article, we will be discussing how to configure passwords with Spring Security and explore the authentication architecture of the Spring Framework.
Spring Security is used for securing a web application API developed in Spring and is comprised of both authentication and authorization components.
Authentication is all about who should get access to the API and who should not, whereas authorization is all about which API can gain access.
Authentication in Spring can be done in two ways, in terms of user password configurations:
- Using the database to store the password
- Using an active directory like LDAP to store the password
When the user information is stored in a database, Spring provides two ways to authenticate:
UserDetailService
JDBCAuthentication
When the user information is stored in some active directory like LDAP, we can use Spring LDAP authentication.
Authentication Manager and Provider in Spring Security
Authentication manager is the interface that provides the authentication mechanism for any object. The most common implementation of it is the AuthenticationProvider
. AuthenticationProvider
is similar to the AuthenticationManager
interface but it has an extra method, support()
, which checks whether the object supports the authentication type or not.
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
Sometimes, we have a logical group of the section in the application, which needs to be authenticated differently. And each group is going to have a dedicated AuthenticationManager
Spring Security provides a way of configuring the AuthenticationManager
with the help of a AuthenticationManagerBuilder
class, which is used for setting up authentication using JDBC, LDAP, or a customUserDetailsService
.
The following examples can be found here.
Configuring Passwords
For simplicity purposes, I have used the h2 database and a single table to hold the user role and password. In a real-life scenario, the role should be part of a different table.
USER table
user_id |
name |
password |
role |
joy |
Joydip |
123 |
ROLE_USER |
admin |
Admin |
8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 |
ROLE_ADMIN |
Password is sha256 encoded.
You will need to create a data.sql for loading the insertion query on server startup to the h2 database:
insert into user (user_id, name,password,role) values ('joy','Joydip','A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3','ROLE_USER')
insert into user (user_id, name,password,role) values ('admin','admin','8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918','ROLE_ADMIN')
Create a configuration class, as shown below:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
// Various authentication to follow
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//various authorizations to follow
}
}
Then, make these changes in the pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
@EnableWebSecurity
is the marker annotation that tells Spring to apply the configuration class to the global WebSecurity. Adding the spring-boot-starter-security jar itself brings multiple Spring Security jars in the classpath, which, itself, will enable the security to display an out-of-the-box Spring Security login page.
JDBC Authentication
Update the configure method to enable JDBC authentication:
@Override
publicvoid configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery("select user_id,password, 'true' as enabled from user where user_id=?")
.authoritiesByUsernameQuery("select user_id, role from user where user_id=?").passwordEncoder(new ShaPasswordEncoder(256));
}
Custom UserDetailsService
Next, add the Spring data JPA library in the pom.xml file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Then, you will create a custom UserRepository
:
@Transactional
public interface UserRepository extends JpaRepository<User, String> {
}
Then, you need to create a SecureUser
that extends the User
class and implements the UserDetails
to handle roles and set the User ID and password.
public class SecuredUser extends User implements UserDetails {
private static final long serialVersionUID = 1L;
public SecuredUser(User user){
this.setUserId(user.getUserId());
this.setName(user.getName());
this.setPassword(user.getPassword());
this.setRole(user.getRole());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
//TODO Currently handled with one role for an user
String userRole = super.getRole();
if(userRole != null)
{
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(userRole);
authorities.add(authority);
}
return authorities;
}
@Override
public String getUsername() {
return super.getUserId();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Next, you will create a UserDetailsServiceImpl
, an implementation of the UserDetailsService
:
@Component
public class UserDetailServiceImpl implements UserDetailsService {
@Resource
UserRepository userrepository;
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
User user=userrepository.findOne(userId);
return new SecuredUser(user) ;
}
}
Next, update the configure class, as shown below:
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailServiceImpl).passwordEncoder(new ShaPasswordEncoder(256));
}
LDAP Authentication
LDAP authentication is where credentials are stored in the active directory. We will use a sample active directory from Spring. The server will be set up in the 8389 port.
First, add the following in the pom.xml:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>
Then, add the LDAP configuration in the application.properties file:
spring.ldap.embedded.ldif=classpath:test-server.ldif
spring.ldap.embedded.base-dn=dc=springframework,dc=org
spring.ldap.embedded.port=8389
Then, you will have created a test-server.LDIF file in the resources folder, as shown below:
dn: dc=springframework,dc=org
objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: springframework
dn: ou=roles,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: roles
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Admin1
sn: Admin1
uid: admin
userPassword: admin
dn: uid=joy,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: joydip
sn: Kumar
uid: joy
userPassword: 123
#ROLES
dn: cROLE_USERER,ou=roles,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cnROLE_USERER
member: uid=joy,ou=people,dc=springframework,dc=org
dn: cROLE_n=ADMIN,ou=roles,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cnROLE_ADMININ
member: uid=admin,ou=people,dc=springframework,dc=org
Next, we need to update the configure method:
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=roles")
.contextSource().url("ldap://localhost:8389/dc=springframework,dc=org").and()
.passwordCompare().passwordAttribute("userPassword");
}
This is how it behaves in an application for all three Spring authentications of the same user and credentials:
Conclusion
Spring Security can be configured in a project based on how the user credentials are set up in the system. After reading this article, we hope you learned which method to use and when.
Happy coding!
Published at DZone with permission of Joydip Kumar, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments