Java跨域处理案例

wen java案例 2

Java跨域处理完整案例

什么是跨域问题

当浏览器发起AJAX请求时,如果请求的URL与当前页面的协议、域名、端口任一不同,就产生了跨域,浏览器出于安全考虑,默认会阻止跨域请求。

Java跨域处理案例

跨域示例:

  • 页面地址:http://localhost:8080
  • 请求地址:http://localhost:9090/api/data

解决方案对比

方案 适用场景 优点 缺点
CORS过滤器 Spring Boot/Spring MVC 简单、无需改动业务代码 需要服务端配合
@CrossOrigin注解 单个Controller/方法 精确控制 需要逐类/方法配置
代理转发 前后端分离开发 无需服务端配置 仅限开发环境
JSONP 老项目 兼容性好 只支持GET请求

完整案例实现

1 CORS全局配置(推荐)

实现WebMvcConfigurer接口

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  // 匹配所有路径
                .allowedOriginPatterns("*")  // 允许所有来源
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("Authorization", "Content-Type")
                .allowCredentials(true)  // 允许携带Cookie
                .maxAge(3600);  // 预检请求缓存时间(秒)
    }
}

使用CorsFilter(更底层)

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // 允许的域名,可以使用通配符*
        config.setAllowedOriginPatterns(Arrays.asList(
            "http://localhost:8080",
            "http://localhost:3000",
            "https://*.example.com"
        ));
        // 允许的请求头
        config.setAllowedHeaders(Arrays.asList(
            "Authorization",
            "Content-Type",
            "X-Requested-With",
            "Accept",
            "Origin",
            "Access-Control-Request-Method",
            "Access-Control-Request-Headers"
        ));
        // 暴露的响应头
        config.setExposedHeaders(Arrays.asList(
            "Authorization",
            "Content-Type",
            "X-Session-Id"
        ));
        // 允许的请求方法
        config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
        // 是否允许携带凭证(Cookie等)
        config.setAllowCredentials(true);
        // 预检请求的缓存时间
        config.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

2 自定义跨域拦截器

@Component
public class CorsInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 设置响应头,允许跨域访问
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Max-Age", "3600");
        // 处理预检请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
            return false;  // 不继续处理
        }
        return true;
    }
}

3 注解方式(精确控制)

@RestController
@RequestMapping("/api")
// 类级别的跨域配置
@CrossOrigin(origins = "http://localhost:8080", maxAge = 3600)
public class UserController {
    // 方法级别的跨域配置(优先级高于类级别)
    @GetMapping("/users")
    @CrossOrigin(
        origins = {"http://localhost:8080", "http://localhost:3000"},
        allowedHeaders = {"*"},
        methods = {RequestMethod.GET, RequestMethod.POST}
    )
    public List<User> getUsers() {
        return userService.findAll();
    }
    @PostMapping("/login")
    public Result login(@RequestBody LoginRequest request) {
        // 登录逻辑
        return Result.success();
    }
}

4 Spring Security集成方案

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .cors().and()  // 启用CORS支持
            .csrf().disable()  // 生产环境需谨慎
            .authorizeRequests()
            .antMatchers("/api/**").permitAll()
            .anyRequest().authenticated();
    }
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

前端跨域处理

1 Axios配置

// axios.js
import axios from 'axios'
const service = axios.create({
  baseURL: '/api',
  timeout: 15000,
  withCredentials: true  // 携带Cookie
})
// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
// 响应拦截器
service.interceptors.response.use(
  response => {
    return response.data
  },
  error => {
    if (error.response) {
      switch (error.response.status) {
        case 401:
          // 处理未授权
          router.push('/login')
          break
        case 403:
          // 处理禁止访问
          break
        default:
          break
      }
    }
    return Promise.reject(error)
  }
)
export default service

2 Vue/React配置代理

Vue.config.js:

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:9090',  // 后端地址
        changeOrigin: true,  // 改变请求源
        pathRewrite: {
          '^/api': ''  // 重写路径
        }
      }
    }
  }
}

常见问题处理

1 携带Cookie的跨域请求

// 后端配置
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // 注意:使用allowCredentials时,不能使用 "*" 作为origin
        config.setAllowedOriginPatterns(Arrays.asList("http://localhost:8080"));
        config.setAllowCredentials(true);
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

2 预检请求(OPTIONS)

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        // 处理预检请求
        if (e instanceof HttpRequestMethodNotSupportedException) {
            return Result.error(405, "请求方式不支持");
        }
        return Result.error(500, e.getMessage());
    }
}

最佳实践建议

  1. 生产环境:采用CORS过滤器方案,配合Spring Security
  2. 开发环境:使用代理转发,避免跨域问题
  3. 安全考虑
    • 不要使用通配符,指定具体域名
    • 谨慎使用allowCredentials(true)
    • 设置合理的预检请求缓存时间
  4. 性能优化
    • 减少预检请求次数
    • 合理设置maxAge
    • 使用HTTP缓存

测试跨域配置

# 使用curl测试跨域请求
curl -X OPTIONS http://localhost:8080/api/users \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: GET" \
  -v
# 检查响应头
# Access-Control-Allow-Origin: http://localhost:3000
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
# Access-Control-Allow-Headers: Authorization, Content-Type

通过以上方案,可以完整解决Java项目中的跨域问题,让前后端分离开发更加顺畅。

抱歉,评论功能暂时关闭!