前后端分離,shiro谈秫,跨域問題

跨域問題是進(jìn)行ajax請求時扒寄,js文件所在域名,與請求域名不一致拟烫,觸發(fā)跨域限制该编。

解決思路是利用filter修改response header信息。shiro本身依賴fiter實(shí)現(xiàn)硕淑。所以shiro時filter是shiro特定的filter课竣。

可以在nginx容器中嘉赎,配置filter。

還可以用nginx的反向代理于樟,實(shí)現(xiàn)相同域名公条,就不會出現(xiàn)跨域問題。





前后端分離

? ? 前后端分離的好處

? ? 最大的好處就是前端JS可以做很大部分的數(shù)據(jù)處理工作迂曲,對服務(wù)器的壓力減小到最小赃份。

? ? 后臺錯誤不會直接反映到前臺,錯誤接秒較為友好奢米。

? ? 由于后臺是很難去探知前臺頁面的分布情況,而這又是JS的強(qiáng)項(xiàng)纠永,而JS又是無法獨(dú)立和服務(wù)器進(jìn)行通訊的鬓长。所以單單用后臺去控制整體頁面,又或者只靠JS完成效果尝江,都會難度加大涉波,前后臺各盡其職可以最大程度的減少開發(fā)難度。

? ? 個人理解上存在兩種解釋

? ? 第一種只是單純的前后端分離炭序,實(shí)在物理層面上的啤覆,將View層的任務(wù)分配給前端,Controller和Model層給后端惭聂,這就存在一個問題窗声,就是后端的同事需要去關(guān)注前端的展示邏輯、而前端只要存在變化辜纲,后端的數(shù)據(jù)處理需要做相應(yīng)的改變笨觅。

? ? 第二種是基于職責(zé)層面上的分離,將View和Controller層分配的前端耕腾,后端只處理Model和業(yè)務(wù)處理见剩,這就需要Controller使用Node.js,M-V-C對應(yīng)的是JAVA/PHP-JAVASCRIPT扫俺、HTML苍苞、CSS-Node.js。

跨域問題存在的原因

? ? ? ? 隨著前后端分離技術(shù)的越來越盛行狼纬,跨域問題也逐漸凸顯了出來羹呵。跨域問題的根本原因:因?yàn)闉g覽器收到同源策略的限制疗琉,當(dāng)前域名的js只能讀取同域下的窗口屬性担巩。什么叫做同源策略?就是不同的域名, 不同端口, 不同的協(xié)議不允許共享資源的没炒,保障瀏覽器安全涛癌。同源策略是針對瀏覽器設(shè)置的門檻犯戏。如果繞過瀏覽就能實(shí)現(xiàn)跨域,所以說早期的跨域都是打著安全路數(shù)的擦邊球拳话,都可以認(rèn)為是 hack 處理先匪。這一段是我從別的地方cp過來的,大家將就著看吧弃衍。

? ? ? ? 這里要注意的是呀非,只有訪問類型為xhr(XMLHttpRequest)的才會出現(xiàn)跨域。

跨域問題的解決方案

? ? 修改瀏覽器的設(shè)置

? ? 修改請求的方式:jsonp

? ? CORS

修改瀏覽器配置解決跨域

? ? ? ? 以Google Chrome為例镜盯,瀏覽器以

? ? "C:\ProgramFiles(x86)\Google\Chrome\Application\chrome.exe"

? ? ? ? --disable-web-security--user-data-dir

中模式打開岸裙,右鍵點(diǎn)擊瀏覽器快捷方式,在目標(biāo)中輸入上述代碼即可解決(不推薦)速缆。

使用jsonp解決跨域

JQuery中的正常AJAX請求代碼片段

? ? $("#demo1").click(function(){

? ? ? ? $.ajax({

? ? ? ? ? ? url : 'http://www.tpadmin.top/Index/Test/crossDomain',

? ? ? ? ? ? data : {},

? ? ? ? ? ? type : 'get',

? ? ? ? ? ? success : function (res) {

? ? ? ? ? ? ? ? //No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1' is therefore not allowed access. 在執(zhí)行時候報出的錯誤降允,這代表了跨域錯誤

? ? ? ? ? ? ? ? alert(res);

? ? ? ? ? ? }

? ? ? ? });

? ? });

JQuery中的使用JSONP的AJAX請求代碼:

? ? $("#demo2").click(function(){

? ? ? ? $.ajax({

? ? ? ? ? ? url : 'http://www.tpadmin.top/Index/Test/crossDomain',

? ? ? ? ? ? data : {},

? ? ? ? ? ? type : 'get',

? ? ? ? ? ? dataType : 'jsonp',

? ? ? ? ? ? success : function (res) {

? ? ? ? ? ? ? ? alert(res);

? ? ? ? ? ? }

? ? ? ? });

? ? });

這時候我們看到 請求的網(wǎng)址自動變成了

http://www.tpadmin.top/Index/Test/crossDomain?callback=jQuery331015214102388989237_1534993962395&_=1534993962396

這是為什么呢?原來由于跨域訪問的只限制xhr類型的請求(上文中已經(jīng)說了)艺糜,所以js中就利用了這一特點(diǎn)剧董,讓服務(wù)端不在返回的是一個JSON格式的數(shù)據(jù),而是返回一段JS代碼破停,將JSON的數(shù)據(jù)以參數(shù)的形式傳遞到這個函數(shù)中翅楼,而函數(shù)的名稱就是callback參數(shù)的值,所以我們還需要修改服務(wù)端的代碼真慢,代碼如下:

? ? <?php

? ? ? ? $callback = isset($_GET['callback'])?$_GET['callback']:'';

? ? ? ? if (!empty($callback)) {

? ? ? ? ? ? $arr = ['code' => 200, 'name' => 'cui'];

? ? ? ? ? ? $data = json_encode($arr);

? ? ? ? ? ? exit($callback . '(' . $data . ')');

? ? ? ? }

? ? ?>

OK毅臊,現(xiàn)在問題解決了,但是JSONP存在著諸多限制黑界,下面將列出兩個個我知道的:

? ? JSONP只支持GET請求褂微,什么?你要提交表單园爷,sorry宠蚂,此路不通

? ? 它只支持跨域HTTP請求

雖然只有兩個,但是讓很多人不得不放棄它童社,所以出現(xiàn)了下面的解決辦法求厕。

CORS解決跨域

回歸問題本質(zhì),跨域問題為什么會產(chǎn)生扰楼,上面已經(jīng)說了呀癣,是由于瀏覽器的限制,那么在執(zhí)行過程中有什么不同弦赖,下面兩張度分析一下(主要看請求頭的部分):

這是非跨域請求

這是跨域請求

這時我們發(fā)現(xiàn)跨域訪問的請求頭中存在Origin的字段项栏,用來記錄當(dāng)前的訪問域名,我們可以再服務(wù)端增加一個響應(yīng)頭Access-Control-Allow-Origin來告訴瀏覽器我們支持它獲取就可以了蹬竖,代碼實(shí)現(xiàn):

? ? <?php

? ? header('Access-Control-Allow-Origin:http://127.0.0.1');

? ? $arr = ['code' => 200, 'name' => 'cui'];

? ? echo $data = json_encode($arr);

? ? ?>

那如果我有多個域名進(jìn)行跨域訪問呢

? ? <?php

? ? $requestHeader = getallheaders();

? ? $origin = isset($requestHeader['Origin'])?$requestHeader['Origin']:'';

? ? switch ($origin) {

? ? ? ? case 'http://127.0.0.1':

? ? ? ? ? ? header('Access-Control-Allow-Origin:http://127.0.0.1');

? ? ? ? ? ? break;

? ? ? ? case 'http://localhost':

? ? ? ? ? ? header('Access-Control-Allow-Origin:http://localhost');

? ? ? ? ? ? break;

? ? ? ? default:

? ? ? ? ? ? break;

? ? }

? ? $arr = ['code' => 200, 'name' => 'cui'];

? ? echo $data = json_encode($arr);

? ? //注意沼沈,不支持下面這種寫法

? ? //header('Access-Control-Allow-Origin:http://localhost,http://127.0.0.1');

? ? ?>

或者直接寫成(很不安全流酬,不推薦這么寫)

? ? <?php

? ? header('Access-Control-Allow-Origin:*');

? ? $arr = ['code' => 200, 'name' => 'cui'];

? ? echo $data = json_encode($arr);

? ? ?>

到這里,其實(shí)已經(jīng)結(jié)束了列另,但還有一些其他的特殊情況

? ? 請求方法不是GET芽腾、HEAD、POST

? ? 請求頭中存在自定義頭

? ? Content-Type不是text/plain页衙、multipart/form-data摊滔、application/x-www-form-urlencoded

? ? 希望獲取到服務(wù)端的Cookie

為了應(yīng)對種種限制,我們再來看一段代碼

? ? $("#demo1").click(function(){

? ? ? ? $.ajax({

? ? ? ? ? ? url : 'http://cui.tpadmin.top/crossDomain.php',

? ? ? ? ? ? data : {},

? ? ? ? ? ? type : 'PUT',

? ? ? ? ? ? contentType : 'application/json',

? ? ? ? ? ? header: {

? ? ? ? ? ? ? ? token:'asdfgqwerttyyazxcvbvb'

? ? ? ? ? ? },

? ? ? ? ? ? success : function (res) {

? ? ? ? ? ? ? ? alert(res);

? ? ? ? ? ? }

? ? ? ? });

? ? });

雖然我們在服務(wù)端加入了Access-Control-Allow-Origin響應(yīng)頭店乐,但是如果出現(xiàn)上面所說的情況時艰躺,我們需要做一些特殊的設(shè)置,修改服務(wù)端代碼為:

? ? <?php

? ? //這里增加了兩行代碼

? ? header('Access-Control-Allow-Headers:Content-Type');

? ? header('Access-Control-Allow-Methods:PUT');

? ? $requestHeader = getallheaders();

? ? $origin = isset($requestHeader['Origin'])?$requestHeader['Origin']:'';

? ? switch ($origin) {

? ? ? ? case 'http://127.0.0.1':

? ? ? ? ? ? header('Access-Control-Allow-Origin:http://127.0.0.1');

? ? ? ? ? ? break;

? ? ? ? case 'http://localhost':

? ? ? ? ? ? header('Access-Control-Allow-Origin:http://localhost');

? ? ? ? ? ? break;

? ? ? ? default:

? ? ? ? ? ? break;

? ? }

? ? $arr = ['code' => 200, 'name' => 'cui'];

? ? echo $data = json_encode($arr);

? ? ?>

這里雖然成功了眨八,但是我們發(fā)現(xiàn)每次請求的時候會出現(xiàn)兩條請求記錄(這個可不是我請求了兩次腺兴,看下面截圖)

這里我們需要進(jìn)行一下區(qū)分(簡單請求模式與非簡單請求模式)

? ? 請求方法只能為GET、HEAD踪古、POST

? ? 請求頭中無自定義頭

? ? Content-Type必須為text/plain、multipart/form-data券腔、application/x-www-form-urlencoded

符合以上條件的為簡單請求伏穆,否則為非簡單請求,注意纷纫,非簡單請求中枕扫,瀏覽器會默認(rèn)發(fā)送兩條請求,第一條為預(yù)檢請求(OPTION)辱魁,第二條為AJAX的請求烟瞧,處于服務(wù)器的性能考量,我們需要將預(yù)檢命令進(jìn)行緩存染簇,而不是每次都執(zhí)行預(yù)檢請求参滴,我們可以修改代碼如下:

? ? <?php

? ? header('Access-Control-Allow-Headers:Content-Type');

? ? header('Access-Control-Allow-Methods:PUT');

? ? //看這里

? ? header('Access-Control-Max-Age:3600');

? ? $requestHeader = getallheaders();

? ? $origin = isset($requestHeader['Origin'])?$requestHeader['Origin']:'';

? ? switch ($origin) {

? ? ? ? case 'http://127.0.0.1':

? ? ? ? ? ? header('Access-Control-Allow-Origin:http://127.0.0.1');

? ? ? ? ? ? break;

? ? ? ? case 'http://localhost':

? ? ? ? ? ? header('Access-Control-Allow-Origin:http://localhost');

? ? ? ? ? ? break;

? ? ? ? default:

? ? ? ? ? ? break;

? ? }

? ? $arr = ['code' => 200, 'name' => 'cui'];

? ? echo $data = json_encode($arr);

? ? ?>

這次我請求了兩次,發(fā)現(xiàn)第二次請求沒有發(fā)送預(yù)檢請求锻弓,新增加的代碼代表允許緩存的時間(3600S)砾赔。

另外還有一些常用的方法,我都放到這里了青灼,有需要的小伙伴可以參考

? ? <?php

? ? //支持Cookie

? ? header('Access-Control-Allow-Credentials:true');

? ? //支持自定義頭

? ? header('Access-Control-Allow-Headers:token,Content-Type,...');?

由于篇幅有限暴心,Cookie的例子小伙伴們自己做就可以了,使用Cookie的時候需要注意Access-Control-Allow-Origin不可設(shè)置為*杂拨。

服務(wù)軟件實(shí)現(xiàn)跨域

上面的例子實(shí)現(xiàn)了跨域訪問专普,但是如果我不想在代碼中修改,還有其他的方法嗎弹沽,當(dāng)然有了檀夹,我們可以直接修改服務(wù)軟件筋粗,下面將從最常用的Apache與Nignx兩個方面說明

基于Apache的服務(wù)

? ? <VirtualHost *:80>

? ? ? ServerName localhost

? ? ? ServerAlias localhost

? ? ? DocumentRoot "${INSTALL_DIR}/www"


? ? ? Header always set Access-Control-Allow-Header "PUT"

? ? ? Header always set Access-Control-Allow-Credentials "true"

? ? ? Header always set Access-Control-Max-Age "3600"

? ? ? #這里設(shè)置的為全匹配

? ? ? Header always set Access-Control-Allow-Origin "expr=%{req:origin}"

? ? ? #這里設(shè)置的為全匹配

? ? ? Header always set Access-Control-Allow-Headers "expr=%{req:Access-Control-Allow-Headers}"


? ? ? <Directory "${INSTALL_DIR}/www/">

? ? ? ? Options +Indexes +Includes +FollowSymLinks +MultiViews

? ? ? ? AllowOverride All

? ? ? ? Require local

? ? ? </Directory>

? ? </VirtualHost>

這里說明一下,由于Apache都是模塊管理击胜,所以這里要想使用Header的話亏狰,需要加載mod_headers.so這個模塊,去看一下自己的主配置文件偶摔,這個模塊是否開啟暇唾,如果沒有開啟,apache會啟動失敗的辰斋。

LoadModule headers_module modules/mod_headers.so

由于本人學(xué)藝不精策州,Apache的配置中的判斷沒有完全鬧懂,所以這里沒有進(jìn)行區(qū)分的匹配宫仗,如果有大神知道够挂,請指導(dǎo)一下,不勝感激藕夫。

基于Nignx的服務(wù)

在nignx服務(wù)的配置中修改為如下代碼(注意位置孽糖,這個例子是修改本地的nignx,沒有域名毅贮,如果沒有熟悉過nignx的小伙伴可以自行百度)办悟。

? ? server {

? ? ? ? listen? ? ? 80 default_server;

? ? ? ? listen? ? ? [::]:80 default_server;

? ? ? ? server_name? _;

? ? ? ? root? ? ? ? /usr/share/nginx/html;

? ? ? ? # Load configuration files for the default server block.

? ? ? ? include /etc/nginx/default.d/*.conf;

? ? ? ? location / {

? ? ? ? ? ? ? ? #支持其他請求

? ? ? ? ? ? ? ? add_header Access-Control-Allow-Methods PUT;

? ? ? ? ? ? ? ? #設(shè)置預(yù)檢請求的緩存

? ? ? ? ? ? ? ? add_header Access-Control-Max-Age 3600;

? ? ? ? ? ? ? ? #允許Cookie

? ? ? ? ? ? ? ? add_header Access-Control-Allow-Credentials true;

? ? ? ? ? ? ? ? #這里最好做判斷,怕麻煩的話就寫*滩褥,但是不建議

? ? ? ? ? ? ? ? if ($http_origin = http://localhost){

? ? ? ? ? ? ? ? ? ? ? ? add_header Access-Control-Allow-Origin http://localhost;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? if ($http_origin = http://127.0.0.1){

? ? ? ? ? ? ? ? ? ? ? ? add_header Access-Control-Allow-Origin http://127.0.0.1;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? #為了方便病蛉,這樣寫了

? ? ? ? ? ? ? ? add_header Access-Control-Allow-Headers $http_access_control_request_headers;

? ? ? ? ? ? ? ? if ($request_method = OPTIONS){

? ? ? ? ? ? ? ? ? ? ? ? return 200;

? ? ? ? ? ? ? ? }

? ? ? ? }

? ? ? ? error_page 404 /404.html;

? ? ? ? ? ? location = /40x.html {

? ? ? ? }

? ? ? ? error_page 500 502 503 504 /50x.html;

? ? ? ? ? ? location = /50x.html {

? ? ? ? }

? ? }

另外再說一句,如果您使用的是JAVA的話瑰煎,可以嘗試一下正反向代理铺然。



背景

最近做一個前后端分離的項(xiàng)目時,使用shiro做權(quán)限管理時遇到跨域問題,這里做一下記錄酒甸。

魄健,,


原因

后端通過Shiro配置URL過濾,

shiroFilterFactoryBean.setLoginUrl("/unauth");

默認(rèn)對于沒有授權(quán)的訪問請求會redirect至LoginUrl.但在跨域訪問時,redirect失敗.原因是基于安全考慮,redirect發(fā)生時Response Header的信息會被清除,導(dǎo)致client端的訪問被server端拒絕.

解決


@Component

public class CORSFilter extends OncePerRequestFilter {

/*

* 在ResponseBodyWrapHandler中已處理跨域問題

* 但是在shiro驗(yàn)證未通過跳轉(zhuǎn)/unauth時, 因?yàn)閞edirect 重定向會丟失所有請求頭插勤,跨域問題重新出現(xiàn)

* */

@Override

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {

HttpServletResponse res = (HttpServletResponse) servletResponse;

res.setContentType("text/html;charset=UTF-8");

res.setHeader("Access-Control-Allow-Origin", "*");

res.setHeader("Access-Control-Allow-Methods", "*");

res.setHeader("Access-Control-Max-Age", "0");

res.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,userId,token");

res.setHeader("Access-Control-Allow-Credentials", "true");

res.setHeader("XDomainRequestAllowed","1");

filterChain.doFilter(servletRequest, servletResponse);

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诀艰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子饮六,更是在濱河造成了極大的恐慌其垄,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卤橄,死亡現(xiàn)場離奇詭異绿满,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)窟扑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進(jìn)店門喇颁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏健,“玉大人,你說我怎么就攤上這事橘霎∧杞” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵姐叁,是天一觀的道長瓦盛。 經(jīng)常有香客問我,道長外潜,這世上最難降的妖魔是什么原环? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮处窥,結(jié)果婚禮上嘱吗,老公的妹妹穿的比我還像新娘。我一直安慰自己滔驾,他們只是感情好谒麦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哆致,像睡著了一般绕德。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沽瞭,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天迁匠,我揣著相機(jī)與錄音剩瓶,去河邊找鬼驹溃。 笑死,一個胖子當(dāng)著我的面吹牛延曙,可吹牛的內(nèi)容都是我干的豌鹤。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼枝缔,長吁一口氣:“原來是場噩夢啊……” “哼布疙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起愿卸,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤灵临,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后趴荸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體儒溉,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年发钝,在試婚紗的時候發(fā)現(xiàn)自己被綠了顿涣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片波闹。...
    茶點(diǎn)故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涛碑,靈堂內(nèi)的尸體忽然破棺而出精堕,到底是詐尸還是另有隱情,我是刑警寧澤蒲障,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布歹篓,位于F島的核電站,受9級特大地震影響晌涕,放射性物質(zhì)發(fā)生泄漏滋捶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一余黎、第九天 我趴在偏房一處隱蔽的房頂上張望重窟。 院中可真熱鬧,春花似錦惧财、人聲如沸巡扇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厅翔。三九已至,卻和暖如春搀突,著一層夾襖步出監(jiān)牢的瞬間刀闷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工仰迁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甸昏,地道東北人。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓徐许,卻偏偏與公主長得像施蜜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子雌隅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評論 2 349