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

Spring Boot POST请求403权限错误排查与解决方法

问题:Spring Boot POST请求返回403无访问权限

我正在开发一个Spring Boot简易项目,但在创建POST请求时遇到问题:每次请求都会返回403错误(请求载荷payload正确,仅状态码异常,提示“无访问权限”)。我甚至尝试配置.permitAll(),但问题仍未解决。

Fig. 1 - Error Messages

相关代码

前端JS代码

// Creates a new event
const newEvent = {
     id: null,
     title: title,
     start: start,
     end: end,
     allDay: allDay,
     backgroundColor: backgroundColor,
     textColor: textColor,
     extendedProps: {
          description: description,
          creator: creator
     }
};

// Sends event to backend
fetch('/api/events/add', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(newEvent)
})
.then(response => {
    if (!response.ok) {
         throw new Error('Error saving the event.');
    }
    return response.json();
})
.then(data => {
    // Add the event to the calendar
    newEvent.id = data.id;
    gereralCalendar.addEvent(newEvent);
    personalCalendar.addEvent(newEvent);
})
.catch(error => {
    console.error('Error:', error);
});

后端Controller

@RestController
@RequestMapping("/api/events")
public class EventRestController {
    @Autowired
    EventService eventService;

    @Autowired
    UserService userService;

    @GetMapping("/all")
    public List<EventDto> getAllEvents() {
        return eventService.findAllEvents();
    }

    @GetMapping("/personal")
    public List<EventDto> getPersonalEvents(Principal principal) {
        return eventService.findEventsUser(principal.getName());
    }

    @PostMapping("/add")
    public ResponseEntity<Event> createEvent(@RequestBody EventDto eventDto) {
        Event savedEvent = eventService.saveEvent(eventDto);
        return ResponseEntity.ok(savedEvent);
    }
}

EventDto实体

@Getter
@Setter
public class EventDto {
    private int id;
    private String title;
    private Boolean allDay;
    private LocalDateTime start;
    private LocalDateTime end;
    private String backgroundColor;
    private String textColor;
    private ExtendedProps extendedProps;

    @Getter
    @Setter
    public static class ExtendedProps {
        private String description;
        private String creator;

        // Constructors
    }
    // Constructors
}

Spring Security配置

// I HAVE TWO ROLES -> USER and ADMIN -> hierarchy = "ROLE_ADMIN > ROLE_USER"
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests((authorize) ->
                    authorize
                            .requestMatchers("/").permitAll() // nothing
                            .requestMatchers("/index").permitAll() // index.html
                            .requestMatchers("/home").hasRole("USER") // home.html
                            .requestMatchers("/calendar").hasRole("USER") // events_calendar.html
                            .requestMatchers("/register/**").hasRole("ADMIN") // register.html
                            .requestMatchers("/api/events/all").hasRole("USER") // GET API
                            .requestMatchers("/api/events/personal").hasRole("USER") // GET API
                            .requestMatchers("/api/events/add").hasRole("USER") // POST API
            )
            .formLogin(
                    form -> form
                            .loginPage("/login")
                            .loginProcessingUrl("/login")
                            .defaultSuccessUrl("/home") // home.html
                            .permitAll()
            )
            .logout(
                    logout -> logout
                            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                            .permitAll()
            );
    return http.build();
}

问题排查与解决方案

1. CSRF防护拦截(最可能原因)

Spring Security默认开启CSRF防护,POST请求如果未携带CSRF令牌会被直接拦截返回403。当前配置未关闭CSRF,前端请求也未携带令牌。

  • 临时调试方案(仅用于排查,生产环境禁用):
    在Security配置中添加CSRF关闭逻辑:
    http.csrf(csrf -> csrf.disable());
    
  • 生产环境正确方案:
    1. 通过模板引擎(如Thymeleaf)在页面注入CSRF令牌:
      <meta name="_csrf" content="${_csrf.token}"/>
      <meta name="_csrf_header" content="${_csrf.headerName}"/>
      
    2. 在fetch请求中携带令牌头:
      const csrfToken = document.querySelector('meta[name="_csrf"]').content;
      const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content;
      
      fetch('/api/events/add', {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json',
              [csrfHeader]: csrfToken
          },
          body: JSON.stringify(newEvent)
      })
      

2. 请求匹配与权限验证

  • 检查requestMatchers("/api/events/add").hasRole("USER")路径是否拼写正确,确保和前端请求路径完全一致。
  • hasRole("USER")会自动添加ROLE_前缀,确认你的用户角色实际为ROLE_USER(而非仅USER)。
  • 可临时修改权限配置为.permitAll()测试是否为权限规则问题:
    .requestMatchers("/api/events/add").permitAll()
    

3. 用户认证状态确认

确保发起请求的用户已完成登录,且拥有USER角色。可在后端createEvent方法中打印Principal信息确认用户身份:

@PostMapping("/add")
public ResponseEntity<Event> createEvent(@RequestBody EventDto eventDto, Principal principal) {
    System.out.println("当前登录用户:" + principal?.getName());
    Event savedEvent = eventService.saveEvent(eventDto);
    return ResponseEntity.ok(savedEvent);
}

4. 参数绑定验证

检查EventDto是否有无参构造函数,Spring需要无参构造函数完成JSON到对象的绑定。若绑定失败可能引发间接异常,可在方法中打印eventDto确认参数是否正确接收。

内容的提问来源于stack exchange,提问作者Diogo Freire

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:35:01