SpringBoot接口经WebApp调用时触发403 Forbidden问题求助
问题描述
我被这个问题困扰两天了:我有一个集成Spring Security的简易SpringBoot应用,用Swagger和Postman测试控制器接口一切正常,但通过前端WebApp调用同一接口时,却抛出403 Forbidden错误,相关日志如下:
2022-10-07T21:43:51.991+0800 DEBUG http-nio-8080-exec-1 (FilterChainProxy.java:323) - Secured OPTIONS /category/all 2022-10-07T21:43:51.993+0800 DEBUG http-nio-8080-exec-1 (LogFormatUtils.java:119) - OPTIONS "/category/all", parameters={} 2022-10-07T21:43:51.995+0800 DEBUG http-nio-8080-exec-1 (PropertySourcedRequestMappingHandlerMapping.java:108) - looking up handler for path: /category/all 2022-10-07T21:43:51.998+0800 DEBUG http-nio-8080-exec-1 (AbstractHandlerMapping.java:522) - Mapped to com.edar.sales.be.controller.CategoryController#getAllCategories() 2022-10-07T21:43:52.002+0800 DEBUG http-nio-8080-exec-1 (OpenEntityManagerInViewInterceptor.java:86) - Opening JPA EntityManager in OpenEntityManagerInViewInterceptor 2022-10-07T21:43:52.015+0800 DEBUG http-nio-8080-exec-1 (HttpSessionSecurityContextRepository.java:346) - Did not store anonymous SecurityContext 2022-10-07T21:43:52.018+0800 DEBUG http-nio-8080-exec-1 (OpenEntityManagerInViewInterceptor.java:111) - Closing JPA EntityManager in OpenEntityManagerInViewInterceptor 2022-10-07T21:43:52.019+0800 DEBUG http-nio-8080-exec-1 (FrameworkServlet.java:1131) - Completed 403 FORBIDDEN
Controller代码
package com.edar.sales.be.controller; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import org.apache.commons.beanutils.BeanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.edar.sales.be.dto.CategoryDTO; import com.edar.sales.be.entity.Category; import com.edar.sales.be.service.CategoryService; import com.google.gson.Gson; @RestController(value = "category/") public class CategoryController { private static final Logger LOG = LoggerFactory.getLogger(CategoryController.class); private static final Gson GSON = new Gson(); @Autowired CategoryService categoryService; @GetMapping(value = "category/all") public List<CategoryDTO> getAllCategories() throws IllegalAccessException, InvocationTargetException { List<CategoryDTO> retval = new ArrayList<>(); List<Category> categories = categoryService.getAllCategories(); for (Category category : categories) { CategoryDTO categoryDTO = new CategoryDTO(); BeanUtils.copyProperties(categoryDTO, category); retval.add(categoryDTO); } return retval; } @GetMapping(value = "category/{id}") public CategoryDTO getCategoryById(@PathVariable("id") long id) throws IllegalAccessException, InvocationTargetException { CategoryDTO categoryDTO = new CategoryDTO(); BeanUtils.copyProperties(categoryDTO, categoryService.getCategoryById(id)); return categoryDTO; } @PostMapping(value = "category/delete/{id}") public void deleteCategoryById(@PathVariable("id") Long id) { categoryService.deleteCategoryById(id); } @PostMapping(value = "category/add") public void addCategory(@RequestBody Category category) { LOG.debug("Adding category : {}", GSON.toJson(category)); categoryService.addCategory(category); } @PatchMapping(value = "category/update") public void updateCategory(@RequestBody Category category) { LOG.debug("Updating category : {}", GSON.toJson(category)); categoryService.addCategory(category); } }
SecurityConfig配置代码
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests().antMatchers("**").permitAll(); } }
解决方案
从日志能看到前端发起的是OPTIONS请求,虽然已关闭CSRF并允许所有路径访问,但Spring Security默认会拦截跨域场景下的OPTIONS预检请求,这是问题核心。
修复步骤
- 完善Security配置,明确放行OPTIONS方法
修改SecurityConfig,添加对OPTIONS方法的明确放行:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() // 允许所有OPTIONS预检请求 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers("/**").permitAll(); } }
- 添加跨域(CORS)配置
前端跨域调用必须配置CORS,否则浏览器会拦截响应。两种配置方式可选:
方式一:Controller级别添加注解
在CategoryController类上添加跨域注解:
@RestController(value = "category/") @CrossOrigin(origins = "*", allowedHeaders = "*") public class CategoryController { // ... 原有代码 }
方式二:全局CORS配置
创建全局配置类统一处理跨域:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS") .allowedHeaders("*") .allowCredentials(true); } }
- 修正Controller路径配置
当前@RestController的value = "category/"和请求映射中的category/前缀重复,导致实际接口路径为/category/category/all,可能和前端调用路径不匹配。建议修正为:
@RestController @RequestMapping("/category") public class CategoryController { // 去掉请求映射中的重复前缀 @GetMapping("/all") public List<CategoryDTO> getAllCategories() throws IllegalAccessException, InvocationTargetException { // ... 原有代码 } }
修正后实际接口路径为/category/all,和前端调用路径一致,避免路径不匹配引发的权限问题。
内容的提问来源于stack exchange,提问作者NothingBox
相关产品推荐
相关产品推荐

