CORS с spring -boot и angularjs не работают
Я пытаюсь вызвать конечные точки REST для одного приложения (spring -boot application) из другого (angularjs). Приложения работают на следующих хостах и портах.
- Приложение REST, используя spring boot,
http://localhost:8080
- приложение HTML, используя angularjs,
http://localhost:50029
Я также использую spring-security
с приложением spring -boot. В приложении 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
я получаю a 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 { }
}
Конфигурация безопасности spring выглядит следующим образом.
@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");
}
}
Любые идеи о том, чего я не вижу или делаю неправильно?
Ответы
Ответ 1
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
Ответ 2
Я был в подобной ситуации. После проведения исследований и тестирования, вот мои выводы:
-
С Spring Boot рекомендуемый способ включения глобального CORS заключается в объявлении в Spring MVC и в сочетании с мелкозернистой конфигурацией @CrossOrigin
как:
<Предварительно > <код > @Configuration
открытый класс CorsConfig {
@Bean public WebMvcConfigurer corsConfigurer() { вернуть новый WebMvcConfigurerAdapter() { @Override public void addCorsMappings (реестр CorsRegistry) { register.addMapping( "/**" ). allowedMethods ( "GET", "POST", "PUT", "DELETE" ). allowedOrigins ( "*" ) .allowedHeaders( "*" ); } }; }
}
Код >
-
Теперь, поскольку вы используете Spring Security, вам необходимо включить CORS на уровне Spring Security, чтобы позволить ему использовать конфигурацию, определенную на уровне Spring MVC, как:
<Предварительно > <код > @EnableWebSecurity
открытый класс WebSecurityConfig расширяет WebSecurityConfigurerAdapter {
@Override protected void configure (HttpSecurity http) throws Exception { http.cors(). и()... }
}
Код > Здесь - очень отличный учебник, объясняющий Поддержка CORS в среде Spring MVC.
Ответ 3
Если вы хотите включить CORS без использования фильтров или без файла конфигурации, просто добавьте
@CrossOrigin
в верхней части вашего контроллера, и он работает.
Ответ 4
Это работает для меня:
@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;
}
});
//...
}
//...
}
Ответ 5
Я использую spring boot 2.1.0
и у меня получилось
А. Добавьте сопоставления с помощью:
@Configuration
public class Config implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
}
B. Добавьте ниже конфигурацию к моему 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 вы можете использовать эти INSTEAD OF 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);
}
Ответ 6
Для меня единственная вещь, которая работала на 100%, когда использовалась весенняя безопасность, заключалась в том, чтобы пропустить весь дополнительный пух дополнительных фильтров и beans и любые косвенные "магические" люди, предлагающие работать с ними, но не для меня.
Вместо этого просто заставьте его написать нужные заголовки с помощью обычного StaticHeadersWriter
:
<Предварительно > <код > @Configuration
@EnableWebSecurity
открытый класс SecurityConfig расширяет WebSecurityConfigurerAdapter {
@Override protected void configure (HttpSecurity http) throws Exception {
HTTP // ваш конфиг безопасности здесь .authorizeRequests() .antMatchers(HttpMethod.TRACE, "/**" ). denyAll() .antMatchers( "/админ/**" ). проверка подлинности() .anyRequest(). permitAll() й(). httpBasic() й(). (заголовки). frameOptions(). отключить() й(). CSRF(). отключить() .headers() // заголовки, которые вы хотите здесь. Это решило все мои проблемы с CORS! .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Origin", "*" )) .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Methods", "POST, GET" )) .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Max-Age", "3600" )) .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Credentials", "true" )) .addHeaderWriter(новый StaticHeadersWriter ( "Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization" ) ); }
}
Код > Это самый прямой и явный способ, который я нашел для этого. Надеюсь, это поможет кому-то.
Ответ 7
Чтобы опираться на другие ответы выше, в случае, если у вас есть приложение службы загрузки REST Spring (не Spring MVC) с безопасностью Spring, тогда достаточно включить CORS через безопасность Spring (если вы используете Spring MVC, то использование компонента WebMvcConfigurer
как упомянуто Yogen, может это будет путь, поскольку Spring security делегирует приведенное в нем определение 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
Замечания:
- Включение CORS для всех источников (*) для развернутого приложения может не всегда быть хорошей идеей.
- CSRF может быть включен с помощью настройки Spring HttpSecurity без каких-либо проблем
- Если у вас включена аутентификация в приложении с помощью Spring (например, через
UserDetailsService
), то configuration.setAllowCredentials(true);
должен быть добавлен
Протестировано для загрузки Spring 2.0.0.RELEASE (то есть Spring 5.0.4.RELEASE и безопасности Spring 5.0.3.RELEASE)
Ответ 8
проверьте это:
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
...
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
...
}
Ответ 9
Расширение класса 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();
}
});
}
}
Ответ 10
Если изначально ваша программа не использует spring безопасность и не может позволить себе для изменения кода, создание простого обратного прокси-сервера может сделать трюк. В моем случае я использовал Nginx со следующей конфигурацией:
http { сервер { слушайте 9090; место нахождения /{ if ($ request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Пользовательские заголовки и заголовки различных браузеров * должны быть в порядке, но не # add_header 'DNT, User-Agent, Access-Control-Allow-Headers', X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range '; # # Сообщите клиенту, что эта предполетная информация действительна в течение 20 дней # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; кодировка = 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 'DNT, User-Agent, Access-Control-Allow-Headers', X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range '; add_header 'Content-Length-Content-Range' Access-Control-Expose-Headers '; } if ($ request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'DNT, User-Agent, Access-Control-Allow-Headers', X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range '; add_header 'Content-Length-Content-Range' Access-Control-Expose-Headers '; }
proxy_pass http://localhost: 8080; } }
}
Код>
Моя программа прослушивает : 8080.
REF: CORS на Nginx
Ответ 11
Это то, что сработало для меня.
@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);
}
}