I created very simple site where everybody can access /about, authenticated users can access /profile and only user with "ADMIN" role can access. I added one "ADMIN" user in the DBInit.java
Then I try to access /admin and I get an http basic login form. I enter adminEmail as login and admin123 as password and I can't access /admin page. So somewhere is an error in my code, and I can see it. So where is an error and how to get rid of it?
If I use in memory authentication everything works well.
// In memory authentication
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
// auth.authenticationProvider(authenticationProvider());
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN");
}
SecurityConfiguration.java
import Onlinestore.service.UserPrincipalDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private UserPrincipalDetailsService userPrincipalDetailsService;
public SecurityConfiguration(UserPrincipalDetailsService userPrincipalDetailsService)
{
this.userPrincipalDetailsService = userPrincipalDetailsService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
{
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("/about").permitAll()
.antMatchers("/profile").authenticated()
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.httpBasic();
}
@Bean
DaoAuthenticationProvider authenticationProvider()
{
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(userPrincipalDetailsService);
return daoAuthenticationProvider;
}
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
}
User.java
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
@Entity(name = "user")
@Table(name = "users")
@NoArgsConstructor
public class User
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Getter
@Setter
private int id;
@Getter
@Setter
@Column(nullable = false)
private String name;
@Getter
@Setter
private String surname;
@Getter
@Setter
@Column(nullable = false)
private String password;
@Getter
@Setter
@Column(name = "telephone_number", nullable = false)
private String telephoneNumber;
@Getter
@Setter
@Column(unique = true)
private String email;
@Getter
@Setter
private String country;
@Getter
@Setter
private String address;
@Getter
@Setter
// delimiter = ";"
private String roleNames;
public User(String name, String surname, String password, String telephoneNumber, String email, String country, String address, String roleNames)
{
this.name = name;
this.surname = surname;
this.password = password;
this.telephoneNumber = telephoneNumber;
this.email = email;
this.country = country;
this.address = address;
this.roleNames = roleNames;
}
}
UserPrincipal.java
import Onlinestore.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class UserPrincipal implements UserDetails
{
private User user;
public UserPrincipal(User user)
{
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
String[] roles = user.getRoleNames().split(";");
List<GrantedAuthority> authorities = new ArrayList<>();
for (String role : roles)
{
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
@Override
public String getPassword()
{
return user.getPassword();
}
@Override
public String getUsername()
{
return user.getName();
}
@Override
public boolean isAccountNonExpired()
{
return false;
}
@Override
public boolean isAccountNonLocked()
{
return false;
}
@Override
public boolean isCredentialsNonExpired()
{
return false;
}
@Override
public boolean isEnabled()
{
return false;
}
}
UserPrincipalDetailsService.java
import Onlinestore.entity.User;
import Onlinestore.repository.UserRepository;
import Onlinestore.security.UserPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserPrincipalDetailsService implements UserDetailsService
{
private UserRepository userRepository;
public UserPrincipalDetailsService(UserRepository userRepository)
{
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
User user = userRepository.findUserByEmail(username);
return new UserPrincipal(user);
}
}
DBInit.java
import Onlinestore.entity.User;
import Onlinestore.repository.UserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
@Service
public class DBInit implements CommandLineRunner
{
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
public DBInit(UserRepository userRepository, PasswordEncoder passwordEncoder)
{
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public void run(String[] args)
{
User user1 = new User("admin", "admin", passwordEncoder.encode("admin123"),
" 111111111", "adminEmail", "country1", "address1", "ADMIN");
List<User> users = Arrays.asList(user1);
userRepository.saveAll(users);
}
}
UserRepository
import Onlinestore.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Integer>
{
User findUserByEmail(String email);
}
CodePudding user response:
In memory authentication expects hard coded credentials in the configuration. It does not pull credentials from the database.
If you want to use credentials from a database, the rest of your setup looks good at first glance. Try
httpSecurity
.authorizeRequests()
.antMatchers(PUBLIC_MATCHERS)
.permitAll()
.anyRequest()
.authenticated();
Where PUBLIC_MATCHERS
is an array of endpoints that do not require authentication, if you have such a thing.
CodePudding user response:
I has found an error. In class UserPrincipal
I need to override this methods and return true
, but not return false
. I think when I return false
Spring Security thinks the user is blocked, expired, disabled and so on and blocks authentication.
This but there also some errors and application yet not working as I wanted to.
Proper implementation of methods in UserPrincipal
@Override
public boolean isAccountNonExpired()
{
return true;
}
@Override
public boolean isAccountNonLocked()
{
return true;
}
@Override
public boolean isCredentialsNonExpired()
{
return true;
}
@Override
public boolean isEnabled()
{
return true;
}