I'm implementing SecurityFilterChain and setting it up so that only admins have access to that endpoint. When I log in with the admin account, it doesn't allow me access.
My code
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class EndToEndSecurityDemo{
private final EndToEndUserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests((auth) -> auth
.requestMatchers(
new AntPathRequestMatcher("/"),
new AntPathRequestMatcher("/login"),
new AntPathRequestMatcher("/error"),
new AntPathRequestMatcher("/registration/**")
)
.permitAll()
.requestMatchers(
new AntPathRequestMatcher("/users/**")
)
.hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin((login)-> login
.loginPage("/login")
.usernameParameter("email")
.defaultSuccessUrl("/")
.permitAll()
)
.logout((logout)-> logout
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
)
.csrf(withDefaults())
.httpBasic(withDefaults())
.build();
}
}
@Data
public class EndToEndUserDetails implements UserDetails {
private String userName;
private String password;
private boolean isEnabled;
private List<GrantedAuthority> authorities;
public EndToEndUserDetails(User user) {
this.userName = user.getEmail();
this.password = user.getPassword();
this.isEnabled = user.isEnabled();
this.authorities =
Arrays.stream(user.getRoles().toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
@Service
@RequiredArgsConstructor
public class EndToEndUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.map(EndToEndUserDetails::new)
.orElseThrow(()-> new UsernameNotFoundException("User not found"));
}
}
At registration, I set the user as a 'user'.
@Service
@RequiredArgsConstructor
public class UserService implements IUserService{
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final VerificationTokenService verificationTokenService;
@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}
@Override
public User registerUser(RegistrationRequest registration) {
var user = new User(registration.getFirstName(), registration.getLastName(),
registration.getEmail(),
passwordEncoder.encode(registration.getPassword()),
Arrays.asList(new Role("USER")));
return userRepository.save(user);
}
@Override
public Optional<User> findByEmail(String email) {
return Optional.ofNullable(userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found")));
}
@Override
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
@Transactional
@Override
public void updateUser(Long id, String firstName, String lastName, String email) {
userRepository.update(firstName, lastName, email, id);
}
@Transactional
@Override
public void deleteUser(Long id) {
Optional<User> theUser = userRepository.findById(id);
theUser.ifPresent(user -> verificationTokenService.deleteUserToken(user.getId()));
userRepository.deleteById(id);
}
}
In the database, I have the tables 'user', 'role', and 'user_role'. I changed the name of the role to 'ADMIN' and restarted the application, but it's still the same.
You should pass the role with the prefix ROLE_
to new SimpleGrantedAuthority(). Example: ROLE_ADMIN
Unfortunately, you did not provide an example of the Role class, so I cannot see how you have implemented it. Please check that the prefix is present.