1. 程式人生 > >通過Filter解決跨域問題,可以跨多個域,域可以通過@Value註解取

通過Filter解決跨域問題,可以跨多個域,域可以通過@Value註解取

跨域中不同的域指的是“協議+IP+埠”,只要其中一個不相同就要跨域訪問,為了安全,瀏覽器對於跨域預設是禁止訪問的。現在很多應用的客戶端和服務端是分開的,那麼如何來讓處於不同域的客戶端和服務端實現跨域訪問呢,而且客戶端還可能不止一個,那麼又如何實現多客戶端跨域訪問一個服務端呢?

本文的專案預設使用Maven構建,並使用Spring Security實現安全,首先在src/main/resources目錄建properties檔案,在配置檔案中輸入Access-Control-Allow-Origin=http://192.168.1.1,http://192.168.1.2,http://192.168.1.3,代表客戶端的三個域。(站點必須帶"http://"還有埠號,這裡使用預設埠號80,因此可以省略)

1、通過@Value註解從properties檔案取字串陣列

建一個類,並使用@Component註解標註類,屬性適用@Value註解,注意引用org.springframework.beans包。
@Component
public class MyFilter {

	@Value("${Access-Control-Allow-Origin:}")// 這裡的冒號加與不加感覺沒區別
	String[] originProperties; // 這裡竟然可以直接以陣列接收以逗號分隔的多個屬性

}

這樣就可以直接得到客戶端來源域的陣列。

有的時候@Value取不到值,問題大概有幾個:

  1. resources下有很多xml檔案

  2. 屬性以com.開頭

  3. 如果 resources下同時存在 application.yml,這個檔案裡也必須要有這個屬性

  4. 屬性不能是static


2、通過Filter設定響應頭的"Access-Control-Allow-Origin"

"Access-Control-Allow-Origin"是響應頭的信任站點,只可以設定一個站點。讓剛建的類繼承javax.servlet.Filter,重寫doFilter方法,把所有請求的HttpServletResponse的Header的"Access-Control-Allow-Origin"設定信任站點。注意引用javax.servlet包。

httpServletResponse.setHeader("Access-Control-Allow-Origin", "http://192.168.1.72");

當然,通過這種方式只能實現跨一個域。

3、 跨多個域

"Origin"屬性表示請求頭的請求來源,通過第1步的方法已經從properties取多個域名的陣列,通過下邊這句程式碼來取得訪問來源,判斷該訪問來源是否在域名陣列中。

<pre name="code" class="java"><span style="white-space:pre">	</span>// 解決跨域問題,把幾個客戶端來源新增信任
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		String curOrigin = httpRequest.getHeader("Origin");
		System.out.println("當前訪問來源是:"+curOrigin);
		// 如果當前訪問來源在application.properties的Access-Control-Allow-Origin配置範圍內,則允許訪問,否則不允許
		if(curOrigin != null) {
			for (int i = 0; i < originProperties.length; i++) {
				//System.out.println("允許跨域訪問的來源是:"+originProperties[i]);
				if(curOrigin.equals(originProperties[i])) {
					httpResponse.setHeader("Access-Control-Allow-Origin", curOrigin);
				}
			}
		} else { // 對於無來源的請求(比如在瀏覽器位址列直接輸入請求的),那就只允許我們自己的機器可以吧
			httpResponse.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1");
		}
		httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT,HEAD");
		// 請求來自哪個域,我就允許哪個域的來源,也就是說允許所有域訪問服務,這也太不安全了
		//if(httpRequest.getHeader("Origin") != null){
		//	httpResponse.setHeader("Access-Control-Allow-Origin", curOrigin);
		//}
		
		// 這句千萬別忘,讓Filter按預設方式處理請求和響應,如果沒寫,那麼response裡沒有body
		chain.doFilter(request, response);
	}

這樣就會根據使用者的來源來設定允許跨域的來源:"Access-Control-Allow-Origin"。

4. 跨域中的安全

如果你使用Spring Security,它是通過過濾器鏈來實現安全的,它的過濾器預設優先順序最高,會第一個攔截請求。因此要把我們自己的跨域的Filter設定成優先順序最高:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
	MyFilter myFilter;

	protected void configure(HttpSecurity http) throws Exception {
		//Spring Security的filter預設再整個filter chain的最前邊,因此需要把我們自己寫的跨域的filter放在最前邊
		http.addFilterBefore(myFilter, ChannelProcessingFilter.class)
	 	    .authorizeRequests()
	 	    .anyRequest().authenticated();
	}
}

本文不是重點講Spring Security,因此只是寫了簡單的例子。通過這種方式,而不使用JSONP,我們就可以實現跨多個域了。