仅当涉及文件ipload /下载时,跨域请求才被阻止

时间:2018-10-26 18:03:19

标签: java angular spring-boot spring-web

我有一个Angular应用程序,它向Spring Boot RESTful API发出请求。出于开发目的,我在Spring Boot中设置了一个松散的CORS策略(请注意,任何阅读此文档以及可能进行复制或粘贴的操作,都不意味着它们是安全的生产价值)。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers",
                "Origin, X-Requested-With, Content-Type, Accept, xsrf-token, authorization");

        response.setHeader("Access-Control-Expose-Headers", "Location");
        response.addHeader("Access-Control-Expose-Headers", "xsrf-token");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }

使用它的@Configuration类:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private SimpleCORSFilter corsFilter;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.addFilterBefore(corsFilter, ChannelProcessingFilter.class).authorizeRequests().antMatchers("/admin/**")
                .hasAnyAuthority("ROLE_ADMIN").antMatchers(HttpMethod.GET, "/public").permitAll().and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().httpBasic()
                .realmName("test").and().csrf().disable();

    }
}

通常情况下,应用之间的通信才可以找到。我可以毫无问题地检索和发布JSON数据。但是,每当我尝试上传图像或运行一些应该下载文件的代码时,我的浏览器(Firefox)控制台都会收到以下错误:

  

跨源请求被阻止:同源策略禁止阅读   http://localhost:8092/report/csv/detail上的远程资源。   (原因:CORS请求未成功。)

控制器端点(Java):

@RequestMapping("/csv/detail")
public ResponseEntity<?> getDetailedCSV(@RequestBody ReportRequestDTO dto) {
    HttpHeaders headers = new HttpHeaders();
    byte[] contents;
    try {
        contents = IOUtils.toByteArray(new FileInputStream(reportService.getPDFSummary((dto))));
        headers.setContentType(MediaType.parseMediaType("application/pdf"));
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
        ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
        return response;
    } catch (FileNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    } catch (IOException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);

    }
}

getPDFSummary方法:

@Override
public String getPDFSummary(ReportRequestDTO req) {
    String outputName = outDir + UUID.randomUUID().toString() + ".pdf";
    List<SummaryReport> rows = new ArrayList<>();

    if (req.getStatusType() == ReportStatus.ALL) {
        rows = summaryRepo.getSummaryReportAllStatus(req.getPropertyIDs(), req.getItemIDs(), req.getStart(),
                req.getEnd());
    } else {
        boolean isOpen = req.getStatusType() == ReportStatus.OPEN;
        rows = summaryRepo.getSummaryReport(isOpen, req.getPropertyIDs(), req.getItemIDs(), req.getStart(),
                req.getEnd());
    }

    String html = convertRepsToHtml(rows);
    PdfConverterExtension.exportToPdf(outputName, html, "", options);
    return outputName;
}

此与Spring Boot应用程序联系的Angular服务:

@Injectable()
export class ReportService {
  private baseEndpoint: String = 'http://localhost:8092';
  constructor(private http: HttpClient) {}

  public getReport(req: ReportRequestModel): Observable<Blob> {
    return this.http.post(
      `${this.baseEndpoint}/report/${req.exportType}`,
      req,
      { responseType: 'blob' }
    );
  }
}

虽然我之前遇到过类似的问题,但我认为Java代码中的CORSFilter可以防止该错误。我想念什么?

1 个答案:

答案 0 :(得分:0)

我不确定,但我想这个答案会以某种方式帮助您

我曾经遇到过同样的问题,在学习了一些要点之后,我意识到弹簧靴为CORS起源提供了某种扩展的安全性,以限制“跨站点攻击”。

我们稍后进行讨论,首先我们要说的是

当您使用任何CORS来源工作时,您在类SimpleCORSFilter中编写的代码主要负责标头级安全。

要进行验证,请使用检查元素(点-1)检查您的响应标头。

检查执行前端代码(前面提到的角度代码)所需或使用的每个标头元素。下载文件需要某些参数,例如content-type,content-disposition。我想您没有按照Spring Boot标准(要点2)正确地提及或不使用它。

如果缺少此参数,则在响应中发送该参数,我不确定您的代码是否正常工作,但是当您使用spring boot时,将多部分文件作为Resource发送,并且每个标头参数都像< / p>

content-type:文件MIME类型, 内容–处理:文件名和执行的操作:附件/内联(指向-3)。

在某些情况下,所有这些参数仍然存在,您仍然无法下载(我面对),

解决方案是您添加该参数作为响应,但不允许它们按CORS来源进行访问。

为此,您需要在“ Access-Control-Expose-Headers”(点-4)中设置一些必需的参数。 还添加其他要添加的参数。

  
      
  1. 右键单击浏览器->选择检查元素->进入网络窗格->单击所请求的URL->在新打开的块中,您将看到请求及其相关内容->进入响应标头选项

  2.   
  3. 在前端代码上添加调试器-> console.log(“”)

  4.   
  5. 到达终点->获取资源参数->将文件添加为OutputStream->将其发送为ResponseEntity

  6.   
Path path = Paths.get(<directory-path>).normalize();
Path filePath = path.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
Return ResponseEntity.ok
        .contentType(MediaType.<MIME-Type>)
        .header(HttpHeaders.CONTENT_DISPOSITION, “attachment; filename=””+ resource.filename())
        .body(resource);
  
      
  1. SimpleCORSFilter类-> doFilter()方法->添加
  2.   
response.setHeader(“Access-Control-Expose-Headers”, “xsrf-token, X-Total-Results, Authorization, Content-type, Content-Disposition”)

在评论中您仍然遇到一些问题,我将为此发送一个演示。