Я работаю над проектом весенний ботинок, который включает лист тимьяна, пружинная безопасность. Он отлично работает, когда я выполняю - показ списка продуктов, отображение сведений о продуктах, добавление нового продукта, обновление существующего продукта.
Но когда я выполняю удаление продукта, выдает следующую ОШИБКУ:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Jul 18 16:59:16 BDT 2019
There was an unexpected error (type=Forbidden, status=403).
Forbidden
Вот мой код:
product_list.html
<!DOCTYPE html>
<html xmlns:th = "http://www.thymeleaf.org">
<head>
<title>Product List</title>
<link rel = "stylesheet" th:href = "@{/css/bootstrap.css}">
</head>
<body>
<div class = "container">
<h1>Product List</h1>
<hr>
<a class = "btn btn-success" th:href = "@{/products/add}">Create New Product</a>
<hr>
<table class = "table">
<thead>
<tr>
<th>Product ID</th>
<th>Name</th>
<th>Brand</th>
<th>Made In</th>
<th>Price</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr th:each = "theProduct:${theProducts}">
<td th:text = "${theProduct.id}">Product ID</td>
<td th:text = "${theProduct.name}">Name</td>
<td th:text = "${theProduct.brand}">Brand</td>
<td th:text = "${theProduct.madein}">Made In</td>
<td th:text = "${theProduct.price}">Price</td>
<td>
<a class = "btn btn-info" th:href = "@{'/products/show/' + ${theProduct.id}}">View</a>
<a class = "btn btn-warning" th:href = "@{'/products/edit/' + ${theProduct.id}}">Edit</a>
<a class = "btn btn-danger" th:data-the-product-id = "${theProduct.id}" data-toggle = "modal" data-target = "#deleteConfirmationModal">Delete</a>
<div class = "modal fade" id = "deleteConfirmationModal" tabindex = "-1" role = "dialog" aria-labelledby = "deleteConfirmationModalLabel" aria-hidden = "true">
<div class = "modal-dialog" role = "document">
<div class = "modal-content">
<div class = "modal-header">
<h5 class = "modal-title" id = "exampleModalLabel">Delete Confirmation</h5>
<button type = "button" class = "close" data-dismiss = "modal" aria-label = "Close">
<span aria-hidden = "true">×</span>
</button>
</div>
<div class = "modal-body">
Are you sure you want to DELETE the Product.
<br>
<form id = "deleteForm" action = "#" th:method = "DELETE">
<button type = "submit" class = "btn btn-danger">Delete Employee</button>
</form>
</div>
<div class = "modal-footer">
<button type = "button" class = "btn btn-secondary" data-dismiss = "modal">Close</button>
</div>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<script th:src = "@{/js/jquery-3.3.1.js}"></script>
<script th:src = "@{/js/popper.js}"></script>
<script th:src = "@{/js/bootstrap.js}"></script>
<script>
$('#deleteConfirmationModal').on('show.bs.modal', function (event) {
var anchorLink = $(event.relatedTarget)
var theProductId = anchorLink.data('theProductId')
var modal = $(this)
$("#deleteForm").attr("action", "/products/delete/" + theProductId)
})
</script>
</body>
</html>
Продуктконтроллер.java
package com.example.demo.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.dao.ProductRepository;
import com.example.demo.entity.Product;
@Controller
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping("/index")
public String index(Model theModel) {
List<Product> theProducts = productRepository.findAll();
theModel.addAttribute("theProducts", theProducts);
return "product/product_list";
}
@GetMapping("/add")
public String add(Model theModel) {
Product theProduct = new Product();
theModel.addAttribute("theProduct", theProduct);
return "product/product_add_form";
}
@GetMapping("/show/{productId}")
public String show(@PathVariable int productId, Model theModel) {
Product theProduct = productRepository.findById(productId).get();
if (theProduct == null) {
return null;
}
theModel.addAttribute("theProduct", theProduct);
return "product/product_detail";
}
@PostMapping("/create")
public String create(@ModelAttribute("theProduct") Product theProduct) {
theProduct.setId(0);
productRepository.save(theProduct);
return "redirect:/products/index";
}
@GetMapping("/edit/{productId}")
public String edit(@PathVariable(name = "productId") int productId, Model theModel) {
Product theProduct = productRepository.findById(productId).get();
theModel.addAttribute("theProduct", theProduct);
return "product/product_edit_form";
}
@PutMapping("/update")
public String update(@ModelAttribute("theProduct") Product theProduct) {
productRepository.save(theProduct);
return "redirect:/products/index";
}
@DeleteMapping("/delete/{productId}")
public String delete(@PathVariable int productId) {
Product tempProduct = productRepository.findById(productId).get();
if (tempProduct == null) {
return null;
}
productRepository.deleteById(productId);
return "redirect:/products/index";
}
}
Логинконтроллер.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/login")
public String login() {
return "login";
}
@GetMapping("/")
public String home(Model theModel) {
return "redirect:/products/index";
}
}
SecurityConfig.java
package com.example.demo.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
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.security.core.userdetails.User;
import org.springframework.security.core.userdetails.User.UserBuilder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication().withUser(users.username("admin").password("Admin.123").roles("EMPLOYEE", "ADMIN"));
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**")
.permitAll()
.antMatchers("/js/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout()
.permitAll();
}
}




Попробуйте отключить токен csrf в вашей конфигурации:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**")
.permitAll()
.antMatchers("/js/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout()
.permitAll()
.and().csrf().disable();
}
Я полагаю, проблема в том, что вы пропустили th:href для кнопки удаления.
к счастью, вы добавляете «модальный» внутри тимелеафа в каждом цикле, поэтому отредактируйте свой html в соответствии с моим кодом....
<form id = "deleteForm" th:action = "${'/products/delete/' + theProductId}" th:method = "DELETE">
<button type = "submit" class = "btn btn-danger">Delete Employee</button>
</form>
это проверенный код...работает нормально..
вы просто пропустите «th: action», потому что он не работает как форма тимьяна. поэтому тимелеаф не предоставляет скрытое поле «_csrf»;
Спасибо, это сработало. На самом деле я добавил th:action = "${'/products/delete/' + theProductId}" в начале. Но я сделал что-то не так в JavaScript и удалил th:action. А потом решил проблему с JavaScript, но забыл добавить th:action :p
вы правы... я проверяю его код... но csrf очень важен из соображений безопасности.... даже для целей тестирования