UserDetailsService approach

Another approach to fetch use and role information is using the UserDetailsService interface. It is an abstract way to fetch authentication and authorization details. It has one method—loadUserByUsername() that will return user details based on username. You can override it and write your own logic to retrieve the user details.

Spring provides a class called DaoAuthenticationProvider that basically uses the UserDetailsService implementation to fetch user details during the authentication process. The flexibility in this approach means we can define a custom method to fetch user details. We will define a method in the JPA repository for the User entity. The JPA is a standard way of interacting with a relational database with Java objects. The repository looks as follows:

@Repository
interface UserRepository : JpaRepository<User, Int> {
fun findByUsername(username: String): User?
}

UserRepository is a Kotlin interface that extends the Java JpaRepository interface. The @Repository annotation is used to declare this interface as a JPA repository. The findByUsername method is a query method, which will fetch the user. Spring Data JPA has an inbuilt query building mechanism, based on the repository method name. 

For the findByUsername method, it will first remove the findBy prefix and compose the query from the rest of the method name. In this case, it will internally create a query, such as select * from users where username=?. This method returns an object of the User entity class. Next, we need to provide a custom user service, and for that we will implement UserDetailsService as follows: 

@Service
class CustomUserDetailsService : UserDetailsService {

@Autowired
private val userRepository: UserRepository? = null

@Throws(UsernameNotFoundException::class)
override fun loadUserByUsername(username: String): UserDetails {
val user = userRepository?.findByUsername(username) ?:
throw UsernameNotFoundException(username)
return CustomUserPrinciple(user)
}
}

The CustomUserDetailsService class is declared with a @Service annotation to declare it as a service component. It overrides a loadUserByUsername() method, where we can write custom logic to fetch user details. A repository findByUsername() method that we have created is used here to fetch user details.

The return type is UserDetails that is an interface that actually stores user information, which is then encapsulated to authenticate objects later. We have created a CustomUserPrinciple class to provide an implementation of UserDetails as follows:

class CustomUserPrinciple : UserDetails {
constructor(user: User?) {
this.user = user
}
private var user:User? = null

override fun isEnabled(): Boolean {
return true
}
override fun getUsername(): String {
return this.user?.getUsername() ?: ""
}
override fun isCredentialsNonExpired(): Boolean {
return true
}
override fun getPassword(): String {
return this.user?.getPassword() ?: ""
}
override fun isAccountNonExpired(): Boolean {
return true
}
override fun isAccountNonLocked(): Boolean {
return true
}
override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
var userRoles:Set<Role>? = user?.getRoles() ?: null
var authorities:MutableSet<GrantedAuthority> = HashSet<GrantedAuthority>()
for(role in userRoles.orEmpty()){
authorities.add(CustomGrantedAuthority(role))
}
return authorities
}

}

The UserDetails interface requires the implementation of certain methods as follows:

  • isEnable(): This method basically returns if the user is activated or not. In a practical scenario, there must be a separate database column to check if the user is enabled or not. For simplicity, we simply return true  assuming that all users are enabled. If the user returns false, Spring Security will not allow login.
  • getUsername(): This simply returns the username.
  • isCredentialsNonExpired(): This is a very useful method when you want to impose a constraint on the user to update the password after a certain time limit. In this method, you need to check whether the password has expired, based on your requirement, and return the value accordingly. For simplicity, if we return true , it means password has not expired.
  • getPassword(): It should return the password.
  • isAccountNonExpired(): This indicates whether a user account has expired or not. To make it simple, we just return true.
  • isAccountNonLocked(): This is used to check whether a user account is locked. Again, for simplicity, we a just return true.
  • getAuthorities(): This method returns authorities granted to the user. We retrieve roles from a user object and wrap them in a GrantedAuthority type. The GrantedAuthority is an interface. We have provided an implementation through the CustomGrantedAuthority class as follows:
class CustomGrantedAuthority : GrantedAuthority{
private var role:Role?=null
constructor( role:Role ){
this.role = role
}
override fun getAuthority(): String {
return role?.getRole() ?: ""
}
}
  • We are injecting a user object through the constructor, which can be used to retrieve further details in each of these methods.

The last part is to define the Spring Security configuration. Add methods to the WebSecurityConfig class as follows:

@Throws(Exception::class)
override fun configure(auth: AuthenticationManagerBuilder?) {
auth!!.authenticationProvider(authenticationProvider())
}

@Bean
fun authenticationProvider(): DaoAuthenticationProvider {
val authProvider = DaoAuthenticationProvider()
authProvider.setUserDetailsService(userDetailService)
authProvider.setPasswordEncoder(passwordEncoder())
return authProvider
}

The authenticationProvider() method simply creates an object of the DaoAuthenticationProvider type, configure it with user detail service object and password encoder and return. It is then used in the configure() method to set as the authentication provider. The object of UserDetailService can be injected in the same class as follows:

@Autowired
private var userDetailService: CustomUserDetailsService? = null

This approach is more flexible in terms of allowing a customized way to fetch user details, which are then used by Spring to perform various security constraints. It simply decouples the logic of authentication and authorization from the mechanism to fetch user details. This makes the system more flexible.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.114.125