乱世王者8天150万 | 財經 | 資源 | 理財 | 考研 | 職場 | 論文 | 資格 | 股票學院 |

股票學院: 股票入門 - 股票知識 - 股票術語 - 炒股技巧 - 選股技巧 - 跟莊技巧 - 炒股經驗 - 投資策略 - K線圖 - 均線 - 分時圖 - 成交量 - 波浪理論 - 基本面分析 - 心理分析 - 漲停研究 - 趨勢線 - 江恩理論 - MACD - KDJ - 技術指標 - 財經股票書籍在線閱讀 - 金融類書籍下載 - 銀行學院 - 保險學院 - 外匯學院 - 債券學院 - 股票學院 - 基金學院 - 港股學院 - 黃金學院

當前位置:乱世王者8天150万 > 金融云 > 文章正文

乱世王者贵8可以玩弓吗:Tomcat 容器的安全認證和鑒權

時間:2019-05-21 02:23:22來源:頓悟源碼作者:佚名

    乱世王者8天150万 www.bubvk.icu 大量的 Web 應用都有安全相關的需求,正因如此,Servlet 規范建議容器要有滿足這些需求的機制和基礎設施,所以容器要對以下安全特性予以支持:

    Tomcat 容器的安全認證和鑒權

    • 身份驗證:驗證授權用戶的用戶名和密碼
    • 資源訪問控制:限制某些資源只允許部分用戶訪問
    • 數據完整性:能夠證明數據在傳輸過程中未被第三方修改
    • 機密性或數據隱私:傳輸加密(SSL),確保信息只能被信任用戶訪問

    本文就以上問題,對 Tomcat 容器提供的認證和鑒權的設計與實現,以及內部單點登錄的原理進行分析。首發于微信公眾號頓悟源碼.

    1. 授權

    容器和 Web 應用采用的是基于角色的權限訪問控制方式,其中容器需要實現認證和鑒權的功能,而 Web 應用則要實現授權的功能。

    在 Servlet 規范中描述了兩種授權方式:聲明式安全和編程式安全。聲明式安全就是在部署描述符中聲明角色、資源訪問權限和認證方式。以下代碼片段摘自 Tomcat 自帶的 Manager 應用的 web.xml:

    1. <security-constraint> <!-- 安全約束 --> 
    2.   <web-resource-collection> <!-- 限制訪問的資源集合 --> 
    3.     <web-resource-name>HTML Manager commands</web-resource-name> 
    4.     <url-pattern>/html/*</url-pattern> 
    5.   </web-resource-collection> 
    6.   <auth-constraint><!-- 授權可訪問此資源集合的角色 --> 
    7.      <role-name>manager-gui</role-name> 
    8.   </auth-constraint> 
    9. </security-constraint> 
    10.  
    11. <login-config><!-- 配置驗證方法 --> 
    12.   <auth-method>BASIC</auth-method> 
    13.   <realm-name>Tomcat Manager Application</realm-name> 
    14. </login-config> 
    15.  
    16. <security-role><!-- 定義一個安全角色 --> 
    17.   <description> 
    18.     The role that is required to access the HTML Manager pages 
    19.   </description> 
    20.   <role-name>manager-gui</role-name> 
    21. </security-role> 

    這些安全相關的配置,都會在應用部署時,初始化和設置到 StandardContext 對象中。更多詳細的內容可查看規范對部署描述文件的解釋,接下來看 Tomcat 怎么設計和實現認證及鑒權。

    2. 認證和鑒權的設計

    Servlet 規范雖然描述了 Web 應用聲明安全約束的機制,但沒有定義容器與關聯用戶和角色信息之間的接口。因此,Tomcat 定義了一個 Realm 接口,用于適配身份驗證的各種信息源。整體設計的類圖如下:

    Tomcat 容器的安全認證和鑒權

    上圖中,包含了各個類的核心方法,關鍵類或接口的作用如下:

    • Realm - 譯為域,域有泛指某種范圍的意思,在這個范圍內存儲著用戶名、密碼、角色和權限,并且提供身份和權限驗證的功能,典型的這個范圍可以是某個配置文件或數據庫
    • CombinedRealm - 內部包含一個或多個 Realm,按配置順序執行身份驗證,任一 Realm 驗證成功,則表示成功驗證
    • LockOutRealm - 提供用戶鎖定機制,防止在一定時間段有過多身份驗證失敗的嘗試
    • Authenticator - 不同身份驗證方法的接口,主要有 BASIC、DIGEST、FORM、SSL 這幾種標準實現
    • Principal - 對認證主體的抽象,它包含用戶身份和權限信息
    • SingleSignOn - 用于支持容器內多應用的單點登錄功能

    2.1 初始化

    Realm 是容器的一個可嵌套組件,可以嵌套在 Engine、Host 和 Context 中,并且子容器可以覆蓋父容器配置的 Realm。默認的 server.xml 在 Engine 中配置了一個 LockOutRealm 組合域,內部包含一個 UserDatabaseRealm,它從配置的全局資源 conf/tomcat-users.xml 中提取用戶信息。

    web.xml 中聲明的安全約束會初始化成對應的 SecurityConstraint、SecurityCollection 和 LoginConfig 對象,并關聯到一個 StandardContext 對象。

    在上圖可以看到,AuthenticatorBase 還實現了 Valve 接口,StandardContext 對象在配置的過程中,如果發現聲明了標準的驗證方法,那么就會把它加入到自己的 Pipeline 中。

    3. 一次請求認證和鑒權過程

    Context 在 Tomcat 內部就代表著一個 Web 應用,假設配置使用 BASIC 驗證方法,那么 Context 內部的 Pipeline 就有 BasicAuthenticator 和 StandardContextValve 兩個閥門,當請求進入 Context 管道時,就首先進行認證和鑒權,方法調用如下:

    Tomcat 容器的安全認證和鑒權

    整個過程的核心代碼就在 AuthenticatorBase 的 invoke 方法中:

    1. public void invoke(Request request, Response response) throws IOException, ServletException { 
    2.   LoginConfig config = this.context.getLoginConfig(); 
    3.   // 0. Session 對象中是否緩存著一個已經進行身份驗證的 Principal 
    4.   if (cache) { 
    5.     Principal principal = request.getUserPrincipal(); 
    6.     if (principal == null) { 
    7.       Session session = request.getSessionInternal(false); 
    8.       if (session != null) { 
    9.         principal = session.getPrincipal(); 
    10.         if (principal != null) { 
    11.           request.setAuthType(session.getAuthType()); 
    12.           request.setUserPrincipal(principal); 
    13.         } 
    14.       } 
    15.     } 
    16.   } 
    17.   // 對于基于表單登錄,可能位于安全域之外的特殊情況進行處理 
    18.   String contextPath = this.context.getPath(); 
    19.   String requestURI = request.getDecodedRequestURI(); 
    20.   if (requestURI.startsWith(contextPath) && requestURI.endsWith(Constants.FORM_ACTION)) { 
    21.           return; 
    22.       } 
    23.   } 
    24.   // 獲取安全域對象,默認配置是 LockOutRealm 
    25.   Realm realm = this.context.getRealm(); 
    26.   // 根據請求 URI 嘗試獲取配置的安全約束 
    27.   SecurityConstraint [] constraints = realm.findSecurityConstraints(request, this.context); 
    28.   
    29.   if ((constraints == null) /* && (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) { 
    30.     // 為 null 表示訪問的資源沒有安全約束,直接訪問下一個閥門 
    31.     getNext().invoke(request, response); 
    32.     return; 
    33.   } 
    34.   // 確保受約束的資源不會被 Web 代理或瀏覽器緩存,因為緩存可能會造成安全漏洞 
    35.   if (disableProxyCaching &&  
    36.       !"POST".equalsIgnoreCase(request.getMethod())) { 
    37.       if (securePagesWithPragma) { 
    38.           response.setHeader("Pragma", "No-cache"); 
    39.           response.setHeader("Cache-Control", "no-cache"); 
    40.       } else { 
    41.           response.setHeader("Cache-Control", "private"); 
    42.       } 
    43.       response.setHeader("Expires", DATE_ONE); 
    44.   } 
    45.   int i; 
    46.   // 1. 檢查用戶數據的傳輸安全約束 
    47.   if (!realm.hasUserDataPermission(request, response, constraints)) { 
    48.     // 驗證失敗 
    49.     // Authenticator已經設置了適當的HTTP狀態代碼,因此我們不必做任何特殊的事情 
    50.     return; 
    51.   } 
    52.   // 2. 檢查是否包含授權約束,也就是角色驗證 
    53.   boolean authRequired = true; 
    54.   for(i=0; i < constraints.length && authRequired; i++) { 
    55.     if(!constraints[i].getAuthConstraint()) { 
    56.       authRequired = false; 
    57.     } else if(!constraints[i].getAllRoles()) { 
    58.       String [] roles = constraints[i].findAuthRoles(); 
    59.       if(roles == null || roles.length == 0) { 
    60.         authRequired = false; 
    61.       } 
    62.     } 
    63.   } 
    64.   // 3. 驗證用戶名和密碼 
    65.   if(authRequired) { 
    66.     // authenticate 是一個抽象方法,由不同的驗證方法實現 
    67.     if (!authenticate(request, response, config)) { 
    68.       return; 
    69.     }  
    70.   } 
    71.   // 4. 驗證用戶是否包含授權的角色 
    72.   if (!realm.hasResourcePermission(request, response,constraints,this.context)) { 
    73.     return; 
    74.   } 
    75.   // 5. 已滿足任何和所有指定的約束 
    76.   getNext().invoke(request, response); 

    相關閱讀

    焦點圖文

    關于我們 | 廣告服務 | 商務合作 | 網站地圖

    版權所有 Copyright(C)2018-2020 蘇州騏云躍網絡科技有限公司,未經授權禁止復制或建立鏡像,否則將依法追究法律責任!
    聲明:我們不做任何形式的代客理財及投資指導,凡是以天下金融網名義做股票推薦的行為均屬違法!
    廣告商的言論與行為均與天下金融網無關!股市有風險,投資需謹慎。
    蘇公網安備 32050502000166號
    蘇ICP備14018528號
    商務合作:乱世王者8天150万

    天下金融網版權所有