I started to deal with spring security and there was such a problem. That in the browser, get and delete requests can be performed by logging in, but the save method cannot (because a body is needed). I use postman for save, but even after filling in the basic auth data, I still get the authorization page.
Maybe I didn't set up the security config file correctly?
Here is the controller:
@RestController
@RequestMapping("/product")
@Slf4j
public class ProductController {
private final ProductRepository productRepository;
@Autowired
public ProductController(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@GetMapping("/products")
public List<Product> getAllProducts() {
log.info("Receiving all products");
return productRepository.findAll();
}
@GetMapping("/getProduct/{id}")
public Product getProductById(@PathVariable Long id) {
log.info("Receiving product by id");
return productRepository.getReferenceById(id);
}
@PostMapping("/save")
public Product saveProduct(@RequestBody Product product) {
log.info("Save products");
return productRepository.save(product);
}
@DeleteMapping("/remove/{id}")
public void deleteProduct(@PathVariable Long id) {
Product product = productRepository.getReferenceById(id);
log.info("delete product by id");
if (product != null) {
productRepository.delete(product);
}
}
}
Here is the security config:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("user1Pass"))
.roles(UserRole.USER.name())
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin1Pass"))
.roles(UserRole.USER.name())
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz
.requestMatchers("/product/save").hasRole(UserRole.ADMIN.name())
.requestMatchers("/product/remove/**").hasRole(UserRole.ADMIN.name())
.requestMatchers("/product/products").hasAnyRole(UserRole.ADMIN.name(), UserRole.USER.name())
.requestMatchers("/product/getProduct/{id}").permitAll()
.requestMatchers("/product").permitAll()
.anyRequest().authenticated()
).formLogin(withDefaults());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Here is authorization and basic auth in postman:
I tried adding .httpBasic(Customizer.withDefaults()) and in the end all methods work with basic auth, but the post(save in my case) method gives an empty string and a 401 error.
Please try this. I mean to remember that I had the same challenge. And remove .formLogin(withDefaults());
.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz
// your definitions
);
http.httpBasic(); // <<--
http.csrf().disable(); // <<--
return http.build();
}