我创建了两个Web应用程序 - 客户端和服务应用程序 .
当客户端和服务应用程序部署在同一个Tomcat实例中时,它们之间的交互很顺利 .
但是当应用程序部署到单独的Tomcat实例(不同的机器)时,我在发送服务应用程序的请求时会收到以下错误 .
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401
我的客户端应用程序使用JQuery,HTML5和Bootstrap .
AJAX调用服务如下所示:
var auth = "Basic " + btoa({usname} + ":" + {password});
var service_url = {serviceAppDomainName}/services;
if($("#registrationForm").valid()){
var formData = JSON.stringify(getFormData(registrationForm));
$.ajax({
url: service_url+action,
dataType: 'json',
async: false,
type: 'POST',
headers:{
"Authorization":auth
},
contentType: 'application/json',
data: formData,
success: function(data){
//success code
},
error: function( jqXhr, textStatus, errorThrown ){
alert( errorThrown );
});
}
我的服务应用程序使用Spring MVC,Spring Data JPA和Spring Security .
我已经包含 CorsConfiguration
类,如下所示:
CORSConfig.java
:
@Configuration
@EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*");
}
}
SecurityConfig.java
:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@ComponentScan(basePackages = "com.services", scopedProxy = ScopedProxyMode.INTERFACES)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("authenticationService")
private UserDetailsService userDetailsService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().fullyAuthenticated();
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
}
Spring Security依赖项:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
我正在使用 Apache Tomcat 服务器进行部署 .
3 回答
CORS的预检请求使用没有凭据的HTTP
OPTIONS
,请参阅Cross-Origin Resource Sharing:您必须允许HTTP
OPTIONS
的匿名访问 .您修改(和简化)的代码:
从Spring Security 4.2.0开始,您可以使用内置支持,请参阅Spring Security Reference:
@覆盖
protected void configure(HttpSecurity http)抛出Exception {
HTTP
//默认使用名为corsConfigurationSource的Bean
.cors()和()
...
}
@ beans
CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration配置=新CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList( “https://example.com”));
configuration.setAllowedMethods(Arrays.asList( “GET”, “POST”));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(“/ **”,配置);
返回来源;
}
}
从Spring Security 4.1开始,这是使Spring Security支持CORS的正确方法(在Spring Boot 1.4 / 1.5中也需要):
和:
不要执行以下任何操作,这是尝试解决问题的错误方法:
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
web.ignoring().antMatchers(HttpMethod.OPTIONS);
参考:http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
在我的情况下,我启用了启用OAuth安全性的资源服务器,并且上述任何解决方案都不起作用 . 经过一些调试和谷歌搜索后想出了原因 .
基本上在这个例子中
Ordered.HIGHEST_PRECEDENCE
是关键!https://github.com/spring-projects/spring-security-oauth/issues/938
各种pom依赖项添加了不同类型的过滤器,因此我们可能会遇到基于订单的问题 .