Spring项目重新分包后访问S3出现No Access-Control-Allow-Origin错误如何解决
我对Spring项目做了简单的重新分包操作后,访问S3云存储时出现了Access-Control-Allow-Origin相关CORS错误。
我搭建了本地S3兼容服务器存储视频,基于Spring框架直接从本地云端拉流播放视频,所有功能此前均运行正常。
原来所有类都放在com.example.video包下,包含如下类:
- S3Config.java:存储AmazonS3Client相关配置
- User.java:模型类
- VideoController.java:简易控制器
- VideoStreamingServiceApplication.java:项目启动类
之后我新建了com.example.s3包,将User.java和S3Config.java移动到该包下,一开始出现自动装配报错,我通过配置组件扫描解决了装配问题,但修复后拉流时出现如下报错:
Access to XMLHttpRequest at "http://localhost/9999/recordins/a.m3u8" from origin 'null' has been block by CORS policy: No 'Access-Control-Allow-Origin' header is present on the request resource .
我已经在接口返回中配置了对应的响应头,我的VideoController.java代码如下:
@RestController @RequestMapping("/cloud") @ConfigurationProperties(prefix = "amazon.credentials") public class VideoCotroller { @Autowired private S3Config s3Client; private String bucketName= "recordings"; Logger log = LoggerFactory.getLogger(VideoCotroller.class); @Autowired User userData; @GetMapping(value = "/recordings/{fileName}", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE }) public ResponseEntity<StreamingResponseBody> streamVideo(HttpServletRequest request, @PathVariable String fileName) { try { long rangeStart = 0; long rangeEnd; AmazonS3 s3client = s3Client.getAmazonS3Client(); String uri = request.getRequestURI(); System.out.println("Fetching " + uri); S3Object object = s3client.getObject("recordings", fileName); long size = object.getObjectMetadata().getContentLength(); S3ObjectInputStream finalObject = object.getObjectContent(); final StreamingResponseBody body = outputStream -> { int numberOfBytesToWrite = 0; byte[] data = new byte[(int) size]; while ((numberOfBytesToWrite = finalObject.read(data, 0, data.length)) != -1) { outputStream.write(data, 0, numberOfBytesToWrite); } finalObject.close(); }; rangeEnd = size - 1; return ResponseEntity.status(HttpStatus.OK) .header("Content-Type", "application/vnd.apple.mpegurl") .header("Accept-Ranges", "bytes") // HERE IS THE ACCESS CONTROL ALLOW ORIGIN .header("Access-Control-Allow-Origin", "*") .header("Content-Length", String.valueOf(size)) .header("display", "staticcontent_sol, staticcontent_sol") .header("Content-Range", "bytes" + " " + rangeStart + "-" + rangeEnd + "/" + size) .body(body); //return new ResponseEntity<StreamingResponseBody>(body, HttpStatus.OK); } catch (Exception e) { System.err.println("Error "+ e.getMessage()); return new ResponseEntity<StreamingResponseBody>(HttpStatus.BAD_REQUEST); }}
如果我将包结构恢复为原来的单包结构,所有功能又恢复正常。请问为什么重新分包会引发该CORS问题?有什么修复方案?
该问题和你调整组件扫描的配置直接相关:
- Spring Boot默认只会扫描启动类所在包及其子包下的组件,你移动S3Config和User到新包后,为了修复装配问题新增的组件扫描配置,大概率只指定了
com.example.s3,漏了原来的com.example.video包,导致你项目里原来配置的全局CORS过滤器、CORS配置类没有被Spring容器加载。 - 你虽然在Controller的GET接口里手动加了CORS响应头,但浏览器在发送实际GET请求前会先发OPTIONS预检请求,这个请求不会走到你写的GET接口逻辑里,自然不会携带CORS头,就会触发跨域报错。
- 恢复单包时启动类的默认扫描覆盖了所有组件,全局CORS配置生效,所以不会报错。
方案1:调整组件扫描配置
在启动类的@ComponentScan注解里同时加入两个包,确保所有配置类都被加载:
@SpringBootApplication @ComponentScan(basePackages = {"com.example.video", "com.example.s3"}) public class VideoStreamingServiceApplication { public static void main(String[] args) { SpringApplication.run(VideoStreamingServiceApplication.class, args); } }
方案2:给当前Controller单独加跨域注解
如果不想修改全局扫描配置,直接在VideoController类上加@CrossOrigin注解即可让该类的所有接口自动支持跨域,包括OPTIONS预检请求:
@RestController @RequestMapping("/cloud") @CrossOrigin(origins = "*") @ConfigurationProperties(prefix = "amazon.credentials") public class VideoCotroller { // 原有代码保持不变 }
方案3:统一配置全局CORS规则
新增CORS配置类,放在确保会被扫描到的包下,一劳永逸解决所有接口的跨域问题:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600); } }
内容的提问来源于stack exchange,提问作者George

