Estou tentando chamar pontos de extremidade REST em um aplicativo (aplicativo de inicialização rápida) de outro (angularjs). Os aplicativos estão sendo executados nos seguintes hosts e portas.
- Aplicação REST, usando boot de mola,
http://localhost:8080
- Aplicativo HTML, usando angularjs,
http://localhost:50029
Também estou usando spring-security
com o aplicativo Spring Boot. A partir do aplicativo HTML, posso autenticar no aplicativo REST, mas, depois disso, ainda não consigo acessar nenhum terminal REST. Por exemplo, eu tenho um serviço angularjs definido como segue.
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;
}]);
O controlador angularjs se parece com o seguinte.
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!');
});
};
}]);
Na ligação para http://localhost:8080/api/admin/isloggedin
, recebo um 401 Unauthorized
.
No lado do aplicativo REST, tenho um filtro CORS parecido com o seguinte.
@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 { }
}
Minha configuração de segurança de primavera se parece com o seguinte.
@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);
}
}
Tudo o que os manipuladores estão fazendo é escrever uma resposta JSON, como {success: true}
se o usuário efetuou login, falhou na autenticação ou desconectou-se. oRestAuthenticationEntryPoint
parece com o seguinte.
@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");
}
}
Alguma ideia do que estou perdendo ou fazendo de errado?
fonte
API Gateway
com o spring cloud, o que achei um exagero .Respostas:
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() { } }
Não há necessidade de definir este filtro extra, basta adicionar esta classe. O Spring será digitalizado e adicionado para você. SimpleCORSFilter. Aqui está o exemplo: spring-enable-cors
fonte
HEADERS
eX_REDIRECT_LOCATION_HEADER
? 2) A linha érequest.getRequestURL());
um erro de digitação ou erro de copiar / colar? 3) Por que você não verificaOPTIONS
e simplesmente continua com a cadeia de filtros?request.getHeader("Origin")
como mostrado acima por causa da divisão de resposta HTTP@Order(Ordered.HIGHEST_PRECEDENCE)
.Eu já havia passado por uma situação semelhante. Depois de fazer pesquisas e testes, aqui estão minhas descobertas:
Com o Spring Boot, a maneira recomendada de habilitar CORS global é declarar dentro do Spring MVC e combinado com
@CrossOrigin
configuração refinada como:@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("*"); } }; } }
Agora, como você está usando o Spring Security, é necessário habilitar o CORS no nível do Spring Security também para permitir que ele aproveite a configuração definida no nível do Spring MVC como:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and()... } }
Aqui está um excelente tutorial que explica o suporte CORS na estrutura Spring MVC.
fonte
Se você deseja ativar o CORS sem usar filtros ou sem arquivo de configuração, basta adicionar
@CrossOrigin
na parte superior do seu controlador e funcionará.
fonte
Para construir sobre outras respostas acima, no caso de você ter um aplicativo de serviço REST Spring boot (não Spring MVC) com segurança Spring, então habilitar CORS via segurança Spring é suficiente (se você usar Spring MVC, então usando um
WebMvcConfigurer
bean como mencionado por Yogen pode ser o caminho a seguir, pois a segurança do Spring delegará à definição CORS mencionada nele)Portanto, você precisa ter uma configuração de segurança que faça o seguinte:
@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; }
}
Este link contém mais informações sobre o mesmo: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors
Nota:
UserDetailsService
por exemplo), então oconfiguration.setAllowCredentials(true);
deve ser adicionadoTestado para Spring boot 2.0.0.RELEASE (ou seja, Spring 5.0.4.RELEASE e Spring security 5.0.3.RELEASE)
fonte
Estou usando
spring boot 2.1.0
e o que funcionou para mim foiA. Adicione mapeamentos de cors por:
@Configuration public class Config implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*"); } }
B. Adicione a configuração abaixo ao meu
HttpSecurity
para segurança de primavera.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; } })
Também no caso de um proxy Zuul você pode usar este em vez de A e B (apenas use
HttpSecurity.cors()
para habilitá-lo na segurança do 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); }
fonte
org.springframework.web.filter.CorsFilter
. Eu tive o mesmo problema enquanto usava de pacotes Catalina acidentalmente.Isso funciona para mim:
@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; } }); //... } //... }
fonte
Para mim, a única coisa que funcionou 100% quando a segurança da mola é usada foi pular toda a penugem adicional de filtros e grãos extras e qualquer "mágica" indireta que as pessoas continuaram sugerindo que funcionava para eles, mas não para mim.
Em vez disso, apenas force-o a escrever os cabeçalhos de que você precisa com um simples
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")); } }
Esta é a maneira mais direta e explícita que encontrei de fazer isso. Espero que ajude alguém.
fonte
Passo 1
Ao anotar o controlador com
@CrossOrigin
anotação permitirá as configurações do CORS.@CrossOrigin @RestController public class SampleController { ..... }
Passo 2
O Spring já tem um CorsFilter, embora você possa simplesmente registrar seu próprio CorsFilter como um bean para fornecer sua própria configuração da seguinte maneira.
@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); }
fonte
verifique este:
@Override protected void configure(HttpSecurity httpSecurity) throws Exception { ... .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() ... }
fonte
Estender a classe WebSecurityConfigurerAdapter e substituir o método configure () em sua classe @EnableWebSecurity funcionaria: Abaixo está um exemplo de classe
@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(); } }); } }
fonte
Se originalmente o seu programa não usa a segurança spring e não pode se permitir uma mudança de código, criar um proxy reverso simples pode resolver o problema. No meu caso, usei o Nginx com a seguinte configuração:
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; } } }
Meu programa escuta : 8080 .
REF: CORS em Nginx
fonte
Isto é o que funcionou para mim.
@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); } }
fonte
Esta resposta copia a resposta @abosancic, mas adiciona segurança extra para evitar exploit CORS .
Dica 1: não reflita a origem de entrada como está, sem verificar a lista de hosts permitidos para acesso.
Dica 2: permitir solicitações credenciadas apenas para hosts na lista de permissões.
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; } } }
fonte
Em nosso aplicativo Spring Boot, configuramos CorsConfigurationSource assim.
A sequência de adição
allowedOrigns
primeiro e depois configuraçãoapplyPermitDefaultValues()
permite que o Spring configure os valores padrão para cabeçalhos permitidos, cabeçalhos expostos, métodos permitidos, etc., então não temos que especificá-los.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); }
fonte
Basta fazer uma única classe como, tudo ficará bem com isso:
@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 { } }
fonte
Isso é o que funcionou para mim, a fim de desabilitar o CORS entre o Spring boot e 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); } }
Tive que modificar a configuração de Segurança também como abaixo:
@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()); }
fonte