Issue
I have a simple PUT request, which saves a book in a row in a local h2 database. Using postman, when I send the request first, before another, it returns me a 401 Error - with no data. Screenshot:1: . But if I execute the request after another (a GET Request, which executes successfully), the PUT request return a 403 forbidden error. I use a local h2 in-memory database and I populate it with this code (I also set up a schema - otherwise it would not recognize the table name):
DROP TABLE IF EXISTS BOOKS;
CREATE TABLE BOOKS(
isbn LONG AUTO_INCREMENT PRIMARY KEY,
count_books INT NOT NULL,
author VARCHAR(30) NOT NULL,
name VARCHAR(30) NOT NULL UNIQUE,
description VARCHAR(250)
);
INSERT INTO BOOKS (count_books, author, name, description) VALUES
(2,'Иван Вазов', 'Под Игото', 'В малко градче пристига странник и им показва значението на свободата'),
(4,'Тютюн', 'Димитър Димов', 'История за човешки характери, поквара и любов на фона на ВСВ.'),
(6,'Клетниците', 'Виктор Юго', 'Разтърсваща история за човешкия падеж и неговото възстановяване.');
As for the Rest API, I use Spring Boot, with Web, H2, PostGre, and OAuth 2.0 (in future). Here is my Service class (acts as a DAO):
package Library.demo.dao;
import Library.demo.entities.Books;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.LinkedList;
@Service
public class BooksDAOImpl {
@Autowired
BookRepository bookRepository;
public LinkedList<Books> get_all_books() {
LinkedList<Books> books = new LinkedList<>();
for(Books book : bookRepository.findAll()) {
books.add(book);
}
System.out.println(bookRepository.count());
return books;
}
public void addBook_admin(int count, String name, String author, String description){
Books book = new Books(count,name, author,description);
bookRepository.save(book);
System.out.println(bookRepository.count());
}
}
Here is my REST Controller class for adding books to the database:
package Library.demo.command;
import Library.demo.dao.BooksDAOImpl;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.exception.JDBCConnectionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import java.util.InputMismatchException;
@RestController
public class Add_book_admin_command {
@Autowired
BooksDAOImpl bookDAO;
@PutMapping("/books/add")
public void execute(@RequestParam int count_books, @RequestParam String author, @RequestParam String name, @RequestParam String description) {
try {
bookDAO.addBook_admin(count_books, author, name, description);
}catch (InputMismatchException ime){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Wrong type of information");
}catch (JDBCConnectionException jdbcConnectionException){
throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "Error connecting to database");
}catch (NonUniqueObjectException objectException){
throw new ResponseStatusException(HttpStatus.NOT_ACCEPTABLE, "Book already exists");
}
}
}
Here is my controller for getting all books:
package Library.demo.command;
import Library.demo.dao.BooksDAOImpl;
import Library.demo.entities.Books;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.LinkedList;
@RestController
public class List_all_books_admin_command {
@Autowired
BooksDAOImpl bookDAO;
@GetMapping("/books/all")
public LinkedList<Books> execute() {
return bookDAO.get_all_books();
}
}
And my my application.settings file:
spring.datasource.url=jdbc:h2:mem:test;
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.jpa.hibernate.ddl-auto=none
security.basic.enable= false
security.ignored=/**
My entity class is Books :
package Library.demo.entities;
import javax.persistence.*;
@Entity(name = "BOOKS")
public class Books {
@Id
@GeneratedValue
private long isbn;
private int count_books;
private String author;
private String name;
private String description;
public Books( int count_books, String author, String name, String description) {
this.count_books = count_books;
this.author = author;
this.name = name;
this.description = description;
}
public Books() {
}
public long getIsbn() {
return isbn;
}
public void setIsbn(int isbn) {
this.isbn = isbn;
}
public int getCount() {
return count_books;
}
public void setCount(int count) {
this.count_books = count;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The program builds just fine and there are no exceptions in the console output, even when the PUT request fails. I am quite new to the Spring Framework, so any help would be appreciated :)
Solution
Answer - Switch off the csrf via the WebSecurityConfig class. Code snippet:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception{
http.cors().and().csrf().disable();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("*"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
That should fix the problem
Answered By - Jordan Baller