轉載:https://blog.csdn.net/lmy86263/article/details/51724221
由於工程合作開發的需要,后台的應用要能支持跨域訪問,但是在這個跨域訪問“時好時壞”,我們這幫屌絲所知道的就是加上兩個jar包,然后聲明一下Filter,感覺很簡單的有沒有!!感覺自己很牛X有沒有!!全是幻覺!!要不然怎么會時好時壞!!為了深入了解這個問題,決定寫這篇文章總結一下。
要知道跨域請求就要先了解同源策略,那么什么是同源?什么是不同源?簡單來說就是,如果兩個資源,包括HTML頁面、JavaScript腳本、css樣式,對應的協議、域名和端口完全相同,那么這兩個資源就是同源的,Same-origin policy解釋得很清楚。那么同源策略的意思就是一個源中的資源訪問另外一個源中的資源,在在這一點上JavaScript的跨站資源訪問表現的更加明顯。在HTML5之前Ajax是不允許發起跨站請求的,如果有需求的話,可以使用JSONP等方法,但是缺點就是:
- 只支持
Get
不支持Post
; - 本質上是腳本注入的方式,存在安全隱患;
還有JSONP的優缺點,但是自從HTML5出現之后,提出了CORS(跨站資源共享)這種方式,極大地方便了日常的開發。如果要理解CORS的工作原理,首先要知道跨域訪問是怎么被禁止的,之前本屌絲一直以為是前台的跨域訪問請求不能發出去,是實現同源策略的瀏覽器攔截了該請求,但是后來才知道瀏覽器並沒有攔截請求,而是攔截了服務器端返回的響應。
所以如果要支持跨域訪問,需要瀏覽器和后台服務器程序同時支持,如果這兩個條件不能同時滿足,則還是不能支持跨域訪問。
用於CORS中的Http的首部有如下幾個:
-
響應頭
- Access-Control-Allow-Origin: 允許跨域訪問的域,可以是一個域的列表,也可以是通配符”*”;
- Access-Control-Allow-Methods: 允許使用的請求方法,以逗號隔開;
- Access-Control-Allow-Headers: 允許自定義的頭部,以逗號隔開,大小寫不敏感;
- Access-Control-Expose-Headers: 允許腳本訪問的返回頭,請求成功后,腳本可以在XMLHttpRequest中訪問這些頭的信息
- Access-Control-Allow-Credentials: 是否允許請求帶有驗證信息,XMLHttpRequest請求的withCredentials標志設置為true時,認證通過,瀏覽器才將數據給腳本程序。
- Access-Control-Max-Age: 緩存此次請求的秒數。在這個時間范圍內,所有同類型的請求都將不再發送預檢請求而是直接使用此次返回的頭作為判斷依據,非常有用,大幅優化請求次數;
-
請求頭
- Origin: 普通的HTTP請求也會帶有,在CORS中專門作為Origin信息供后端比對,表明來源域,要與響應頭中的Access-Control-Allow-Origin相匹配才能進行跨域訪問;
- Access-Control-Request-Method: 將要進行跨域訪問的請求方法,要與響應頭中的Access-Control-Allow-Methods相匹配才能進行跨域訪問;
- Access-Control-Request-Headers: 自定義的頭部,所有用setRequestHeader方法設置的頭部都將會以逗號隔開的形式包含在這個頭中,要與響應頭中的Access-Control-Allow-Headers相匹配才能進行跨域訪問
從支持跨域訪問的范圍說,可以有整個服務器、單個應用程序、單個接口。
1、在整個服務器上支持跨域訪問
在服務器上可以部署多個應用程序,如果在整個服務器的范圍上支持跨域訪問,那么在所有應用程序上都不用單獨配置了,直接使用服務器的配置即可,這里通過tomcat來進行舉例。在Tomcat7之后包括tomcat7才開始支持CORS,之前的版本是不支持的。配置CORS,首先配置Tomcat中的conf\web.xml
,在其中添加一個Filter聲明,如下:
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
這樣就能應用到在該Tomcat中部署的所有的應用程序的接口上。然后在Tomcat的lib文件夾下加入兩個jar包:cors-filter-2.5.jar和java-property-utils-1.9.jar,這兩個jar包對應的maven依賴如下:
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.5</version>
</dependency>
在Tomcat服務器上配置完成后,在自己的應用程序上就不要再配置有關跨域訪問的內容了,這樣會造成訪問相應的接口時不支持跨域訪問。
這種配置方式覆蓋面太廣,有些部署在該服務器下的應用程序根本不需要支持跨域訪問,就會帶來一些安全問題,所以其實不推薦使用這種配置方式。
2、在整個后台應用上支持跨域訪問
在這個應用程序的范圍內支持跨域訪問是比較常見的使用方法,而且這種配置有很多實現方式。
2.1、在web.xml
中配置
將在上述服務器中的web.xml上配置的filter,配置到自己的工程中。然后加入maven依賴,如下類似:
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.5</version>
</dependency>
這樣就完成了對跨域訪問請求的支持,如果並不想對所有的請求都支持跨域訪問,則可以在Filter的url-pattern中改變匹配到的url地址。
2.2、通過WebMvcConfigurer
###
在Spring中,使用這個接口可以通過定義回調方法來進行一些Spring MVC中要用到的配置,在里面用來支持CORS的方法是addCorsMappings(CorsRegistry registry)
,我們並不直接使用這個接口而是使用它的抽象實現類WebMvcConfigurerAdapter
,這個類中給我們提供了WebMvcConfigurer
接口中方法的空實現,我們可以直接填上自己的業務邏輯就可以直接使用。在這個回調方法中的參數是CorsRegistry
,這個類可以幫助我們為相應的url地址提供CORS配置,關於這個方法可以放到Application啟動類中,也可以單獨放到一個類中。
在Application啟動類中使用該方法,只需要在Application啟動類中加入如下代碼即可。
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}
獨自建立新類使用該方法,則需要配置一些注解,如下:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.springboot.demo" })
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/*").allowedOrigins("http://localhost:8070");
}
}
與在Application啟動類中使用相比多了@Configuration
和@EnableWebMvc
注解,這是因為@SpringBootApplication
已經包含上述兩個注解了,所以不需要重新加入。
在CorsRegistry
中調用addmapping
后會得到CorsRegistration
類,這時候就可以使用鏈式調用可以對這個CORS配置進行origin、method、header、maxage等的限制,這里就不展開了。
2.3 自定義Filter
所有的方法歸根結底就是攔截對接口的訪問,所以如果你不想麻煩並且想深入了解CORS到底是怎樣工作的,可以通過自定義Filter來實現,其實這也沒有什么難的,就是在攔截器上通過對請求和響應加上一些Headers,這里就不說了。
3、在單個接口上支持跨域訪問
跨域訪問所支持的最小的范圍就是在Controller以及對應的方法上,這里使用@CrossOrigin
注解來完成相應的配置,首先來看看這個注解的源代碼。
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
String[] DEFAULT_ORIGINS = { "*" };
String[] DEFAULT_ALLOWED_HEADERS = { "*" };
boolean DEFAULT_ALLOW_CREDENTIALS = true;
long DEFAULT_MAX_AGE = 1800;
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials() default "";
long maxAge() default -1;
}
從該注解中可以看到,它可以使用在方法上,也可以使用在類上,並且已經完全覆蓋了CORS提出的響應頭首部。
- 使用在類上,一般使用在有
@Controller
或者@RestController
注解的類上,這樣在該類上全部遵從該注解提供的關於CORS的配置。 - 使用在方法上,該CORS的配置只是針對該指定的方法;
默認情況下,@CrossOrigin
支持所有的origin和所有的headers來進行跨域訪問,而且它所限制的方法是由@Requestmapping
中的method
屬性來提供的。
如果查看源碼的話是可以發現的,在CorsRegistry
中使用addMapping()
配置映射時,返回的CorsRegistration
類中對origin、header、Credentials的配置使用的就是@CrossOrigin
的默認配置。實例如下:
@CrossOrigin(origins={"http://localhost:8070"}, methods={RequestMethod.GET, RequestMethod.POST})
@RequestMapping("/")
@ResponseBody
public String getInformation(){
restHandleService.getRestInformation();
return "spring boot";
}