<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          前后端分離的項(xiàng)目集成CAS

          共 15318字,需瀏覽 31分鐘

           ·

          2021-06-13 18:16

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

            作者 |  一頭磕在鍵盤(pán)上

          來(lái)源 |  urlify.cn/UV7Vvu

          這里說(shuō)的前后端分離,是指部署也分離的項(xiàng)目.

          關(guān)于前后端分離的項(xiàng)目如何集成CAS,很少有資料博客來(lái)說(shuō)如何做.

          本文先給出一個(gè)解決流程思路,再?gòu)脑韺用鎭?lái)進(jìn)行一定的剖析.

          采用環(huán)境:JDK1.8,Tomcat 8.5,cas 3.4.1.

          實(shí)際本文內(nèi)容和JDKTomcat版本無(wú)關(guān),理論上和cas版本也無(wú)關(guān).

          主要涉及以下知識(shí):

          1. cas認(rèn)證原理,及客戶(hù)端的認(rèn)證源碼分析.

          2. sessioncookie相關(guān)知識(shí)

          主要解決如下兩個(gè)問(wèn)題:

          1. 前后端分離項(xiàng)目如何接入cas.

          2. 如何和項(xiàng)目原有的登錄做集成.

          問(wèn)題一、前后端分離項(xiàng)目如何集成CAS

          先說(shuō)解決方案.

          關(guān)鍵就是這兩點(diǎn):

          1. 登錄成功后跳轉(zhuǎn)的第一個(gè)地址必須是被后端CAS Filter攔截的地址(主要用來(lái)重定向到前端,可以是一個(gè)接口,也可以是一個(gè).jsp界面),保證cas可以把認(rèn)證信息寫(xiě)入session.

          2. 前端發(fā)起的后續(xù)請(qǐng)求必須和第一個(gè)請(qǐng)求(即前面的那個(gè)跳轉(zhuǎn)后端的那個(gè)請(qǐng)求)是屬于同一個(gè)session.

          只要能保證這兩點(diǎn),那就絕對(duì)沒(méi)問(wèn)題.下面這是我的解決思路:

          假設(shè):

          cas服務(wù)端地址為192.168.0.90:8080/cas

          后端服務(wù)地址為192.168.0.100:8080/api-server

          前端界面地址為192.168.0.120

          后端webapp根目錄下增加一個(gè)cas.jsp來(lái)做重定向,這個(gè)jsp的訪問(wèn)地址為192.168.0.100:8080/api-server/cas.jsp

          具體步驟:

          1. 修改web.xml增加對(duì)cas.jsp的攔截

          隨手建一個(gè)cas.jsp,放到后端webapp的根目錄下,里面暫時(shí)不用寫(xiě)什么代碼,后面再加.

          修改web.xml中的CAS Filter的配置,額外增加對(duì)cas.jsp的攔截.

          <filter-mapping>
           <filter-name>CAS Filter</filter-name>
           <url-pattern>/cas.jsp</url-pattern>
          </filter-mapping>

          2.未登錄時(shí),前端重定向后端cas.jsp

          調(diào)整下前端的js邏輯,在發(fā)現(xiàn)未登錄時(shí)跳轉(zhuǎn)cas.jsp(準(zhǔn)確的說(shuō),最終目的是保證登錄成功后第一個(gè)跳轉(zhuǎn)的界面是cas.jsp).

          // 沒(méi)有登錄,跳轉(zhuǎn)后端cas.jsp
          if(notLogin){
              window.location.href='192.168.0.100:8080/api-server/cas.jsp';
          }

          cas.jspCAS Filter攔截,那么會(huì)再自動(dòng)重定向到cas登錄界面:192.168.0.90:8080/cas/login?service=192.168.0.100:8080/api-server/cas.jsp.

          如果想節(jié)省一次重定向的開(kāi)銷(xiāo),前端也可以直接重定向到登錄界面,注意加上service參數(shù)即可

          //和上面代碼結(jié)果一樣,但是節(jié)省一次重定向開(kāi)銷(xiāo).參考代碼如下
          if(notLogin){
              window.location.href=`192.168.0.90:8080/cas/login?service=192.168.0.100:8080/api-server/cas.jsp`;
          }

          如果一切順利的話(huà),這個(gè)時(shí)候登錄成功,那么就會(huì)進(jìn)入cas.jsp界面.這個(gè)進(jìn)入cas.jsp的請(qǐng)求的session,是通過(guò)了cas認(rèn)證的.

          3. cas.jsp重定向到前端

          修改cas.jsp,加入如下代碼:

          // 重定向到前端地址.
          String url = "192.168.0.120";
          response.sendRedirect(url);

          那么此時(shí),登錄成功后最終會(huì)進(jìn)入前端界面.下面我們只需要保證,后續(xù)前端發(fā)起的請(qǐng)求,和剛剛這個(gè)經(jīng)過(guò)了cas.jsp的請(qǐng)求,是屬于同一個(gè)session就行了,或者說(shuō),JSEESIONID這個(gè)cookie能正常寫(xiě)入前端的域下.

          4. 保證將JSESSIONID寫(xiě)入前端cookie

          在創(chuàng)建一個(gè)新的session后,TomcatSession Managerresponse中會(huì)加上Set-Cookie:JSESSIONID=123123這個(gè)頭,來(lái)讓瀏覽器寫(xiě)入名為JSESSIONIDcookie.但是這個(gè)是寫(xiě)在后端的域下面而不是前端的.而一般情況下,cookie是沒(méi)法跨域讀寫(xiě)的.

          后果就是,登錄成功了也跳轉(zhuǎn)回了前端界面,但是后續(xù)的請(qǐng)求因?yàn)檎?qǐng)求中沒(méi)有JSESSIONID這個(gè)cookie(實(shí)際上也不是必須放在cookie里),導(dǎo)致后續(xù)的每個(gè)請(qǐng)求,對(duì)于Tomcat來(lái)說(shuō),都是一個(gè)新的請(qǐng)求,進(jìn)而創(chuàng)建新的session–和第一個(gè)通過(guò)了認(rèn)證的請(qǐng)求半毛錢(qián)關(guān)系都沒(méi),那CAS Filter自然也就不會(huì)放行.所以后續(xù)的請(qǐng)求依舊是調(diào)用不成功.

          PS:如果這部分沒(méi)看懂,那么建議還是看看后文中的CAS認(rèn)證原理的章節(jié).

          那么這一步要解決的問(wèn)題就是如何將JSESSIONID寫(xiě)入前端的域下的問(wèn)題.

          那大體上的方案,也就如下三種:

          1. 允許跨域?qū)?/span>cookie.大體上就是ajax請(qǐng)求加上{crossDomain: true, xhrFields: {withCredentials: true}},后端響應(yīng)頭加上response.addHeader("Access-Control-Allow-Credentials", "true").

          2. 讓前后端"不跨域".具體方法就是,用nginx將前后端反向代理到同一個(gè)域下,無(wú)論是訪問(wèn)前端界面還是調(diào)用后端接口亦或是后端CAS Filter中的配置都是用這個(gè)代理后的地址.

          3. 前端手動(dòng)寫(xiě)入JSESSIONID.比如,cas.jsp在重定向回前端時(shí),在url后面附帶上JSESSIONID的值,前端js獲取到地址欄里的值,再手動(dòng)寫(xiě)入cookie中.

          就以第三種–讓前端手動(dòng)寫(xiě)入JSESSIONID為例:

          修改cas.jsp.

          //獲取sessionid
          String jsessionid = session.getId();
          // 前端地址.
          String url = "192.168.0.120";
          response.sendRedirect(url + "?jsessionid=" + jsessionid);

          前端js:

          // getJsessionIdFromUrl() 從地址欄里獲取jseesionid參數(shù)的值,具體邏輯自行實(shí)現(xiàn)
          var jsessionid = getJsessionIdFromUrl();
          // setCookie() 寫(xiě)入cookie,具體邏輯自行實(shí)現(xiàn)
          setCookie('jsessionid',jsessionid);

          前端后續(xù)再發(fā)送的請(qǐng)求,帶上JSESSIONID這個(gè)cookie,就沒(méi)事了.

          當(dāng)然細(xì)節(jié)上還有很多可優(yōu)化可完善的地方,但大致上就是這個(gè)流程思路.

          方案流程

          上述方案的總體的流程就是:

          1. 訪問(wèn)前端192.168.0.120

          2. 前端判斷沒(méi)登錄,跳轉(zhuǎn)192.168.0.100:8080/api-server/cas.jsp

          3. 訪問(wèn)192.168.0.100:8080/api-server/cas.jsp的請(qǐng)求到達(dá)TomcatSession Manager,發(fā)現(xiàn)沒(méi)有session,創(chuàng)建新的session

          4. 請(qǐng)求到達(dá)后端的CAS Filter,CAS Filter發(fā)現(xiàn)session未認(rèn)證且沒(méi)有ST參數(shù),302讓瀏覽器重定向到cas服務(wù)端登錄界面192.168.0.90:8080/cas/login?service=192.168.0.100:8080/api-server/cas.jsp

          5. 請(qǐng)求到到達(dá)cas服務(wù)端,服務(wù)端沒(méi)有在自己的域(即192.168.0.90:8080)下找到名為TGCcookie,那么認(rèn)定是用戶(hù)之前沒(méi)有登錄過(guò),展示登錄界面.

          6. cas服務(wù)端登錄界面輸入用戶(hù)名密碼,進(jìn)行登錄.

          7. 登錄成功,cas服務(wù)端根據(jù)service參數(shù),302讓瀏覽器重定向到192.168.0.100:8080/api-server/cas.jsp?ticket=ST-ABC1234567,注意此時(shí)是附帶上了ST參數(shù),并且在服務(wù)端的域(192.168.0.90:8080)下寫(xiě)入名為TGCcookie.

          8. 訪問(wèn)192.168.0.100:8080/api-server/cas.jsp?ticket=ST-ABC1234567到達(dá)TomcatSession Manager,發(fā)現(xiàn)沒(méi)有session,創(chuàng)建新的session.

          9. 請(qǐng)求進(jìn)入后端CAS Filter,發(fā)現(xiàn)session未認(rèn)證但是有ST參數(shù),予以放行

          10. 請(qǐng)求再進(jìn)入后端的CAS Validation Filter,向cas服務(wù)端校驗(yàn)ST,校驗(yàn)通過(guò),將校驗(yàn)結(jié)果聲明(內(nèi)含用戶(hù)信息)寫(xiě)入此session.

          11. 請(qǐng)求到達(dá)192.168.0.100:8080/api-server/cas.jsp界面

          12. cas.jsp做一系列處理(比如二次登錄)后,將JSESSIONID附帶在url參數(shù)中,再重定向回前端

          13. 前端從地址欄url中獲取到JSESSIONID參數(shù),寫(xiě)入cookie中.

          14. 后續(xù)請(qǐng)求,帶上JSESSIONID這個(gè)cookie.比如再去請(qǐng)求192.168.0.100:8080/api-server/a/b/c-api.

          15. 請(qǐng)求到達(dá)TomcatSession Manager,發(fā)現(xiàn)有JSESSIONID,并且存在,不再創(chuàng)建新的session.

          16. 請(qǐng)求到達(dá)后端CAS Filter,CAS Filter發(fā)現(xiàn)session中有認(rèn)證信息.予以放行.

          17. 請(qǐng)求到達(dá)192.168.0.100:8080/api-server/a/b/c-api

          問(wèn)題二、CAS如何和原有的登錄認(rèn)證做對(duì)接

          常見(jiàn)的一個(gè)問(wèn)題就是,我這個(gè)項(xiàng)目本身已經(jīng)有了一套登錄體系,現(xiàn)在要集成cas,應(yīng)當(dāng)如何去做.

          尤其是對(duì)接第三方的cas,cas服務(wù)端采用的用戶(hù)庫(kù)都和我們自己的不一樣,這個(gè)該怎么去做.

          大體思路依舊是通過(guò)前面我們?cè)黾拥倪@個(gè)cas.jsp.在cas.jsp中根據(jù)cas返回的用戶(hù)信息(一般都有用戶(hù)名,但是不會(huì)有也不應(yīng)當(dāng)有密碼),做二次登錄,二次登錄成功后再跳轉(zhuǎn)前端.

          參考代碼如下:

          // cas.jsp 內(nèi)容
          // cas會(huì)將用戶(hù)信息寫(xiě)入session中. request.getAttribute("_const_cas_assertion_") 也可以拿到.
          Object object = request.getSession().getAttribute("_const_cas_assertion_");
          // org.jasig.cas.client.validation.Assertion
          Assertion assertion = (Assertion) object;
          // 獲取到用戶(hù)名
          String userName = assertion.getPrincipal().getName()
          /* 
          假設(shè)原有登錄是調(diào)用的 loginService.login(userName,password);方法
          那我們?cè)黾右粋€(gè)免密登錄方法 loginService.loginWithOutPWD(userName),
          里面的處理邏輯和返回值 同loginService.login(userName,password)基本一致,唯獨(dú)不再需要密碼.
           */
          Object obj = loginService.loginWithOutPWD(userName);
          // isLogin判斷是否二次登錄成功
          if(isLogin(obj)){
              // 二次登錄成功,調(diào)整前端. 如果原有登錄有其它參數(shù)需要給前端,也可以附帶在url后面.
              //獲取sessionid
              String jsessionid = session.getId();
              // 前端地址.
              String url = "192.168.0.120";
              response.sendRedirect(url + "?jsessionid=" + jsessionid);
          }else{
              // 二次登錄失敗
              //...
          }

          CAS認(rèn)證原理

          CAS基本概念

          1.體系結(jié)構(gòu)

          從總體上看,CAS由兩大部分組成:一個(gè)CAS Server 和多個(gè)CAS Client.

          CAS Server(服務(wù)端)負(fù)責(zé)提供登錄認(rèn)證服務(wù),單獨(dú)部署,會(huì)給用戶(hù)頒發(fā)兩個(gè)核心票據(jù):TGT(登錄票據(jù),服務(wù)端使用)和ST(服務(wù)票據(jù),客戶(hù)端使用).

          CAS Client(客戶(hù)端)負(fù)責(zé)處理對(duì)客戶(hù)端受保護(hù)資源的訪問(wèn)請(qǐng)求.一般通過(guò)是在web.xml中配置了CAS過(guò)濾器,和應(yīng)用系統(tǒng)部署在一起,可以有多個(gè).

          2. 核心票據(jù)

          CAS的核心就是其Ticket,及其在Ticket之上的一系列處理操作.CAS的主要票據(jù)有TGT、ST、PGT、PGTIOU、PT,其中TGT、ST是CAS1.0(基礎(chǔ)模式)協(xié)議中就有的票據(jù),PGT、PGTIOU、PT是CAS2.0(代理模式)協(xié)議中有的票據(jù).這里主要介紹CAS1.0—基礎(chǔ)模式中的幾種票據(jù).

          TGT(Ticket Grangting Ticket)

          TGT是CAS為用戶(hù)簽發(fā)的登錄票據(jù),擁有了TGT,用戶(hù)就可以證明自己在CAS成功登錄過(guò).TGT封裝了Cookie值以及此Cookie值對(duì)應(yīng)的用戶(hù)信息.用戶(hù)在CAS認(rèn)證成功后,生成一個(gè)TGT對(duì)象,放入自己的session;同時(shí),CAS生成TGC(一個(gè)cookie,官方文檔說(shuō)是叫TGC,但是我這邊看到的名稱(chēng)是CASTGC,可能是我理解問(wèn)題或者是版本差異,下文統(tǒng)稱(chēng)TGC),寫(xiě)入瀏覽器.

          TGT對(duì)象的id就是TGC cookie的值,當(dāng)HTTP再次請(qǐng)求到來(lái)時(shí),如果傳過(guò)來(lái)的有TGC這個(gè)cookie,并且CAS能找到對(duì)應(yīng)的TGT,則說(shuō)明用戶(hù)之前登錄過(guò);如果沒(méi)有,則用戶(hù)需要重新登錄.

          TGC(Ticket-granting cookie)

          上面提到,CAS Server會(huì)生成TGT,而TGC就是將TGT的id以cookie形式放到瀏覽器端,是CAS Server用來(lái)明確用戶(hù)身份的憑證.

          ST(ServiceTicket)

          ST是CAS為用戶(hù)簽發(fā)的訪問(wèn)某一服務(wù)票據(jù).在登錄成功后重定向回客戶(hù)端的時(shí)候,會(huì)給客戶(hù)端頒發(fā)ST,客戶(hù)端的CAS Validation Filter會(huì)根據(jù)ST去服務(wù)端再次做校驗(yàn),獲取用戶(hù)信息.

          為了保證ST的安全性:ST 是基于隨機(jī)生成的,沒(méi)有規(guī)律性.而且,CAS規(guī)定 ST 只能存活一定的時(shí)間,然后 CAS Server 會(huì)讓它失效.而且,CAS 協(xié)議規(guī)定ST只能使用一次,無(wú)論 Service Ticket 驗(yàn)證是否成功, CASServer 都會(huì)清除服務(wù)端緩存中的該 Ticket ,從而可以確保一個(gè) Service Ticket 不被使用兩次.

          概括下就是,ST是服務(wù)端提供給客戶(hù)端的用來(lái)獲取用戶(hù)信息的只能用一次的憑證.

          3. 核心過(guò)濾器

          CAS Client使用的核心過(guò)濾器主要由兩個(gè):負(fù)責(zé)判斷請(qǐng)求是否已認(rèn)證的AuthenticationFilter和負(fù)責(zé)校驗(yàn)STTicketValidationFilter.

          CAS認(rèn)證原理

          直接上官方提供的認(rèn)證序列圖(一定要看懂).

          [外鏈圖片轉(zhuǎn)存中…(img-NJvC5iEI-1569813048856)]

          其中的ST就是所謂的唯一令牌,用過(guò)即失效.這個(gè)令牌也可能是采用JWT來(lái)生成的.

          如果ST采用的是JWT來(lái)進(jìn)行生成,那么流程上稍微有些不同,序列圖如下(這個(gè)了解就行):

          [外鏈圖片轉(zhuǎn)存中…(img-OuyOnH3A-1569813048857)]

          可以打開(kāi)Chrome的控制臺(tái)或者是直接采用Filder進(jìn)行抓包,對(duì)照著進(jìn)行分析確認(rèn).

          CAS客戶(hù)端核心過(guò)濾器

          對(duì)著源碼來(lái)看下cas客戶(hù)端的是如何判斷一個(gè)session是否已通過(guò)認(rèn)證.

          前面我們提到過(guò),CAS客戶(hù)端使用的核心過(guò)濾器主要有兩個(gè):負(fù)責(zé)判斷請(qǐng)求是否已認(rèn)證的AuthenticationFilter和負(fù)責(zé)校驗(yàn)STTicketValidationFilter.

          這兩個(gè)過(guò)濾器都有多種不同的實(shí)現(xiàn),下文只以具體的某個(gè)實(shí)現(xiàn)類(lèi)為例來(lái)說(shuō)明.

          AuthenticationFilter

          這個(gè)過(guò)濾器就是用來(lái)負(fù)責(zé)校驗(yàn)每個(gè)請(qǐng)求是否是認(rèn)證的請(qǐng)求,如果請(qǐng)求未通過(guò)認(rèn)證且不包含ST,那么就重定向到服務(wù)端登錄界面.

          對(duì)應(yīng)客戶(hù)端web.xml中配置的CAS Filter,大致是這樣的:

          <filter>
              <filter-name>CAS Filter</filter-name>
           <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
           <init-param>
            <param-name>casServerLoginUrl</param-name>
            <param-value>${cas.serverUrl}/login</param-value>
           </init-param>
           <init-param>
            <param-name>serverName</param-name>
            <param-value>${cas.clientUrl}</param-value>
           </init-param>
           <init-param>
            <param-name>ignorePattern</param-name>
            <param-value>.*/login|.*/unsafe|.*/api/app/token/*|.*\.ico|.*\.js(?!p)|.*\.css|</param-value>
           </init-param>
           <init-param>
            <param-name>ignoreUrlPatternType</param-name>
            <param-value>REGEX</param-value>
           </init-param>
           ...
          </filter>
          <filter-mapping>
           <filter-name>CAS Filter</filter-name>
           <url-pattern>/cas.jsp</url-pattern>
          </filter-mapping>
          <filter-mapping>
           <filter-name>CAS Filter</filter-name>
           <url-pattern>/api/*</url-pattern>
          </filter-mapping>

          看下對(duì)應(yīng)的實(shí)現(xiàn).

          public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
                  final FilterChain filterChain) throws IOException, ServletException {
              
              final HttpServletRequest request = (HttpServletRequest) servletRequest;
              final HttpServletResponse response = (HttpServletResponse) servletResponse;
              
              // 判斷請(qǐng)求是否不需要過(guò)濾
              if (isRequestUrlExcluded(request)) {
                  logger.debug("Request is ignored.");
                  filterChain.doFilter(request, response);
                  return;
              }
              
              final HttpSession session = request.getSession(false);
              // CONST_CAS_ASSERTION = "_const_cas_assertion_"
              final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
              // 存在assertion,即認(rèn)為這是一個(gè)已通過(guò)認(rèn)證的請(qǐng)求.予以放行
              if (assertion != null) {
                  filterChain.doFilter(request, response);
                  return;
              }
              // 不存在 assertion,那么就來(lái)判斷這個(gè)請(qǐng)求是否是用來(lái)校驗(yàn)ST的(校驗(yàn)通過(guò)后會(huì)將信息寫(xiě)入assertion)
              final String serviceUrl = constructServiceUrl(request, response);
              final String ticket = retrieveTicketFromRequest(request);
              final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
              // 是校驗(yàn)ST的請(qǐng)求,予以放行
              if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
                  filterChain.doFilter(request, response);
                  return;
              }

              final String modifiedServiceUrl;

              logger.debug("no ticket and no assertion found");
              if (this.gateway) {
                  logger.debug("setting gateway attribute in session");
                  modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
              } else {
                  modifiedServiceUrl = serviceUrl;
              }

              logger.debug("Constructed service url: {}", modifiedServiceUrl);

              // 要重定向界面地址(cas服務(wù)端登錄界面).
              final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
                      getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);

              logger.debug("redirecting to \"{}\"", urlToRedirectTo);
              this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
          }

          可以看到,cas正是通過(guò)session中是否有assertion的信息來(lái)判斷一個(gè)請(qǐng)求是否合法.

          而這個(gè)assertion信息,又是在客戶(hù)端校驗(yàn)ST之后(登錄成功后重定向回客戶(hù)端的請(qǐng)求附帶有ST參數(shù))寫(xiě)入session中的.具體見(jiàn)下文TicketValidationFilter

          TicketValidationFilter

          這個(gè)過(guò)濾器用來(lái)校驗(yàn)ST,并在session中寫(xiě)入認(rèn)證聲明信息assertion.

          對(duì)應(yīng)客戶(hù)端web.xml中配置的CAS Validation Filter,大致是這樣的:

          <filter>
           <filter-name>CAS Validation Filter</filter-name>
           <filter-class>
            org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
           </filter-class>
          </filter>

          查看Cas20ProxyReceivingTicketValidationFilter源碼,可以在其父類(lèi)AbstractTicketValidationFilterdoFilter()方法中找到對(duì)應(yīng)的代碼,如下:

          final Assertion assertion = this.ticketValidator.validate(ticket,
                              constructServiceUrl(request, response));

          logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());

          // CONST_CAS_ASSERTION = "_const_cas_assertion_"
          request.setAttribute(CONST_CAS_ASSERTION, assertion);

          // useSession 對(duì)應(yīng)配置項(xiàng)中的useSession參數(shù),缺省值為true.但這個(gè)配置在3.4版本之后是棄用的,后續(xù)隨時(shí)可能會(huì)被移除.
          if (this.useSession) {
              request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion);
          }

          那么通過(guò)這兩個(gè)過(guò)濾器的源碼分析,可以證明這兩點(diǎn):

          1. cas客戶(hù)端是根據(jù)session中是否有assertion信息來(lái)判斷是否已認(rèn)證.

          2. assertion是在登錄成功后重定向回后端的時(shí)候?qū)懭?/span>session的.

          那么進(jìn)而就會(huì)得出我們前文中提到的解決思路的關(guān)鍵性?xún)牲c(diǎn):

          1. 登錄成功后跳轉(zhuǎn)的第一個(gè)地址必須是被后端CAS Filter攔截的地址(主要用來(lái)重定向到前端,可以是一個(gè)接口,也可以是一個(gè).jsp界面),保證cas可以把認(rèn)證聲明信息assertion寫(xiě)入session.

          2. 前端發(fā)起的后續(xù)請(qǐng)求必須和第一個(gè)請(qǐng)求(即前面的那個(gè)跳轉(zhuǎn)后端的那個(gè)請(qǐng)求)是屬于同一個(gè)session(因?yàn)橛?/span>assertion,一定可以通過(guò)認(rèn)證).

          關(guān)于第一點(diǎn)不再多說(shuō).

          關(guān)于第二點(diǎn),如何保證前端發(fā)起的后續(xù)請(qǐng)求和第一個(gè)請(qǐng)求是同一個(gè)session,答案就是保證JSEESIONID一致就行.

          關(guān)于sessioncookieJSESSIONID在其中起到的作用

          眾所周知,Http協(xié)議是一種無(wú)狀態(tài)協(xié)議,每次服務(wù)端接收到客戶(hù)端的請(qǐng)求時(shí),都是一個(gè)全新的請(qǐng)求,服務(wù)器并不知道客戶(hù)端的歷史請(qǐng)求記錄;

          為了彌補(bǔ)Http的無(wú)狀態(tài)特性,session應(yīng)運(yùn)而生.服務(wù)器可以利用session存儲(chǔ)客戶(hù)端在同一個(gè)會(huì)話(huà)期間的一些操作記錄,而服務(wù)端的這個(gè)session,對(duì)應(yīng)到瀏覽器端,則是名為JSESSIONIDcookie,JSESSIONID的值就是session的id.

          那么再看這兩個(gè)問(wèn)題:

          1. 服務(wù)器如何判斷客戶(hù)端發(fā)送過(guò)來(lái)的請(qǐng)求是屬于同一個(gè)seesion?

          答:用session的id來(lái)進(jìn)行區(qū)分,如果id相同,那就認(rèn)為是同一個(gè)會(huì)話(huà).在Tomcat中,session的id的默認(rèn)名字是JSESSIONID.對(duì)應(yīng)到前端就是名為JSESSIONIDcookie.

          1. session的id是在什么時(shí)候創(chuàng)建,又是怎樣在前后端傳輸?shù)?

          答:tomcat第一次接收到一個(gè)請(qǐng)求時(shí),會(huì)創(chuàng)建一個(gè)session對(duì)象,同時(shí)生成一個(gè)session id,并通過(guò)響應(yīng)頭的Set-Cookie:"JSESSIONID=XXXXXXX"命令,向客戶(hù)端發(fā)送要求設(shè)置Cookie的響應(yīng).

          前端在后續(xù)的每次請(qǐng)求時(shí),會(huì)帶上所有cookie信息,自然也就包含了JSESSIONID這個(gè)cookie.Tomcat據(jù)此來(lái)查找到對(duì)應(yīng)的session.如果指定session不存在(比如我們隨手編一個(gè)JSESSIONID,那對(duì)應(yīng)的session肯定不存在),那么就會(huì)創(chuàng)建一個(gè)新的session,其id的值就是請(qǐng)求中的JSESSIONID的值.

          這樣我們?cè)诖a中就可以通過(guò)request.getSession()來(lái)獲取到當(dāng)前的會(huì)話(huà)信息.

          PS:
          實(shí)際上,
          JSESSIONID傳給后端的方式,還可以直接在url后面加上;jsessionid=xxxx來(lái)傳遞.但是會(huì)被cookie中的JSESSIONID覆蓋.

          那么具體到cas對(duì)接上,第一次重定向回客戶(hù)端的請(qǐng)求肯定是可以通過(guò)cas的認(rèn)證的,那么只要后續(xù)的請(qǐng)求和第一個(gè)是同一個(gè)session,那就一定也能通過(guò)cas的過(guò)濾.

          前面我們也說(shuō)了,只要請(qǐng)求中的JSESSIONID是一致的,那就會(huì)被認(rèn)定是同一個(gè)session.

          前文中給出的解決思路,也正是基于此進(jìn)行.

          原理部分基本上就到這里.

          如果覺(jué)得看懂了原理,那我最后提個(gè)問(wèn)題:

          如果通過(guò)nginx將前后端反向代理到同一個(gè)域下,登錄成功后直接跳轉(zhuǎn)前端地址,那是否可行?原因是什么?








          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本色情 电影在线播放 | 小黄片在线视频 | 高清 无码 免费 | 色婷婷丁香激情五月电影 | 日韩 人妻 精品 无码 欧美 |