由于公司的新項目決定使用java 與angularjs進行開發(fā),前后端分離,因此我需要對前端的請求框架進行搭建。接觸angularjs是在上一個ionic的項目,感覺angularjs的mvc架構非常出色,尤其對大型項目很有好處。
angularjs的請求是通過ajax的,在請求過程中,有一個很麻煩的問題,那就是跨域。在這次的項目中,打算在請求的http header中加入自token進行身份驗證,結果遇到了麻煩。于是現(xiàn)在把解決方案寫下來,希望能給自己留一個記憶,并希望能夠幫助遇到問題的小伙伴。
首先,任何請求都需要在http header中加入token,我了解到了angularjs里的一個很重要的機制:攔截。
可在config中加入:
config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
$httpProvider.defaults.headers.mbs-common['X-Requested-With']; }]);
factory中加入sessionInjector如下:
testService.factory('sessionInjector', ['$localStorage','HOST', function($localStorage,HOST)
{ var sessionInjector = {
request:
function(config) {
config.headers = config.headers || {};
if(config.url.indexOf(HOST.URL)>=0) {
if ($localStorage.u && $localStorage.u.token) {
config.headers.token = $localStorage.u.token;
}
}
return config;
}
};
return sessionInjector;}]);
代碼寫好,跑起來一看,token已經成功加入到http header中,很好!但是請求出錯了!報:No 'Access-Control-Allow-Origin' 錯誤。看到這個報錯,我的第一反映是跨域問題。但是我前一個ionic+angularjs項目在java端已經解決了跨域問題了,而這個新的項目用的框架與上個項目幾乎一樣的,為何還會跨域呢?我感覺自己對跨域的理解不夠徹底,于是又開始漫長的搜索,各種google,stackoverflow。終于找到了跨域的原因,wiki上說得很清楚:https://en.wikipedia.org/wiki/Cross-origin_resource_sharing 。
核心問題在于跨域時會先進行一個OPTIONS請求,如果成功了才會進行GET或POST請求。于是馬上通過chrome查看OPTIONS請求的結果,果然OPTIONS報403錯誤。

看到OPTIONS請求的403錯誤,我就蒙了!怎么回事呢,我明明在java代碼中寫了一個CorsFilter,并且我也加入了跨域的允許代碼:
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
httpResponse.setContentType("text/html;charset=UTF-8");
httpResponse.setHeader("Access-Control-Allow-Origin",httpRequest.getHeader("Origin"));
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
httpResponse.setHeader("Access-Control-Max-Age", "0");
httpResponse.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,authorization,mbs_token");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("XDomainRequestAllowed","1");
chain.doFilter(request, response);
網上說:Access-Control-Allow-Origin不能用"*",而是需要指定請求的域名:httpRequest.getHeader("Origin")
然后我再跑了一次,查看后臺日志,發(fā)現(xiàn)CorsFilter根本就沒有跑!
于是又經過了各種google與stackoverflow,轉眼兩天下來了,還是無果!心里真是有說不出的郁悶!??!
然后,我懷疑是不是tomcat層面攔截了OPTIONS請求,于是看了一下TOMCAT的請求日志,發(fā)現(xiàn)果然有攔截記錄,可是不應該TOMCAT層攔截的呀!于是我做了一個HELLO WORD 發(fā)現(xiàn)跨域是沒問題的,于是放棄這個想法,再繼續(xù)找原因!
最后,我懷疑是不是Web.xml有問題,這個web.xml有部分是直接復制人家配置好的,沒有好好去分析各個節(jié)點,于是我一句一句核對,終于發(fā)現(xiàn)一個關鍵地方:
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint></auth-constraint>
</security-constraint>
沒錯,就是這里?。?br> <http-method>OPTIONS</http-method>
我立即把這段文字刪除了,然后跑了一下,奇跡出現(xiàn)了,跨域解決了?。?!當時心里真是無比興奮,從開始解決到完工,我整整花了三天,而且后兩天是雙休!
于是我花時間了解一下這個關鍵的<security-constraint>,網上對于這個節(jié)點的說明比較少,經過google的查閱發(fā)現(xiàn):
web.xml中<security-constraint> 的子元素 <http-method> 是可選的,如果沒有 <http-method> 元素,這表示將禁止所有 HTTP 方法訪問相應的資源。
子元素 <auth-constraint> 需要和 <login-config> 相配合使用,但可以被單獨使用。如果沒有 <auth-constraint> 子元素,這表明任何身份的用戶都可以訪問相應的資源。也就是說,如果 <security-constraint> 中沒有 <auth-constraint> 子元素的話,配置實際上是不起中用的。
<security-constraint> 是java servlet的安全配置,
可參考:http://openhome.cc/Gossip/ServletJSP/DeclarativeSecurityBasic.html
關鍵的一句:如果加入了 <auth-constraint> 子元素,但是其內容為空,這表示所有身份的用戶都被禁止訪問相應的資源。
其實在解決的過程中我也注意到過:<http-method>OPTIONS</http-method>,當時我的理解是,允許這個求請方法!
總結:任何技術疑問都不能馬虎,要徹底理解才行?。?/p>