未使用Spring Security时AJAX调用Spring MVC接口遇403禁止错误
解决OPTIONS请求403错误 + 自定义权限控制方案
我来帮你一步步拆解问题:先搞定OPTIONS预请求的403坑,再实现不用Spring Security的自定义权限控制。
一、先解决OPTIONS请求403的问题
这个403大概率是跨域预检请求(OPTIONS)未被后端正确放行导致的。浏览器发送跨域的POST/GET这类请求前,会先发OPTIONS请求做预检,如果后端没配置允许OPTIONS通过,就会直接返回403。
1. 配置全局跨域(推荐)
写个Spring Boot配置类,实现WebMvcConfigurer,明确把OPTIONS加入允许的请求方法:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 对所有接口生效 .allowedOriginPatterns("*") // 生产环境记得改成具体域名,别用通配符 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 必须包含OPTIONS .allowedHeaders("*") .allowCredentials(true) // 如果你需要带cookie或者token,开启这个 .maxAge(3600); // 预检请求缓存1小时,减少重复OPTIONS请求 } }
2. 检查自定义拦截器/过滤器
如果你自己写了拦截器或者过滤器,一定要跳过OPTIONS请求的权限校验,直接放行:
比如拦截器里这么改:
public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 是OPTIONS请求就直接过,别拦 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } // 其他请求再走你的权限校验逻辑 // ...你的自定义校验代码 } }
过滤器的逻辑类似,先判断请求方法是OPTIONS的话,直接调用filterChain.doFilter放行即可。
二、实现不用Spring Security的权限控制
不用Spring Security的话,我们可以用自定义注解+拦截器的组合来做细粒度权限控制,步骤很清晰:
1. 写个权限注解
先定义一个注解,用来标记接口需要哪些权限:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RequirePermission { String[] value(); // 比如{"admin", "user:edit"},支持多个权限 }
2. 实现权限校验拦截器
在拦截器里,拿到当前登录用户的权限,和接口注解要求的权限对比:
@Component public class PermissionInterceptor implements HandlerInterceptor { // 假设你有个工具类能拿到当前登录用户的权限,比如UserContext @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 还是先跳过OPTIONS请求 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } // 判断是不是Controller的方法 if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; RequirePermission requirePermission = handlerMethod.getMethodAnnotation(RequirePermission.class); // 接口没加权限注解,直接放行 if (requirePermission == null) { return true; } // 获取当前用户的权限列表(这里要你自己实现,比如从session、token里取) Set<String> userPermissions = UserContext.getCurrentUserPermissions(); // 检查用户有没有注解要求的权限 String[] requiredPermissions = requirePermission.value(); for (String perm : requiredPermissions) { if (userPermissions.contains(perm)) { return true; } } // 没权限的话,返回403和自定义提示 response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setContentType("application/json;charset=utf-8"); response.getWriter().write("{\"code\":403,\"msg\":\"抱歉,你没有访问权限\"}"); return false; } return true; } }
3. 注册拦截器
把刚才的权限拦截器加到Spring的拦截器链里,在之前的CorsConfig里加一段:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Autowired private PermissionInterceptor permissionInterceptor; // 跨域配置... @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(permissionInterceptor) .addPathPatterns("/**") // 对所有接口生效 .excludePathPatterns("/login", "/register"); // 排除登录、注册这些不需要权限的接口 } }
4. 在接口上用注解
比如你的接口需要特定权限,就加上注解:
@RestController @RequestMapping("/api/user") public class UserController { @RequirePermission({"user:view"}) @GetMapping("/list") public ResponseEntity<List<User>> getUserList() { // 业务逻辑 } @RequirePermission({"user:edit"}) @PutMapping("/{id}") public ResponseEntity<Void> updateUser(@PathVariable Long id, @RequestBody User user) { // 业务逻辑 } }
三、测试验证
- 先测OPTIONS请求:用Postman发个OPTIONS请求到你的接口,应该返回200,不会再403了。
- 测无权限场景:登录一个没有对应权限的用户,调用加了
@RequirePermission的接口,应该返回你自定义的403响应,而不是预检的403。
内容的提问来源于stack exchange,提问作者ekka




