I recently asked a question very similar to this one but instead of 401 the error I was getting was 403 (Forbbiden), but I changed the entire code so I decided to post a new one specific to this code and this problem.
I'm trying to create an user logic to my project (for the first time ever) but it has been impossible to implement any kind of security measure. I've been stuck in this for days so if anyone knows where I'm wrong I'd be grateful!
this is my code:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/users/create", "/users/create/**").permitAll()
.and()
.httpBasic();
}
}
@Data
@Component
public class CreateUserRoleDTO {
private Integer idUser;
private List<Integer> idsRoles;
public CreateUserRoleDTO() {
super();
}
public CreateUserRoleDTO(Integer idUser, List<Integer> idsRoles) {
super();
this.idUser = idUser;
this.idsRoles = idsRoles;
}
public Integer getIdUser() {
return idUser;
}
public void setIdUser(Integer idUser) {
this.idUser = idUser;
}
public List<Integer> getIdsRoles() {
return idsRoles;
}
public void setIdsRoles(List<Integer> idsRoles) {
this.idsRoles = idsRoles;
}
}
@Service
public class CreateRoleUserService {
@Autowired
private UserRepository repo;
@Autowired
private CreateUserRoleDTO createUserRoleDTO;
public Users execute(CreateUserRoleDTO createUserRoleDTO) {
Optional<Users> userExists=repo.findById(createUserRoleDTO.getIdUser());
List<Roles> roles=new ArrayList<>();
if (userExists.isEmpty()) {
throw new Error("User does not exist");
}
roles=createUserRoleDTO.getIdsRoles().stream().map(role -> {
return new Roles(role);
}).collect(Collectors.toList());
Users user=userExists.get();
user.setRole(roles);
repo.save(user);
return user;
}
@Entity
@Table(name="users_table")
public class Users implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(unique=true)
private String login;
@Column(unique=true)
private String email;
private String password;
@ManyToMany
private List<Roles> role;
}
(plus the getters and setters and constructors)
data.sql
:
INSERT INTO `ROLES`(`ID`, `NAME`) VALUES(1, 'USER');
INSERT INTO `ROLES`(`ID`,`NAME`) VALUES(2, 'ADMIN');
-> the code runs fine, it even gives me the security password, the problem appears when I try to make any kind of requests.
The entire code if I've left anything out: https://github.com/vitoriaacarvalho/backend-challenge-very-useful-tools-to-remember-
An authentication configuration is missing in your SecurityConfig
. For example, try adding the following to your configure
method:
http.httpBasic();
Additionally, your security configuration is missing a default authorization rule, so authentication is not actually required. You can try adding .anyRequest().authenticated()
to test this out.
Here's a configuration which uses the lambda syntax available in the DSL and is ready to be upgraded to Spring Security 6:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/users/create", "/users/create/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
// Disable CSRF for testing.
// TODO: Delete the following line and learn about CSRF!
http.csrf().disable();
return http.build();
}
@Bean // Automatically injected into Spring Security
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Note: We don't configure a UserDetailsService since it is already
// annotated @Service and therefore already published as an @Bean.
}
Unfortunately, I also spotted a few other mistakes in your application that made it not work.
It looks like you have a mistake in the JPQL used to query the user for the UserDetailsService
. The WHERE clause should be where u.login = :username
(add a u.
).
You also have the if-statement inverted as well. When throwing a UsernameNotFoundException
(a better exception than Error
for this case), it would look like:
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = repo.findByUsernameFetchRoles(username);
if (user == null) {
throw new UsernameNotFoundException("User does not exist!");
}
return UserPrincipal.create(user);
}
Lastly, the constructor of your Users
class was not assigning user data from the user
parameter. It should be:
public UserPrincipal(Users user) {
this.login = user.getLogin();
this.password = user.getPassword();
...
}
With those changes, authentication works and you're on your way to learning Spring Security!