Я пытаюсь вызвать конечные точки REST в одном приложении (приложение с весенней загрузкой) из другого (angularjs). Приложения работают на следующих хостах и портах.
- Приложение REST, использующее весеннюю загрузку,
http://localhost:8080
- HTML-приложение с использованием angularjs,
http://localhost:50029
Я также использую spring-security
с приложением весенней загрузки. Из приложения HTML я могу пройти аутентификацию в приложении REST, но после этого я все еще не могу получить доступ к какой-либо конечной точке REST. Например, у меня есть сервис angularjs, определенный следующим образом.
adminServices.factory('AdminService', ['$resource', '$http', 'conf', function($resource, $http, conf) {
var s = {};
s.isAdminLoggedIn = function(data) {
return $http({
method: 'GET',
url: 'http://localhost:8080/api/admin/isloggedin',
withCredentials: true,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
};
s.login = function(username, password) {
var u = 'username=' + encodeURI(username);
var p = 'password=' + encodeURI(password);
var r = 'remember_me=1';
var data = u + '&' + p + '&' + r;
return $http({
method: 'POST',
url: 'http://localhost:8080/login',
data: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
};
return s;
}]);
Контроллер angularjs выглядит следующим образом.
adminControllers.controller('LoginController', ['$scope', '$http', 'AdminService', function($scope, $http, AdminService) {
$scope.username = '';
$scope.password = '';
$scope.signIn = function() {
AdminService.login($scope.username, $scope.password)
.success(function(d,s) {
if(d['success']) {
console.log('ok authenticated, call another REST endpoint');
AdminService.isAdminLoggedIn()
.success(function(d,s) {
console.log('i can access a protected REST endpoint after logging in');
})
.error(function(d, s) {
console.log('huh, error checking to see if admin is logged in');
$scope.reset();
});
} else {
console.log('bad credentials?');
}
})
.error(function(d, s) {
console.log('huh, error happened!');
});
};
}]);
При звонке http://localhost:8080/api/admin/isloggedin
я получаю 401 Unauthorized
.
На стороне приложения REST у меня есть фильтр CORS, который выглядит следующим образом.
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
@Override
public void destroy() { }
@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", "http://localhost:50029");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig config) throws ServletException { }
}
Моя весенняя конфигурация безопасности выглядит следующим образом.
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private JsonAuthSuccessHandler jsonAuthSuccessHandler;
@Autowired
private JsonAuthFailureHandler jsonAuthFailureHandler;
@Autowired
private JsonLogoutSuccessHandler jsonLogoutSuccessHandler;
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Value("${rememberme.key}")
private String rememberMeKey;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(jsonAuthSuccessHandler)
.failureHandler(jsonAuthFailureHandler)
.permitAll()
.and()
.logout()
.deleteCookies("remember-me", "JSESSIONID")
.logoutSuccessHandler(jsonLogoutSuccessHandler)
.permitAll()
.and()
.rememberMe()
.userDetailsService(userDetailsService)
.tokenRepository(persistentTokenRepository)
.rememberMeCookieName("REMEMBER_ME")
.rememberMeParameter("remember_me")
.tokenValiditySeconds(1209600)
.useSecureCookie(false)
.key(rememberMeKey);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider);
}
}
Все, что делают обработчики, это записывают ответ JSON, например, в {success: true}
зависимости от того, вошел ли пользователь в систему, не прошел аутентификацию или вышел из системы. Это RestAuthenticationEntryPoint
выглядит следующим образом.
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex)
throws IOException, ServletException {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
Есть идеи о том, что мне не хватает или что я делаю неправильно?
источник
API Gateway
облаке с Spring, что, по моему мнению, было излишним. .Ответы:
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class SimpleCORSFilter implements Filter { private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class); public SimpleCORSFilter() { log.info("SimpleCORSFilter init"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }
Нет необходимости дополнительно определять этот фильтр, просто добавьте этот класс. Spring будет сканировать и добавлять его для вас. SimpleCORSFilter. Вот пример: Spring-enable-cors
источник
HEADERS
иX_REDIRECT_LOCATION_HEADER
? 2) Является ли строкаrequest.getRequestURL());
опечаткой или ошибкой копирования / вставки? 3) Почему вы не проверяетеOPTIONS
цепочку фильтров и просто продолжаете ее работу?request.getHeader("Origin")
как показано выше из-за разделения HTTP-ответа@Order(Ordered.HIGHEST_PRECEDENCE)
.Я был в подобной ситуации. После исследования и тестирования я сделал следующие выводы:
В Spring Boot рекомендуемый способ включить глобальный CORS - это объявить в Spring MVC и объединить его с мелкозернистой
@CrossOrigin
конфигурацией как:@Configuration public class CorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*") .allowedHeaders("*"); } }; } }
Теперь, поскольку вы используете Spring Security, вам необходимо включить CORS на уровне Spring Security, а также позволить ему использовать конфигурацию, определенную на уровне Spring MVC, как:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and()... } }
Вот отличный учебник, объясняющий поддержку CORS в Spring MVC framework.
источник
Если вы хотите включить CORS без использования фильтров или без файла конфигурации, просто добавьте
@CrossOrigin
в верхнюю часть вашего контроллера, и он работает.
источник
Чтобы основываться на других ответах выше, если у вас есть приложение службы REST для загрузки Spring (не Spring MVC) с безопасностью Spring, тогда достаточно включить CORS через безопасность Spring (если вы используете Spring MVC, тогда использование
WebMvcConfigurer
bean-компонента, как упомянуто Йогеном, может быть путь, по которому безопасность Spring будет делегировать определение CORS, упомянутое в нем)Итак, вам нужна конфигурация безопасности, которая выполняет следующие функции:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { //other http security config http.cors().configurationSource(corsConfigurationSource()); } //This can be customized as required CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); List<String> allowOrigins = Arrays.asList("*"); configuration.setAllowedOrigins(allowOrigins); configuration.setAllowedMethods(singletonList("*")); configuration.setAllowedHeaders(singletonList("*")); //in case authentication is enabled this flag MUST be set, otherwise CORS requests will fail configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
}
Эта ссылка содержит дополнительную информацию о том же: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors
Заметка:
UserDetailsService
),configuration.setAllowCredentials(true);
необходимо добавитьПротестировано для Spring boot 2.0.0.RELEASE (т.е. Spring 5.0.4.RELEASE и Spring security 5.0.3.RELEASE)
источник
Я использую,
spring boot 2.1.0
и что сработало для меня, так этоA. Добавьте сопоставления cors:
@Configuration public class Config implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*"); } }
Б. Добавьте ниже конфигурацию в мой
HttpSecurity
для весенней безопасности.cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } })
Также в случае прокси Zuul вы можете использовать это ВМЕСТО A и B (просто используйте,
HttpSecurity.cors()
чтобы включить его в безопасности Spring):@Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("OPTIONS"); config.addAllowedMethod("HEAD"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); config.addAllowedMethod("DELETE"); config.addAllowedMethod("PATCH"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }
источник
org.springframework.web.filter.CorsFilter
. У меня была такая же проблема, когда я случайно использовал ее из пакетов catalina.Это работает для меня:
@Configuration public class MyConfig extends WebSecurityConfigurerAdapter { //... @Override protected void configure(HttpSecurity http) throws Exception { //... http.cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } }); //... } //... }
источник
Для меня единственное, что сработало на 100%, когда используется Spring Security, - это пропустить весь дополнительный беспорядок, связанный с дополнительными фильтрами, бобами и любой косвенной "магией", которую люди постоянно предлагали, которая работала для них, но не для меня.
Вместо этого просто заставьте его написать нужные заголовки простым
StaticHeadersWriter
:@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // your security config here .authorizeRequests() .antMatchers(HttpMethod.TRACE, "/**").denyAll() .antMatchers("/admin/**").authenticated() .anyRequest().permitAll() .and().httpBasic() .and().headers().frameOptions().disable() .and().csrf().disable() .headers() // the headers you want here. This solved all my CORS problems! .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", "*")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Methods", "POST, GET")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Max-Age", "3600")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Credentials", "true")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Headers", "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization")); } }
Это самый прямой и ясный способ, который я нашел для этого. Надеюсь, это кому-то поможет.
источник
Шаг 1
Аннотирование контроллера
@CrossOrigin
аннотацией позволит использовать конфигурации CORS.@CrossOrigin @RestController public class SampleController { ..... }
Шаг 2
Spring уже имеет CorsFilter, хотя вы можете просто зарегистрировать свой собственный CorsFilter в качестве bean-компонента, чтобы обеспечить свою собственную конфигурацию следующим образом.
@Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // Provide list of origins if you want multiple origins config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")); config.setAllowCredentials(true); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }
источник
проверьте это:
@Override protected void configure(HttpSecurity httpSecurity) throws Exception { ... .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() ... }
источник
Расширение класса WebSecurityConfigurerAdapter и переопределение метода configure () в вашем классе @EnableWebSecurity будут работать: Ниже приведен образец класса
@Override protected void configure(final HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling(); http.headers().cacheControl(); @Override public CorsConfiguration getCorsConfiguration(final HttpServletRequest request) { return new CorsConfiguration().applyPermitDefaultValues(); } }); } }
источник
Если изначально ваша программа не использует Spring Security и не может позволить себе изменение кода, создание простого обратного прокси-сервера может помочь. В моем случае я использовал Nginx со следующей конфигурацией:
http { server { listen 9090; location / { if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Custom headers and headers various browsers *should* be OK with but aren't # add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; # # Tell client that this pre-flight info is valid for 20 days # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } proxy_pass http://localhost:8080; } } }
Моя программа слушает : 8080 .
ССЫЛКА: CORS на Nginx
источник
Это то, что у меня сработало.
@EnableWebSecurity public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors(); } } @Configuration public class WebConfiguration implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry .addMapping("/**") .allowedMethods("*") .allowedHeaders("*") .allowedOrigins("*") .allowCredentials(true); } }
источник
Этот ответ копирует ответ @abosancic, но добавляет дополнительную безопасность, чтобы избежать эксплойта CORS .
Совет 1: не отражайте входящий источник как есть, не проверив список разрешенных хостов для доступа.
Совет 2. Разрешите запросы с учетными данными только для хостов из белого списка.
import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class SimpleCORSFilter implements Filter { private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class); private List<String> allowedOrigins; public SimpleCORSFilter() { log.info("SimpleCORSFilter init"); allowedOrigins = new ArrayList<>(); allowedOrigins.add("https://mysafeorigin.com"); allowedOrigins.add("https://itrustthissite.com"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String allowedOrigin = getOriginToAllow(request.getHeader("Origin")); if(allowedOrigin != null) { response.setHeader("Access-Control-Allow-Origin", allowedOrigin); response.setHeader("Access-Control-Allow-Credentials", "true"); } response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } public String getOriginToAllow(String incomingOrigin) { if(allowedOrigins.contains(incomingOrigin.toLowerCase())) { return incomingOrigin; } else { return null; } } }
источник
В нашем приложении Spring Boot мы настроили CorsConfigurationSource следующим образом.
Последовательность
allowedOrigns
сначала добавления, а затем настройкиapplyPermitDefaultValues()
позволяет Spring устанавливать значения по умолчанию для разрешенных заголовков, открытых заголовков, разрешенных методов и т.д., поэтому нам не нужно их указывать.public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://localhost:8084")); configuration.applyPermitDefaultValues(); UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource(); configurationSource.registerCorsConfiguration("/**", configuration); return configurationSource; }
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**") .access("@authProvider.validateApiKey(request)") .anyRequest().authenticated() .and().cors() .and().csrf().disable() .httpBasic().authenticationEntryPoint(authenticationEntryPoint); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }
источник
Просто сделайте один класс вроде, с этим все будет хорошо:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class MyCorsConfig implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, enctype"); response.setHeader("Access-Control-Max-Age", "3600"); if (HttpMethod.OPTIONS.name().equalsIgnoreCase(((HttpServletRequest) req).getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } @Override public void destroy() { } @Override public void init(FilterConfig config) throws ServletException { } }
источник
Это то, что у меня сработало, чтобы отключить CORS между загрузкой Spring и React.
@Configuration public class CorsConfig implements WebMvcConfigurer { /** * Overriding the CORS configuration to exposed required header for ussd to work * * @param registry CorsRegistry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true) .maxAge(4800); } }
Мне пришлось изменить конфигурацию безопасности, как показано ниже:
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } }).and() .antMatcher("/api/**") .authorizeRequests() .anyRequest().authenticated() .and().httpBasic() .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().exceptionHandling().accessDeniedHandler(apiAccessDeniedHandler()); }
источник