You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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预检请求,这是问题核心。

修复步骤

  1. 完善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();
    }
}
  1. 添加跨域(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);
    }
}
  1. 修正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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 17:05:31