← Назад к вопросам

Что такое CORS?

1.0 Junior🔥 211 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое CORS?

CORS (Cross-Origin Resource Sharing) — это механизм, который позволяет веб-приложениям, работающим в браузере, получать доступ к ресурсам с других доменов. Без CORS браузер блокирует такие запросы по соображениям безопасности.

Проблема: Same-Origin Policy

Браузер имеет встроенную политику безопасности:

Оригин = Схема (HTTP/HTTPS) + Домен + Порт

Примеры разных оригинов:
https://example.com:443
https://example.com:8443    ← Разный порт!
https://api.example.com     ← Разный поддомен!
http://example.com          ← Разная схема!

Приложение на https://example.com не может сделать AJAX запрос к https://api.example.com без специальных разрешений.

Как это работает

Простые запросы (GET, POST с стандартными заголовками):

1. Браузер отправляет запрос с заголовком Origin:
   
   POST /api/users HTTP/1.1
   Host: api.example.com
   Origin: https://app.example.com
   
2. Сервер проверяет Origin и отправляет ответ с разрешением:
   
   HTTP/1.1 200 OK
   Access-Control-Allow-Origin: https://app.example.com
   Access-Control-Allow-Credentials: true
   
3. Браузер видит заголовок и разрешает приложению использовать ответ

Сложные запросы (PUT, DELETE, кастомные заголовки):

1. Браузер отправляет preflight запрос (OPTIONS):
   
   OPTIONS /api/users HTTP/1.1
   Host: api.example.com
   Origin: https://app.example.com
   Access-Control-Request-Method: PUT
   Access-Control-Request-Headers: X-Custom-Header
   
2. Сервер разрешает или запрещает:
   
   HTTP/1.1 200 OK
   Access-Control-Allow-Origin: https://app.example.com
   Access-Control-Allow-Methods: GET, POST, PUT, DELETE
   Access-Control-Allow-Headers: X-Custom-Header
   Access-Control-Max-Age: 3600
   
3. Только если preflight успешен, браузер отправляет основной запрос

Реализация в Java Spring

Вариант 1: На уровне контроллера

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class UserController {
    
    @CrossOrigin(origins = "https://app.example.com")
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.findAll();
    }
    
    @CrossOrigin(origins = {"https://app.example.com", "https://admin.example.com"})
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
}

Вариант 2: Глобальная конфигурация (рекомендуется)

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://app.example.com", "https://admin.example.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .allowedHeaders("*")  // Разрешить все заголовки
            .allowCredentials(true)  // Разрешить cookies
            .maxAge(3600);  // Кешировать preflight на 1 час
    }
}

Вариант 3: Через фильтр

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

@Component
public class CorsFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) {
        String origin = request.getHeader("Origin");
        
        if (isAllowedOrigin(origin)) {
            response.setHeader("Access-Control-Allow-Origin", origin);
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
            response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Max-Age", "3600");
        }
        
        if (request.getMethod().equals("OPTIONS")) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }
        
        try {
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private boolean isAllowedOrigin(String origin) {
        return origin != null && 
               (origin.equals("https://app.example.com") || 
                origin.equals("https://admin.example.com"));
    }
}

CORS заголовки

ЗаголовокЗначениеЗначение
Access-Control-Allow-Origin* или конкретный originКакие origins могут получать ответы
Access-Control-Allow-MethodsGET, POST, PUT, DELETEКакие HTTP методы разрешены
Access-Control-Allow-HeadersContent-Type, AuthorizationКакие заголовки разрешены
Access-Control-Allow-CredentialstrueРазрешить ли cookies и auth
Access-Control-Max-Age3600Кешировать preflight на N секунд
Access-Control-Expose-HeadersX-Custom-HeaderКакие заголовки видны клиенту

Безопасность

Опасно:

@CrossOrigin(origins = "*")  // Разрешить ВСЕМ!

Это позволяет ЛЮБОМУ веб-сайту делать запросы к вашему API.

Лучше:

@CrossOrigin(origins = {"https://app.example.com"})
allowCredentials = false  // Если используется *

И никогда не используйте CORS для защиты от security-атак — всегда проверяйте на сервере!

Резюме

  • CORS необходим для cross-domain запросов из браузера
  • Браузер автоматически отправляет Origin заголовок
  • Сервер должен ответить с Access-Control-Allow-Origin
  • Для сложных запросов используется preflight (OPTIONS)
  • Всегда явно указывайте allowed origins, не используйте * для credentials