1. 程式人生 > >SSO CAS跨域請求配置

SSO CAS跨域請求配置

    該篇博文資訊參考自:https://www.imooc.com/article/4017

        前面我們介紹的SSO,無論是CAS還是我們自主開發的Nebula,都有一個共同的特點,就是應用系統需要登入時,都先重定向到認證伺服器進行登入。也就是說系統需要從一個應用先跳到另一個應用,我們看阿里的單點登入就是這麼做的。

但有時候,我們想進一步增加使用者體驗,並不希望使用者離開原應用頁面,在原有頁面基礎上進行登入,讓使用者感受不到認證中心的存在,能不能做到呢?回答是肯定的,大家看下新浪的單點登入方式,就是這麼做的。

            在原有應用系統頁面進行登入認證中心,如不發生跳轉,我們需要使用Ajax方式。而最常用的XMLHttpRequest Ajax方式呼叫,存在一個跨域問題,即為了安全,Ajax本身是不允許跨域呼叫的。這也就是為什麼單點登入常規做法是重定向到認證中心去登入,然後再重定向回系統應用的原因。(而且為了安全,CAS本身也不提倡跨域遠端登入)

        在應用頁面中,如何達到遠端登入CAS的效果?擺在我們面前有兩道坎兒需要克服:

        首先是遠端獲取lt和execution引數值問題。前面我們介紹過,CAS登入的form提交不僅有username和password兩個引數,還包括lt和execution,lt防止重複提交,execution保證走的同一個webflow流程。在進行遠端提交時,我們需要遠端得到CAS動態產生的這兩個引數,從而保證能夠向CAS進行正確form提交。

        XMLHttpRequest Ajax不能使用,可以採用另外一種方式,即JSONP。JSONP使用了script標籤可以跨域訪問其它網站資源的特性,巧妙地返回一段js回撥方法程式碼,通過執行這個回撥方法,達到了傳遞跨域呼叫資料的目的。

第二個坎兒是如何在本頁面跨域提交form請求。我們能不能也用JSONP方法呢?很遺憾,不行!JSONP提供的是get方式,而我們提交的form是post方式。我們可以使用另外一種ajax技術來解決,iframe。iframe可以載入和操作其它域的資源,根據使用者提交的username和password,以及前面獲取的lt和execution,在iframe中提交登入form引數,完成登入。

        主頁面如何獲取iframe提交返回的資訊?可以修改CAS的登入流程,讓其在遠端登入的情況下,將出錯資訊以引數的方式重定向迴應用系統服務端,應用系統再以呼叫父頁面js函式方法,將出錯資訊通過引數傳遞給父頁面。

從上面思路可以看出,我們並沒有讓CAS增加遠端登入的功能,CAS登入,還是需要在CAS所在域下登入。我們只是利用iframe方法,讓應用系統達到和遠端登入一樣的使用者體驗效果。而實現這一效果的關鍵,是應用登入頁對lt和execution動態引數以及CAS登入反饋資訊的捕獲。
下面我們就按照上面思路介紹具體開發方法:

    1.改造login-webflow.xml,增加支援跨域遠端登入處理流程分支。

        前面我們已經瞭解,登入流程的控制是在login-webflow.xml中,我們對它進行改造。改造原則是不修改原始碼,在原有登入處理流程的基礎上,增加一種新情況的處理,即支援跨域遠端登入處理。

        在流程初始化處理完成後,我們增加一個新的節點mode,它首先來檢查登入請求中是否包含一個變數mode,並且變數的值為rlogin。如果沒有,就繼續走原常規流程。如果有,說明是跨域遠端登入情況。<on-start> 後加入如下分支流程定義:

<action-state id="mode">
 <evaluate expression="modeCheckAction.check(flowRequestContext)"/>   
 <transition on="rlogin" to="serviceAuthorizationCheckR" />
 <transition on="normal" to="ticketGrantingTicketCheck" /> 
</action-state>

<action-state id="serviceAuthorizationCheckR">
 <evaluate expression="serviceAuthorizationCheck"/>
 <transition to="generateLoginTicketR"/>
</action-state>

<action-state id="generateLoginTicketR">
 <evaluate expression="generateLoginTicketAction.generate
(flowRequestContext)" />
  <transition on="generated" to="rLoginTicket" />
</action-state>

<view-state id="rLoginTicket" view="rLoginTicket" model="credential">
  <binder>
  <binding property="username" required="true" />
  <binding property="password" required="true"/>
  </binder>
  <on-entry>
    <set name="viewScope.commandName" value="'credential'" />
  </on-entry>
  <transition on="submit" bind="true" validate="true"         
to="realSubmitWithRLogin">
  <evaluate expression="authenticationViaRFormAction.doBind
(flowRequestContext, flowScope.credential)" />
   </transition>
</view-state>

<action-state id="realSubmitWithRLogin">
<evaluate expression="authenticationViaRFormAction.submit(flowRequestContext, 
flowScope.credential, messageContext)" />
 <transition on="success" to="sendTicketGrantingTicketR" />
</action-state>

<action-state id="sendTicketGrantingTicketR">
<evaluate expression="sendTicketGrantingTicketAction" />
<transition on="success" to="rLoginRes" />
</action-state>

<end-state id="rLoginRes" view="rLoginRes" />

    2.增加rLoginTicket和rLoginRes新檢視

        新增流程使用了兩個新view,rLoginTicket返回的是JSONP要求的js呼叫,將CAS產生的lt和execution資料傳遞給呼叫方。最後的rLoginRes是將出錯資訊重定向迴應用系統。

        前面我們介紹了定義CAS頁面和修改頁面主題的方法,我們基於前面的工作,在nebula_views.properties中新增(原始是default_views.properties):

rLoginTicket.(class)=org.springframework.web.servlet.view.JstlView
rLoginTicket.url=/WEB-INF/view/jsp/nebula/ui/rLoginTicket.jsp

rLoginRes.(class)=org.springframework.web.servlet.view.JstlView
rLoginRes.url=/WEB-INF/view/jsp/nebula/ui/rLoginRes.jsp

同時在相應目錄下建立這兩個檔案,檔案內容如下:

rLoginTicket.jsp

<%@ page contentType="text/javascript; charset=UTF-8"%>
<%out.print("jsonpcallback({'lt':'");%>${loginTicket}<%out.print
("','execution':'");%>${flowExecutionKey}<%out.print("'})");%>

rLoginRes.jsp

<%@ page contentType="text/html; charset=UTF-8"%>
<html>
<body>
<script type="text/javascript">
location.replace("${service}?ticket=${ticket}&ret=${ret}&msg=${msg}"); 
</script>
</body>
</html>

    3.定義新action節點

        流程中,我們定義了兩個新action,modeCheckAction和authenticationViaRFormAction,分別處理遠端登入流程判斷和form提交處理。在cas-servlet.xml中定義:

<bean id="modeCheckAction" class="org.jasig.cas.web.flow.ModeCheckAction" />

<bean id="authenticationViaRFormAction" 
class="org.jasig.cas.web.flow.AuthenticationViaRFormAction"
        p:centralAuthenticationService-ref="centralAuthenticationService"
        p:ticketRegistry-ref="ticketRegistry"/>

        按照CAS工程架構,這兩個新增的action定義在cas-server-webapp-support工程中。

ModeCheckAction定義如下:

package org.jasig.cas.web.flow;

import javax.servlet.http.HttpServletRequest;

import org.jasig.cas.web.support.WebUtils;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public class ModeCheckAction{  

 public static final String NORMAL = "normal";
 public static final String RLOGIN = "rlogin";

  public RLoginCheckAction() {
  }

 public Event check(final RequestContext context) {
   final HttpServletRequest request = 
       WebUtils.getHttpServletRequest(context);
   //根據mode判斷請求模式,如mode=rlogin,是AJAX登入模式,
   //不存在是原模式,認證中心本地登入
   String mode = request.getParameter("mode");
   if(mode!=null&&mode.equals("rlogin")){
     context.getFlowScope().put("mode", mode);
     return new Event(this, RLOGIN);
   }
   return new Event(this, NORMAL);
 }
}

AuthenticationViaRFormAction參照AuthenticationViaFormAction,對出錯輸出做了處理,核心程式碼如下:

public final Event submit(final RequestContext context, 
 final Credential credential,
 final MessageContext messageContext) throws Exception {

 // Validate login ticket
 final String authoritativeLoginTicket =                  
WebUtils.getLoginTicketFromFlowScope(context);
 final String providedLoginTicket = 
          WebUtils.getLoginTicketFromRequest(context);

  if (!authoritativeLoginTicket.equals(providedLoginTicket)) {
    logger.warn("Invalid login ticket {}", providedLoginTicket);
    messageContext.addMessage(new MessageBuilder().code 
    ("error.invalid.loginticket").build());

    context.getFlowScope().put("ret", -1);
    context.getFlowScope().put("msg", "LT過期,請重新登入!");
  }      
  try {
    final String tgtId = 
this.centralAuthenticationService.createTicketGrantingTicket(credential);
    WebUtils.putTicketGrantingTicketInFlowScope(context, tgtId);
    final Service service = WebUtils.getService(context);
    final String serviceTicketId = 
     this.centralAuthenticationService.grantServiceTicket(tgtId,service);
    WebUtils.putServiceTicketInRequestScope(context,serviceTicketId);
    context.getFlowScope().put("ticket", serviceTicketId);
    return newEvent(SUCCESS);
  } catch (final AuthenticationException e) {
    context.getFlowScope().put("ret", -2);
    context.getFlowScope().put("msg", 
           "使用者名稱密碼錯誤,請重新登入!");
    return newEvent(SUCCESS);
  } catch (final Exception e) {
    context.getFlowScope().put("ret", -3);
    context.getFlowScope().put("msg", "系統內部錯誤,請稍後登入!");
    return newEvent(SUCCESS);
  }
}

支援跨域遠端登入的CAS改造完成。應用系統方怎麼呼叫呢,我們開發一個例子:

設定CAS認證中心的域名為www.cas.com,應用系統的域名為www.ssoclient.com:81

首先我們按照前面方法把應用系統配置成SSO Client應用,這個前面介紹過,這裡不重複。開發一個應用登入頁rlogin.html,程式碼片段如下:

我們定義一個隱藏的iframe:

<iframe style="display:none;width:0;height:0" id="rlogin" name="rlogin"/>

登入form部分:

<div id="sec-login">
<form id="login-form" name="login-form" 
   action="http://www.cas.com/login" method="post" target="rlogin">  

<div><input name="username" id="username" type="text" autocomplete="off" 
class="login-ipt"  placeholder="郵箱/手機號" /></div>  
<div><input name="password" type="password"  id="password"  
       class="login-ipt" placeholder="密碼" /></div>  
<input type="hidden" name="lt" value="" id="lt" />
<input type="hidden" name="execution" value="" id="execution" />        
<input type="hidden" name="_eventId" value="submit" /> 
<input type="button" value="登入" class="login-bnt"  
    
            
           

相關推薦

SSO CAS請求配置

    該篇博文資訊參考自:https://www.imooc.com/article/4017         前面我們介紹的SSO,無論是CAS還是我們自主開發的Nebu

Django請求配置

安裝 pip install django-cores 配置 settings.py裡寫 if DEBUG: INSTALLED_APPS += ('corsheaders',) MIDDLEWARE_CLASSES += ('corsheaders.middlewa

vue+.netcore webapi前後端分離請求配置

1.安裝http庫-axios(axios 是一個基於 promise 的 HTTP 庫): npm install --save axios vue-axios // npm安裝 import Vue from 'vue' import axios from 'a

springboot請求配置

       當它請求的一個資源是從一個與它本身提供的第一個資源的不同的域名時,一個資源會發起一個跨域HTTP請求(Cross-site HTTP request)。        跨域並非瀏覽器限制了發起跨站請求,

beego請求配置

inf fun write ssa ins method 跨域 sage 跨域請求 不說廢話 在main函數前加入如下代碼 func init() { //跨域設置 var FilterGateWay = func(ctx *context.Context) {

Nginx配置請求 Access-Control-Allow-Origin *

默認 all 之前 methods 不包含 通知 text options flight 當出現403跨域錯誤的時候 No ‘Access-Control-Allow-Origin‘ header is present on the requested resource,需

django中配置允許請求

apps ons token middle red href clas cors nbsp 對於django 安裝django-cors-headers,詳情請看官方文檔 pip install django-cors-headers    配置settings.py

cors請求問題 關於spring -springmvc -mybatis .基於xml配置方式

1:場景還原     今天要寫一個方法, 需求是  在購物車服務上,  呼叫一個個人中心的方法 ,用到了 跨域請求.      我就在個人中心的 上面寫了個方法, 並在springMVC.xml中,配置了    &

vue-cli(vue2.x)配置請求代理,設定請求

1、在config/index.js設定配置檔案,跨域配置代理 (預設裡面內容為空,我們需要加入對跨域介面的配置) 根據介面的不同設定的請求頭和主機也不同,自己按照格式要求進行配置即可。 如上圖配置好了之後,'/apis/getSongLyric'為自定義的axios請求路徑,自己根據相

Vue vue-resource 全域性攔截器 Post、Get、Jsonp請求配置請求 全域性路徑配置

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../n

easy-springboot-web-cors | 配置cors解決請求問題

全域性配置 @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public v

vue-cli3.0 axios請求代理配置及埠修改

1.安裝 axios vue add axios 2.專案根目錄下新建 vue.config.js // vue.config.js module.exports = { devServer:

Vue前端配置代理實現請求

跨域的解決方法:         *設定讓伺服器允許跨域         *前端配置代理實現跨域請求 本文介紹前端配置代理實現跨域請求:  在專案config資料

java伺服器端配置支援請求

前言:之前做好的登陸介面,剛剛拿去給前端呼叫。發現前端那邊報錯,無法調到介面。因為我們後端的介面是在一臺伺服器上,前端(使用的是nodejs)的程式碼在另一臺伺服器上,導致兩臺伺服器之間無法相互呼叫(伺服器我們使用的是tomcat 7)。所以為了解決這個問題,前端那邊在介面

我也說說Nginx解決前端問題,正確的Nginx配置(後端Nginx CORS配置、CORS設定,後端允許請求

最近連續兩個朋友問我跨域相關問題,我猜想可能不少朋友也遇到類似問題,我打算寫個部落格聊一下我實際使用的配置, 先說明一下,我並不太瞭解這配置,沒精力去了解太多,但我覺得其中有一些關鍵的小注意點,可能有些初學者不太注意到,導致配置有問題,本文章可能只對新手有點幫助,如果你有好

基於angular-cli配置代理解決請求問題

1.跨域請求產生 隨著不同終端(Pad/Mobile/PC)的興起,對開發人員的要求越來越高,純瀏覽器端的響應式已經不能滿足使用者體驗的高要求,我們往往需要針對不同的終端開發定製的版本。為了提升開發效率,前後端分離的需求越來越被重視,後端負責業務/資料介面,前端負責展現/互動邏輯,

Vue專案設定,axios不成功的一個小問題( Vue CLI3請求,Vue proxyTable配置,Access-Control-Allow-Origin )

Vue專案,因為前後端分離,所以在請求後端介面時,時常遇到跨站問題, 2、如果前後端部署在同一個域名,就不會有跨域問題,但一般是生產環境部署是同一個域名下,但在開發環境時,並不是同域名呀,所以開發時呼叫介面返回類似“No 'Access-Control-Allow-Or

Java web專案請求 xml配置

在web專案開發過程中,跨域請求是再經常不過的事了,剛開始在google搜了一大圈,各種辦法非常多,但是都是比較疏散,經過除錯配置也是沒通過,最終如下程式碼是可以的,寫下來方便日後理解。 <fi

SpringBoot下如何配置實現請求

最近在做的專案中,我們採用前後端分離式開發。後臺RequestController介面寫好後,通過另一臺電腦的前端介面用ajax訪問我電腦上的後臺服務介面時,http請求會返回500的錯誤。經過查閱資料得知,這個問題是由“跨域請求”所引起的。那麼這個“跨域”到底

Spring Boot 2.0版本 全域性配置請求支援

Spring Boot 2.0以前全域性配置跨域主要是繼承WebMvcConfigurerAdapter @Configuration public class CorsConfig extends W