解決在CAS中的跨域請求問題

1.場景描述

現有微服務a和微服務b,它們均通過一個CAS服務器C實現單點登錄。在微服務a的web頁面需要觸發一段js腳本,這段腳本會通過http的get請求來調用微服務b的相關服務,實現對數據庫的相關操作。

2.問題描述與分析

微服務a對微服務b的跨域請求問題

由於微服務a和微服務b處於不同的域(IP或端口不完全相同),又由於微服務a調用微服務b是通過js腳本,所以瀏覽器會對這樣的跨域請求做限制。發起跨域請求的瀏覽器會在請求報文頭裏的Origin字段裏指明源域(即它本身)的地址。響應的一方需要在應答報文頭的Access-Control-Allow-Origin字段裏指明被允許訪問的域的地址,最後發起跨域請求的瀏覽器會先檢驗這個字段,再決定是否解析及處理該響應報文。所以該場景就需要我們在響應一方的報文頭裏加入Control-Allow-Origin字段及值。

微服務a與CAS服務器C之間的跨域請求問題

當微服務a能夠正常解析並處理微服務b的響應報文的時候,會發現這是一個狀態碼爲302的重定向報文。因爲微服務b在解析到微服務a的請求報文的url的時候,會被微服務b中CAS的過濾器匹配到,做身份驗證的操作,即重定向到CAS服務器做認證。此時,微服務a會發起第二次跨域請求,響應的域是CAS服務器C處在的域。所以這時,我們同樣需要在服務器C上做類似上述在微服務b上的操作。

身份驗證時,對cookie的操作問題

由於CAS做驗證,需要獲取其cookie。所以微服務a發起的跨域請求,必須指明要帶上cookie。

3.問題解決

微服務a發起跨域請求時,帶上cookie,即{‘withCredentials’:true}。這裏用的AngularJS

XMLHttpRequest.withCredentials 屬性是一個Boolean類型,它指示了是否該使用類似cookies,authorization headers(頭部授權)或者TLS客戶端證書這一類資格證書來創建一個跨站點訪問控制(cross-site Access-Control)請求。在同一個站點下使用withCredentials屬性是無效的。

$http.get('http://localhost:9107/xxx,{'withCredentials':true}).success(
	function(response){
		 if(response.success){
			 alert("成功");
		 }else{
			 alert(response.message);
		 }		
	}	
);
微服務b響應跨域請求時,帶上Access-Control-Allow-Origin
  • 微服務b下創建一個過濾器(這個過濾器要在安全框架的過濾器或者CAS的過濾器之前)
    web.xml

    <filter>  
    	<filter-name>CORSFilter</filter-name>  
    	<filter-class>com.xzt.filter.CORSFilter</filter-class>  
    	<init-param>
    		<param-name>encoding</param-name>
    		<param-value>utf-8</param-value>
    	</init-param>
     </filter> 
    <filter-mapping> 
        <filter-name>CORSFilter</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping>
    
  • 新建java的過濾器類

    public class CORSFilter implements Filter{
    
    	@Override
    	public void init(FilterConfig filterConfig) throws ServletException {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    			throws IOException, ServletException {
    		System.out.println("進入過濾器CORSFilter");
    		HttpServletResponse resp = (HttpServletResponse)response;
    		HttpServletRequest req = (HttpServletRequest)request;
    		String origin = req.getHeader("Origin");
    		//這種方式可以靈活的配置Access-Control-Allow-Origin
    		//Access-Control-Allow-Origin的值不要寫*號,因爲對於帶有cookie的跨域請求,瀏覽器不支持這種寬鬆的策略
    		resp.setHeader("Access-Control-Allow-Origin", origin);//可以訪問的域
    		resp.setHeader("Access-Control-Allow-Credentials", "true");//如果操作cookie,必須加上這句話
    		chain.doFilter(request, response);
    	}
    
    	@Override
    	public void destroy() {
    		// TODO Auto-generated method stub
    		
    	}
    
    }
    
在CAS服務器C上,配置跨域請求
  • 在cas的工程路徑下修改web.xml,添加過濾器
    cas/WEB-INF/web.xml

    <filter>
        <filter-name>CORS</filter-name>
        <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
        <init-param>
         <param-name>cors.allowOrigin</param-name>
            <param-value>*</param-value>
        </init-param>
        <init-param>
         <param-name>cors.supportedMethods</param-name>
            <param-value>GET, POST, HEAD, PUT, DELETE</param-value>
        </init-param>
        <init-param>
         <param-name>cors.supportedHeaders</param-name>
            <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value>
        </init-param>
        <init-param>
            <param-name>cors.exposedHeaders</param-name>
            <param-value>Set-Cookie</param-value>
        </init-param>
        <init-param>
            <param-name>cors.supportsCredentials</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CORS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  • 添加依賴
    cors-filter-1.7.0.wso2v1.jar java-property-utils-1.9.jar 這兩個包放到
    cas/WEB-INF/lib路徑下

    注:cors-filter-1.7.0.wso2v1.jar的原始下載路徑找不到了。這裏是我上傳的 cas實現單點登錄的相關依賴

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章