Je travaille sur un projet spring-boot qui comprend thymeleaf , spring-security . Cela fonctionne bien lorsque j'exécute - afficher la liste des produits, afficher les détails des produits, ajouter un nouveau produit, mettre à jour le produit existant.
Mais lorsque j'effectue - supprimer un produit, cela donne l'ERREUR suivante:
Page d'erreur de marque blanche
Cette application n'a pas de mappage explicite pour / error, donc vous voyez cela comme une solution de secours.
Jeu 18 juil 16:59:16 BDT 2019
Une erreur inattendue s'est produite (type = Forbidden, status = 403).
Interdit
Voici mon code:
product_list.html
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(); } }
ProductController.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"; } }
LoginController.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"; } }
SecurityConfig.java
<!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>
3 Réponses :
Essayez de désactiver le jeton csrf
dans votre configuration:
@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(); }
vous avez raison .... je teste son code ..... mais csrf est très important pour des raisons de sécurité .... même à des fins de test
Je suppose que le problème est que vous avez manqué th: href pour le bouton de suppression.
heureusement, vous ajoutez "modal" à l'intérieur de la feuille de thymel pour chaque boucle, alors modifiez votre code HTML en fonction de mon code ....
<form id="deleteForm" th:action="${'/products/delete/' + theProductId}" th:method="DELETE"> <button type="submit" class="btn btn-danger">Delete Employee</button> </form>
c'est du code testé ... fonctionne bien ..
vous manquez simplement "th: action" pour que cela ne fonctionne pas sous forme de feuille de thymel. donc la dose de thymeleaf ne fournit pas de champ "_csrf" caché;
Merci, cela a fonctionné. En fait, j'ai ajouté th: action = "$ {'/ products / delete /' + theProductId}"
au début. Mais j'ai fait quelque chose de mal dans JavaScript, puis j'ai supprimé th: action
. Et puis résolu le problème JavaScript mais j'ai oublié d'ajouter le th: action
: p