java 仅当涉及文件上载/下载时才阻止跨源请求

ix0qys7i  于 2023-04-10  发布在  Java
关注(0)|答案(1)|浏览(106)

我有一个Angular应用程序,它向Sping 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;
}

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

@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可以防止这个错误。

368yc8dk

368yc8dk1#

我不确定,但我想这个答案在某种程度上对你有帮助
我曾经遇到过同样的问题,在研究了一些要点之后,我意识到Sping Boot 为CORS源提供了一些扩展级别的安全性,以限制跨站攻击。
我们稍后再讨论,先说重点,
您在SimpleCORSFilter类中编写的代码主要负责在使用任何CORS源时的头级别安全性。
为了验证这一点,请使用inspect元素(Point - 1)检查您的响应头。
检查每个头元素,你需要或用于执行你的前端代码(根据你前面提到的Anguler代码)。一些参数,如content-type,content-disposition是下载文件所必需的。我想你没有正确地提到它,或者没有按照Sping Boot 标准使用(Point - 2)。
如果缺少此参数,请在响应中发送它,我不确定您的代码是否正常工作,但当您使用spring Boot 时,将多部分文件作为Resource发送,每个头参数如下
内容类型:文件MIME类型,内容-处置:文件名和执行的操作:attachment / inline(点-3)。
在某些情况下,这所有的参数都在那里,你仍然无法下载(我面对),
解决方案是将该参数添加为响应,但不允许它们通过CORS源访问。
为此,您需要在“Access-Control-Expose-Headers”(点4)中设置一些必需的参数。还可以添加一些您想要添加的其他参数。
1.右键单击浏览器-〉选择检查元素-〉进入网络窗格-〉单击请求的URL -〉在新打开的块中,您可以看到请求及其相关内容-〉进入响应头选项
1.在前端代码上添加调试器-〉console.log(“”)
1.进入终点-〉Take Resource param -〉Add file as OutputStream -〉Send it as ResponseEntity

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()方法-〉add
response.setHeader(“Access-Control-Expose-Headers”, “xsrf-token, X-Total-Results, Authorization, Content-type, Content-Disposition”)

你仍然面临着一些问题,在评论中提到,我会送一个演示。

相关问题