从 Angular 客户端到 Spring 引导服务器的简单 POST 请求给出状态 403 错误

Simple POST request from Angular client to Spring boot server gives status 403 error

当我尝试从 Angular 客户端向我的 Spring 引导服务器执行一个简单的 POST 请求时,我收到此错误 403。

以下代码是HTML模板...

HTML模板

<!-- Create book html -->
<div class="content-container">
  <div class="content-area">
  
    <!-- Back button -->
    <div> 
      <button class="btn btn-danger btn-block" (click)="back()">Back</button>
    </div>

    <p style="font-weight: bold; color: green;">Please enter all the details in-order to enter a new book!!!</p><br/>

    <p>{{book | json}}<p>

    <form (ngSubmit)="onCreate()">
      <!-- Book name -->
      <label for="name"><b>Book name:</b></label><br/>
      <input type="text" name="name" class="formField" size="100" placeholder="Ex: Bram Stoker's Dracula" [(ngModel)]="book.name"/><br/><br/>
    
      <!-- Author ID -->
      <label for="authorid"><b>Author ID:</b></label><br/>
      <input type="text" name="authorid" class="formField" size="50" placeholder="Ex: 100015" [(ngModel)]="book.authorID"/><br/><br/>

      <!-- Publication ID -->
      <label for="publicationid"><b>Publication ID:</b></label><br/>
      <input type="text" name="publicationid" class="formField" size="50" placeholder="Ex: 200015" [(ngModel)]="book.publicationID"/><br/><br/>
      
      <!-- Publication date -->
      <label for="publicationdate"><b>Publication Date:</b></label><br/>
      <input type="date" name="publicationdate" class="formField" [ngModel]="book.publicationDate | date:'yyyy-MM-dd'" (ngModelChange)="book.publicationDate = $event"/><br/><br/>
      
      <!-- Description -->
      <label for="description"><b>Book Description:</b></label><br/>
      <textarea name="description" class="formField" rows="3" cols="100" placeholder="Describe about the book here..." [(ngModel)]="book.description"></textarea><br/><br/>

      <!-- Edition -->
      <label for="edition"><b>Book Edition:</b></label><br/>
      <input type="text" name="edition" class="formField" size="50" placeholder="Ex: 5" [(ngModel)]="book.edition"/><br/><br/>

      <!-- Category -->
      <label for="category"><b>Book Category:</b></label><br/>
      <input type="text" name="category" class="formField" size="50" placeholder="Ex: 3" [(ngModel)]="book.category"/><br/><br/>

      <!-- Rating -->
      <label for="rating"><b>Book Rating:</b></label><br/>
      <input type="text" name="rating" class="formField" size="50" placeholder="Ex: 10" [(ngModel)]="book.rating"/><br/><br/>
      
      <div style="text-align:center; width:100%;">
         <button type="submit" class="btn btn-primary btn-block">Create</button>
     </div>
    </form>

  </div>
</div>

以下代码是组件...

组件

import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { Book } from '../services/data';
import { Router } from '@angular/router';
import { BookService } from '../services/book.service';

//Create book componenet 

@Component({
  selector: 'app-createbook',
  templateUrl: './createbook.component.html',
  styleUrls: ['./createbook.component.css']
})
export class CreatebookComponent implements OnInit {
  //Attributes
  book = {
    bookID: 0,
    name: '',
    authorID: 0,
    publicationID: 0,
    publicationDate: new Date,
    description: '',
    edition: 0,
    category: 0,
    rating: 0
  };

  //Constructor of the CreatebookComponent. 
  constructor(private location: Location, private router: Router, private bookService: BookService) { }

  //Component life cycle hook. Executes once componenet initiated. 
  ngOnInit(): void { }
  
  //Executes on book create. 
  onCreate() {
      this.bookService.createBook(this.book);
  }

  //Responsible for handling the back mechanism. 
  back() {
      this.location.back();
  }

}

以下代码是执行 POST 请求的服务 class...

服务

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Router } from '@angular/router';
import { Book } from './data';

//Service for books in library client 

@Injectable()
export class BookService{
  //Attributes 
  recentBooks: Array<Book> = []; 
  createdBook: Book; 

  //Constructor of BookService 
  constructor(private httpClient: HttpClient, private router: Router) { }
  
  //Return the recent book array. 
  get(): Book[] {
      return this.recentBooks;
  }
  
  //Return recent books. 
  getRecentBooks() {
      return this.httpClient.get<Array<Book>>("http://localhost:8080/book/recents");//.subscribe(book => this.recentBooks=book);
      //return this.recentBooks;
  }
  
  //Responsible for creating given book. 
  createBook(theBook: any): void {
      this.httpClient.post("http://localhost:8080/book/create",
                           theBook,
                          { headers: new HttpHeaders().set("Content-type", "application/json") }).subscribe(result => this.redirectHome());
  }
  
  //Redirect to home page 
  redirectHome() {
      this.router.navigate(['/home']);
  }
  
}

以下代码显示 spring 引导 REST 控制器代码...

REST 控制器

package com.example.LibraryServer.RestControllers;

import com.example.LibraryServer.Entities.Book;
import com.example.LibraryServer.Repositories.BookRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
 * REST controller for books.
 */
@Slf4j //Lombok annotation for logger
@RestController
@RequestMapping(path = "/book", produces = "application/json")
@CrossOrigin(origins = "http://localhost:4200") //Cross-origin resource sharing
public class BookController {

    private final BookRepository bookRepo;

    /**
     * Constructor of BookController.
     * @param theBookRepository BookRepository
     */
    @Autowired
    public BookController(BookRepository theBookRepository) {
        this.bookRepo = theBookRepository;
    }

    /**
     * Responsible for returning recent books.
     * Returns maximum 12 most recently created books.
     * @return Iterable<Book>
     */
    @GetMapping("/recents")
    public Iterable<Book> recentBooks() {
        log.info("*****LibrarySystem LOGGER***** @GetMapping(/recents) -> REST endpoint executed");
        return bookRepo.recent();
    }

    /**
     * Responsible for returning the book for the given book ID.
     * @param theBookID int
     * @return ResponseEntity<Book>
     */
    @GetMapping("/{id}")
    public ResponseEntity<Book> getBook(@PathVariable("id") int theBookID) {
        log.info("*****LibrarySystem LOGGER***** @GetMapping(/{id}) -> REST endpoint executed");
        Book book = bookRepo.getByID(theBookID);
        if (book != null) {
            return new ResponseEntity<>(book, HttpStatus.OK);
        }
        else {
            return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
        }
    }

    /**
     * Responsible for deleting the book for the given book ID.
     * @param theBookID int
     */
    @DeleteMapping("/{id}")
    public void delete(@PathVariable("id") int theBookID) {
        log.info("*****LibrarySystem LOGGER***** @DeleteMapping(/{id}) -> REST endpoint executed");
        bookRepo.delete(theBookID);
    }

    /**
     * Responsible for persisting the given book.
     * @param theBook Book
     * @return Book
     */
    @PostMapping(path = "/create", consumes = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public Book save(@RequestBody Book theBook) {
        log.info("*****LibrarySystem LOGGER***** @PostMapping -> REST endpoint executed");
        return bookRepo.save(theBook);
    }

}

错误

Spring 引导服务器未收到请求。

我的代码有什么问题?

我必须对我的安全配置进行一项更改。
我确实禁用了 CSRF(跨站点请求伪造),这阻止了我的 POST 请求。

package com.example.LibraryServer.Security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

/**
 * Class responsible for security configurations.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * Responsible for user security configuration.
     * Overridden from WebSecurityConfigurerAdapter level.
     * @param theHttpSecurity HttpSecurity
     * @throws Exception - Exception upon security configuration.
     */
    @Override
    protected void configure(HttpSecurity theHttpSecurity) throws Exception {
        theHttpSecurity.authorizeRequests()
                .antMatchers("/**").access("permitAll")
                .and().cors().and().csrf().disable();
    }

    /**
     * Responsible for configuring user-store.
     * Overridden from WebSecurityConfigurerAdapter level.
     * @param theAuthentication AuthenticationManagerBuilder
     * @throws Exception - Exception upon user store creation.
     */
    @Override
    public void configure(AuthenticationManagerBuilder theAuthentication) throws Exception {
        theAuthentication.inMemoryAuthentication()
                .withUser("sankalpa")
                .password("{noop}123")
                .authorities("ROLE_USER");
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}

检查安全配置我认为出了点问题。