У меня есть приложение Angular, которое делает запрос к Spring Boot RESTful API. В целях разработки я установил различную свободную политику CORS в моей Spring Boot (обратите внимание, что при чтении этого и возможном копировании или вставке они не предназначены для безопасных производственных значений).
@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):
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8092/report/csv/detail. (Reason: CORS request did not succeed).
Конечная точка контроллера (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, которая связывается с приложением Spring 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' }
);
}
}
Хотя я и раньше сталкивался с подобными проблемами, я предполагал, что CORSFilter в моем коде Java предотвратил бы ошибку. Что мне не хватает?
В Chrome я действительно получаю разные ошибки: [Violation] 'load' handler took 891ms [Violation] Forced reflow while executing JavaScript took 62ms
Сообщение об ошибке Chrome помогло мне. Теперь проблема решена. Проблема заключалась в том, что приложение Java записывало в каталог assert приложения Angular, вынуждая его перекомпилировать и перезагружать (я использовал ng serve
с Angular CLI). Спасибо.
я не уверен, но полагаю, что этот ответ вам как-то поможет
Однажды я столкнулся с той же проблемой, и после изучения некоторых моментов я понял, что весенняя загрузка обеспечивает некоторый расширенный уровень безопасности для источников CORS, чтобы ограничить CROSS SITE ATTACK.
Мы обсудим это позже, сначала мы перейдем к делу,
Код, который вы написали в классе SimpleCORSFilter, в основном отвечает за безопасность на уровне заголовков, когда вы работаете с любым источником CORS.
Чтобы убедиться в этом, проверьте заголовок ответа, используя элемент inspect (точка - 1).
Проверьте каждый элемент заголовка, который вам требовался или использовался для выполнения вашего внешнего кода (код Anguler, как вы упомянули ранее). Для загрузки файла требуются некоторые параметры, такие как content-type, content-disposition. Я полагаю, вы не упомянули об этом должным образом или используете не в соответствии со стандартами пружинной загрузки (пункт 2).
Если этот параметр отсутствует, отправьте его в своем ответе, я не уверен, что ваш код работает или нет, но лучше, когда вы работаете с весенней загрузкой, отправляйте ваш составной файл как ресурс с каждым из параметров заголовка, например
content-type: MIME-тип файла, Content - Disposition: имя файла и действие, которое вы выполняете: вложение / встроенное (точка - 3).
В некоторых случаях все эти параметры все еще существуют, и вы все еще не можете загрузить (с чем я сталкиваюсь),
Решение заключается в том, что вы добавляете этот параметр в качестве ответа, но не разрешаете им доступ из источника CORS.
Для этого в «Access-Control-Expose-Headers» нужно указать какой-то обязательный параметр (Пункт - 4). также добавьте некоторые другие параметры, которые вы хотите добавить.
Right Click browser -> select inspect element -> Go in network pane -> Click on requested url -> In newly open block you see the request and their relevant content -> Go in response header option
Add debugger on front-end code -> console.info(“”)
Go the end point -> 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);
- SimpleCORSFilter class -> doFilter() method -> add
response.setHeader(“Access-Control-Expose-Headers”, “xsrf-token, X-Total-Results, Authorization, Content-type, Content-Disposition”)
Тем не менее, вы сталкиваетесь с проблемой, упомянутой в комментарии, я пришлю вам одну демонстрацию для этого.
Вы пробовали другой браузер, например Chrome?