I am working on a Spring Boot 1.4.1 application (that implements some REST web services) and I am finding some difficulties trying to implement Spring Security into this project.
I have the following situation:
1) I have the CustomUserDetails that implements the Spring Security UserDetails interface:
import com.betrivius.domain.User;
import com.betrivius.domain.UserRole;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class CustomUserDetails extends User implements UserDetails {
private static final long serialVersionUID = 1L;
public CustomUserDetails(User user){
super(user);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for(UserRole role : this.getUserRoles() ){
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName());
authorities.add(grantedAuthority);
}
return authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getUsername() {
return super.getUsername();
}
}
2) Then I have the CustomUserDetailsService implementing the Spring Security UserDetailsService interface:
import com.betrivius.dao.UserDAO;
import com.betrivius.domain.User;
import com.betrivius.security.bean.CustomUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
@Autowired
private UserDAO userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("No user present with username: "+username);
}else{
return new CustomUserDetails(user);
}
}
}
3) Finnally I have the Spring Security confinguration class named WebSecurityConfig extending the WebSecurityConfigurerAdapter:
import com.betrivius.security.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/Accomodation/**").access("hasRole('ROLE_USER')")
.anyRequest().permitAll()
.and()
.csrf().disable();
/*.and()
.formLogin().loginPage("/login")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling().accessDeniedPage("/403")
.and()
.csrf();*/
}
@Bean(name = "passwordEncoder")
public PasswordEncoder passwordencoder() {
return new BCryptPasswordEncoder();
}
}
As you can see in the previous code snippet in the configuration class I have specified that only a user having the ROLE_USER setted can access to all the resources under the /Accomodation/ path, I have done it by:
.antMatchers("/Accomodation/**").access("hasRole('ROLE_USER')")
4) This is the UserDAO that obtain the user information used into the previous CustomUserDetailsService class:
@Repository
@Transactional(propagation = Propagation.MANDATORY)
public interface UserDAO extends JpaRepository<User, Long> {
User findByUsername(String username);
@Query("SELECT r FROM User u JOIN u.userRoles r where u.username = :username")
List<UserRole> findRoleByUserName(String username);
}
The username and password are stord in the DB and are:
USERNAME: ErSabba
PASSWORD: pswd
User table have a many to many relationship with User_Roles, the role of ErSabba user is ROLE_USER (tried setting USER as role).
So, by PostMan Chrome plugin I perform a GET Request toward the URL:
http://localhost:8080/Accomodation/7
passing the previous credential in this way:
As you can see the problem is that inserting the right username and password into the basic authentication form it give me 403 error Access denied.
I think that it means that I have perform the authentication but I have not the authorization.
The strange thing is that I also tried to put bad credential and I always obtain the same instead of 401 Bad Credential error.
I also have put a breakpoint on this line of the loadUserByUsername() method into the CustomUserDetailsService class (that retrieve the User info).
User user = userDao.findByUsername(username);
Running in debug mode it doesn't stop on this line so it seems that Spring Security configuration doesn't works fine...
What could be the problem? What am I missing? How can I try to fix it?