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

Posted

技术标签:

【中文标题】从 Angular 客户端到 Spring 引导服务器的简单 POST 请求给出状态 403 错误【英文标题】:Simple POST request from Angular client to Spring boot server gives status 403 error 【发布时间】:2021-08-15 22:43:51 【问题描述】:

当我尝试从 Angular 客户端向我的 Spring Boot 服务器执行简单的 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 请求的服务类...

服务

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 Boot 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 引导服务器没有接收到请求。

我的代码有什么问题?

【问题讨论】:

您是否附上了正确的标题? spring boot 端的日志是什么?您应该提供日志以找到解决方案。 spring boot端没有log。我确实添加了调试点,但它没有被触发。 您是否尝试过使用 postman 之类的 Spring Boot 应用程序?是否按预期工作? 我以前没有使用过 Postman,但感谢您让我了解该工具。之前我使用 Python pip 之类的东西来检查我的请求... 【参考方案1】:

检查安全配置我认为有问题。

【讨论】:

【参考方案2】:

我必须对我的安全配置进行一项更改。 我确实禁用了 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("noop123")
                .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;
    


【讨论】:

以上是关于从 Angular 客户端到 Spring 引导服务器的简单 POST 请求给出状态 403 错误的主要内容,如果未能解决你的问题,请参考以下文章

我无法向自己的 Spring 引导服务器发出 Angular Http 请求

将 Blob 数据作为 MultipartFile 从 Angular6 客户端发送到 Spring Boot API

Angular 从 Spring RestController 获取图像并缓存它

Spring Boot + Angular 5 - http.post 空值

从 Node Js 重定向到 Spring 引导服务并发送回响应

如何强制 Angular cli 从绝对位置而不是 base-href 提供捆绑文件