Подтвердить что ты не робот

Как настроить CORS в приложении Spring Boot + Spring Security?

Я использую Spring Загрузите с помощью Spring Поддержка безопасности и коррекции.

Если я выполняю следующий код

url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
    if xmlhttp.readyState is 4
        console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:a'
do xmlhttp.send

Я получаю в результате

200

Если я тестирую неправильные учетные данные, например

url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
    if xmlhttp.readyState is 4
        console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:aa'
do xmlhttp.send

вместо получения 401 (это стандартный код для неправильной аутентификации в Spring безопасности), я получаю

0

со следующим уведомлением браузера:

GET http://localhost:5000/api/token

XMLHttpRequest не может загрузить http://localhost:5000. В запрошенном ресурсе нет заголовка "Access-Control-Allow-Origin". Origin 'http://localhost:3000', следовательно, не допускается. В ответе был код статуса HTTP 401.

Я разрабатываю код переднего плана, который нуждается в полезных кодах статуса http из ответов сервера, чтобы справиться с ситуацией. Мне нужно что-то более полезное, чем 0. Также тело ответа пуст. Я не знаю, является ли моя конфигурация неправильной, или это ошибка программного обеспечения, и я также не знаю, где, если это хром (с использованием архивного linux) или Spring безопасности.

My Spring Config:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@RestController
@RequestMapping("api")
public class Controller {
    @RequestMapping("token")
    @CrossOrigin
    Map<String, String> token(HttpSession session) {
        return Collections.singletonMap("token", session.getId());
    }
}

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("a").password("a").roles("USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .anyRequest().authenticated()
                .and().httpBasic();
    }
}

Если я тестирую с помощью curl, все работает отлично, я думаю, потому что поддержка CORS не нужна, но я пытался имитировать запросы CORS с запросами OPTION, и результат тоже был в порядке.

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTpha"
*   Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTpha
> 
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Access-Control-Allow-Origin: http://localhost:3000
< Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE
< Access-Control-Max-Age: 3600
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Origin,Accept,X-Requested-    With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
< x-auth-token: 58e4cca9-7719-46c8-9180-2fc16aec8dff
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:15:44 GMT
< 
* Connection #0 to host localhost left intact
{"token":"58e4cca9-7719-46c8-9180-2fc16aec8dff"}

и с неправильными учетными данными:

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTp"
*   Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTp
> 
< HTTP/1.1 401 Unauthorized
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< WWW-Authenticate: Basic realm="Realm"
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:16:15 GMT
< 
* Connection #0 to host localhost left intact
{"timestamp":1462119375041,"status":401,"error":"Unauthorized","message":"Failed to decode basic authentication token","path":"/api/token"}

Изменить: Во избежание недоразумений. Я использую 1.3.3 Spring Boot. Запись в блоге пишет:

Поддержка CORS будет доступна в предстоящей версии Spring Boot 1.3 и уже доступна в сборниках 1.3.0.BUILD-SNAPSHOT.

Использование метода контроллера Конфигурация CORS с аннотациями @CrossOrigin в вашем приложении Spring для загрузки не требует какой-либо конкретной конфигурации.

Глобальную конфигурацию CORS можно определить, зарегистрировав WebMvcConfigurer bean с помощью настраиваемого метода addCorsMappings (CorsRegistry):

Я добавил следующий код для поддержки глобальной поддержки cors. на самом деле я пробовал это раньше, но результат был тот же. Я попробовал это недавно, и результат тот же.

@Configuration
public class MyConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**");
            }
        };
    }
}

Идея о том, что проблема связана с переадресацией между процессом авторизации, является интересной. как я могу изменить перенаправление на любые ресурсы, чтобы избежать этого конфликта?

EDIT:

Думаю, я ближе к решению. Я протестировал с моим сервером nodejs, который поддерживает без проблем, добавив Access-Control-Allow-Origin: * ко всем запросам.

Как уже упоминалось, Стефан Иселе уже упоминал, что защита Spring переадресовывает или не добавляет заголовок CORS, поэтому запрос кажется сломанным. Поэтому, пока защита Spring проверяет аутентификацию, она должна добавить правильный заголовок.

Кто-нибудь знает, как это сделать?

EDIT:

Я нашел обходное решение, которое кажется уродливым. Я начал проблему github для загрузки Spring, где описываю обходной путь: https://github.com/spring-projects/spring-boot/issues/5834

4b9b3361

Ответ 1

Spring Security теперь может использовать поддержку Spring MVC CORS, описанную в этом посте, который я написал.

Чтобы это работало, вам нужно явно включить поддержку CORS на уровне Spring Security следующим образом, иначе запросы с включенным CORS могут быть заблокированы Spring Security до достижения Spring MVC.

Если вы используете аннотации уровня контроллера @CrossOrigin, вам просто нужно включить поддержку Spring Security CORS, и она будет использовать конфигурацию Spring MVC:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }
}

Если вы предпочитаете использовать глобальную конфигурацию CORS, вы можете объявить компонент CorsConfigurationSource следующим образом:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }
}

Этот подход заменяет ранее рекомендованный подход на основе фильтров.

Вы можете найти более подробную информацию в специальном разделе CORS документации Spring Security.

Ответ 2

Если вы используете JDK 8+, есть однострочное лямбда-решение:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}

Ответ 3

Если вы используете Spring Security, вы можете сделать следующее, чтобы сначала обрабатывать запросы CORS:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors().and()
            ...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

См. Spring 4.2.x CORS для получения дополнительной информации.

Без Spring Security это будет работать:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS");
        }
    };
}

Ответ 4

Защита от перекрестного происхождения - это особенность браузера. Curl не заботится о CORS, как вы предполагали. Это объясняет, почему ваши кудри успешны, а запросы браузера - нет.

Если вы отправите запрос браузера с неправильными учетными данными, Spring попытается перенаправить клиента на страницу входа. Этот ответ (вне страницы входа в систему) не содержит заголовка "Access-Control-Allow-Origin", и браузер реагирует так, как вы описываете.

Вы должны сделать пружину, чтобы включить хэдер для этого ответа на вход в систему, и может быть для другого ответа, такого как страницы ошибок и т.д.

Это можно сделать так:

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {

            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                    .allowedOrigins("http://domain2.com")
                    .allowedMethods("PUT", "DELETE")
                    .allowedHeaders("header1", "header2", "header3")
                    .exposedHeaders("header1", "header2")
                    .allowCredentials(false).maxAge(3600);
            }
     }

Это скопировано с Cors-Support-In-Spring-Framework

Я бы начал с добавления Cors Mapping для всех ресурсов:

registry.addMapping("/**")

а также разрешить заголовки для всех методов. Как только это сработает, вы можете начать уменьшать это снова до необходимого минимума.

Обратите внимание, что конфигурация CORS меняется с выпуском 4.2.

Если это не решит ваши проблемы, опубликуйте ответ, полученный на неудавшийся запрос ajax.

Ответ 5

Для конфигурации свойств

# ENDPOINTS CORS CONFIGURATION (EndpointCorsProperties)
endpoints.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported.
endpoints.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers.
endpoints.cors.allowed-methods=GET # Comma-separated list of methods to allow. '*' allows all methods.
endpoints.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled.
endpoints.cors.exposed-headers= # Comma-separated list of headers to include in a response.
endpoints.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients.

Ответ 6

Нашел легкое решение для Spring -Boot, Spring -Security и Java-config:

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

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.cors().configurationSource(new CorsConfigurationSource() {
            @Override
            public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                return new CorsConfiguration().applyPermitDefaultValues();
            }
        });
    }
}

Ответ 7

Я решил эту проблему:

@Configuration
public class CORSFilter extends CorsFilter {

    public CORSFilter(CorsConfigurationSource source) {
        super((CorsConfigurationSource) source);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        response.addHeader("Access-Control-Allow-Headers",
                "Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
        if (response.getHeader("Access-Control-Allow-Origin") == null)
            response.addHeader("Access-Control-Allow-Origin", "*");
        filterChain.doFilter(request, response);
    }

}

и:

@Configuration
public class RestConfig {

    @Bean
    public CORSFilter corsFilter() {
        CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://localhost:4200");
        config.addAllowedMethod(HttpMethod.DELETE);
        config.addAllowedMethod(HttpMethod.GET);
        config.addAllowedMethod(HttpMethod.OPTIONS);
        config.addAllowedMethod(HttpMethod.PUT);
        config.addAllowedMethod(HttpMethod.POST);
        ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", config);
        return new CORSFilter(source);
    }
}

Ответ 8

Я решил эту проблему:

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*"));
    configuration.setAllowCredentials(true);
    configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers","Access-Control-Allow-Origin","Access-Control-Request-Method", "Access-Control-Request-Headers","Origin","Cache-Control", "Content-Type", "Authorization"));
    configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

'

Ответ 9

Корс может быть болью в заднице, но с помощью этого простого кода вы Cors ONLY !!!! до указанного метода

@CrossOrigin(origins="*")// in this line add your url and thats is all for spring boot side
    @GetMapping("/some")
    public String index() {
        return "pawned cors!!!!";
    }

Как очарование весенним ботинком 2.0.2

Ответ 10

У меня была такая же проблема на методе, который возвращает статус сервера. Приложение развертывается на нескольких серверах. Таким образом, самый простой из них - добавить

@CrossOrigin(origins = "*")
@RequestMapping(value="/schedulerActive")
public String isSchedulerActive(){
  //code goes here
}

Этот метод не является безопасным, но вы можете добавить allowCredentials для этого.

Ответ 11

У меня были серьезные проблемы с Axios, Spring Boot и Spring Security с аутентификацией.

Обратите внимание на версию Spring Boot и Spring Security, которые вы используете.

Spring Boot: 1.5.10 Spring: 4.3.14 Spring Security 4.2.4

Чтобы решить эту проблему с помощью конфигурации Java на основе аннотаций, я создал следующий класс:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication()
                .withUser("youruser").password("yourpassword")
                .authorities("ROLE_USER");
    }

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

        http.cors().and().
                authorizeRequests()
                .requestMatchers(CorsUtils:: isPreFlightRequest).permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic()
                .realmName("Biometrix");

        http.csrf().disable();

    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("Authorization"));
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Одним из основных недостатков Axios является то, что когда ваш API требует аутентификации, он отправляет заголовок авторизации с запросом OPTIONS. Если вы не включите Авторизацию в настройке конфигурации разрешенных заголовков, наш запрос OPTIONS (он же PreFlight запрос) завершится неудачно, и Axios сообщит об ошибке.

Как вы можете видеть с помощью пары простых и правильно расположенных настроек, настройка CORS с SpringBoot довольно проста.

Ответ 12

Котлин раствор

...
http.cors().configurationSource {
  CorsConfiguration().applyPermitDefaultValues()
}
...

Ответ 13

После долгих поисков ошибок, возникающих в javascript CORS, единственное элегантное решение, которое я нашел для этого случая, - это настройка cors собственного класса Spring org.springframework.web.cors.CorsConfiguration.CorsConfiguration()

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
    }