<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 41  文章 - 7  trackbacks - 0
    <2016年8月>
    31123456
    78910111213
    14151617181920
    21222324252627
    28293031123
    45678910

    常用鏈接

    留言簿

    隨筆分類

    隨筆檔案

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    原文:http://shiro.apache.org/reference.html
    Apache Shiro介紹
    Apache Shiro是什么?
    Apache Shiro 是一個可干凈處理認證,授權,企業會話管理以及加密的強大且靈活的開源安全框架.
    Apache Shiro的首要目標是易于使用和理解. 安全可以是非常復雜的,有時甚至是痛苦的,但它不是.
    框架應該隱藏復雜的地方,暴露干凈而方便的API,以簡化開發者的努力進而使其程序安全.
    下面是Apache Shiro可以做的事情:
    • 認證用戶的身份
    • 實施用戶訪問控制,如:
    • 確定用戶是否分配了某個角色
    • 確定用戶是否允許做某些事情
    • 任何環境中均可使用Session API, 即使是非web環境或EJB容器
    • 反映在認證,訪問控制,或會話生命期之間的事件.
    • 聚合一個或多個用戶安全數據數據源,以將其作為單一的組合用戶視圖.
    • 可開啟Single Sign On (SSO) 功能
    • 為用戶無須再次登錄開啟 'Remember Me'服務
    ...
    還有更多-所有都集成在緊密結合的易于使用的API中.
    Shiro試圖在所有應用環境中都實現這些目標 - 從最簡單的命令行應用程序到大型企業應用程序,它不強制依賴于第三方框架,容器,或應用程序服務器.
    當然,在可能的情況下,也可以將其整合到這些環境中, 但它在任何環境中都是開箱即可使用的.
    Apache Shiro 特性
    Apache Shiro是一個包含多種特性的綜合應用安全框架。下圖展示了Shiro的主要特性,本參考手冊也會參照下面的特性來組織內容:

    Shiro的目標(Shiro開發團隊稱其為“四大應用安全基石”)是認證、授權、會話管理、加密:
    • 認證: 有時稱為登錄, 用于驗證用戶身份. 
    • 授權: 訪問控制過程,如,確定誰能訪問什么. 
    • 會話管理: 管理特定用戶會話,即使是非web環境或EJB應用程序 
    • 加密: 雖使用加密算法來保證數據安全, 但仍然易于使用.
    也有其它功能,以在不同的應用環境中支持和加強這些特性,特別是:
    • Web 支持 — Shiro的web支持APIs可幫助你容易地保護web應用程序安全.
      • 緩存 — 緩存是在Apache ShiroAPI的一等公民,在確保安全操作的同時能確??焖俸陀行А?/span>
      • 并發性- Apache Shiro的并發特性被設計為支持多線程的應用。
      • 測試 —測試支持簡化了單元和集成測試的創建,并確保您的代碼如預期一樣的安全。
      • "Run As" — Shiro 允許用戶以其它用戶的身份(如果有資格的話)來運行, 這種特性在管理場景中非常有用.
      • "Remember Me" — Shiro 可以跨會話來記住用戶身份,這樣只在強制的情況下才需要用戶登錄.
    第一個 Apache Shiro 應用程序
    如果你從未接觸過Apache Shiro,這篇簡短的指南會向你展示如何使用Apache Shiro來設置一個初始和非常簡單的安全web應用程序. 我們將討論Shiro的核心概念來幫助您熟悉Shiro的設計和API。
    如果你不想遵從本指南來編輯文件,你可以從下面的位置來選擇獲取幾乎相同的示例程序,并按照你的方式來引用:
    設置
    在這個簡單的例子中, 我們會創建一個非常簡單的快速運行和退出的命令行應用程序, 這只是為了讓你對 Shiro的API有所感覺.
    任何應用程序
    Apache Shiro從最初就被設計用于支持任何應用程序- 從最小的命令行程序到大型集群web應用程序. 雖然在本指南中我們創建的是一個簡單app,但不管應用程序是如何創建或無論部署在什么地方,都可以應用同樣的使用模式.
    本指南需要Java 1.5+.同時也會使用 Apache Maven 作為我們的構建工具,當然對于Apache Shiro的使用,這不是必須的.你可以獲取Shiro的jars,并以你喜歡的方式來進行組合,例如可以使用 Apache AntIvy.
    對于本指南,請確保你使用的是Maven 2.2.1+. 你可以在命令中輸入mvn --version查看類似于下面的東西:
    Testing Maven Installation
    hazlewood:~/shiro-tutorial$ mvn --version
    Apache Maven 2.2.1 (r801777; 2009-08-06 12:16:01-0700)
    Java version: 1.6.0_24
    Java home: /
    System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
    Default locale: en_US,
    platform encoding: MacRoman
    OS name:
    "mac os x"
    version:
    "10.6.7"
    arch:
    "x86_64"
    Family:
    "mac"
    現在,在你的文件系統中創建一個新目錄,例如, shiro-tutorial ,并將下面的pom.xml 文件放在那個目錄中:
    pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns=
    "http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.shiro.tutorials</groupId>
    <artifactId>shiro-tutorial</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>First Apache Shiro Application</name>
    <packaging>jar</packaging>
    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.0.2</version>
    <configuration>
    <source>1.5</source> <target>1.5</target>
    <encoding>${project.build.sourceEncoding}</encoding>
    </configuration> </plugin>
    <!-- This plugin is only to test run our little application. It is not needed in most Shiro-enabled applications: -->
    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.1</version>
    <executions>
    <execution>
    <goals>
    <goal>java</goal>
    </goals>
    </execution>
    </executions>
    <configuration>
    <classpathScope>test</classpathScope>
    <mainClass>Tutorial</mainClass>
    </configuration>
    </plugin>
    </plugins>
    </build>
    <dependencies>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.1.0</version>

    </dependency>
    <!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding in this example app. See http://www.slf4j.org for more info. -->
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.6.1</version>
    <scope>test</scope>
    </dependency>
    </dependencies>
    </project>
    Tutorial 類
    我們將運行一個簡單的命令行應用程序,因此我們需要創建一個帶有public static void main(String[] args)方法的java類.
    在包含pom.xml文件的同一個目錄中,創建一個src/main/java子目錄,并使用下面的內容在其中創建一個Tutorial.java文件:
    src/main/java/Tutorial.java
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class Tutorial {
    private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
    public static void main(String[] args) {
    log.info(
    "My First Apache Shiro Application");
    System.exit(0);
    }
    }
    現在不用擔心import語句- 我們很快就會講解它們. 現在,我們已經有了一個典型的命令行程序外殼. 程序會打印文件"My First Apache Shiro Application"并退出.
    測試運行
    要測試我們的Tutorial應用程序,切換到tutorial根目錄(即.shiro-tutorial),在命令行提示窗口中執行下面的命令:
    mvn compile exec:java
    然后你將看到我們的Tutorial 程序運行和退出.你可能會看到與下面相似的內容(注意,粗文本表示我們的輸出):
    運行程序
    lhazlewood:~/projects/shiro-tutorial$ mvn compile exec:java
    ... a bunch of Maven output ...
    1 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
    lhazlewood:~/projects/shiro-tutorial\$
    到此我們已經驗證了應用程序能成功運行 -現在讓我們啟用Apache Shiro. 在繼續本指南的過程中,每次修改代碼后,你都可以運行mvn compile exec:java 來查看結果變化.
    啟用Shiro
    理解啟用Shiro的第一件事是幾乎Shiro中的每件事情都與核心組件SecurityManager有關. 對于那些熟悉Java安全的人來說,這是Shiro的SecurityManager概念 -它不同于java.lang.SecurityManager.
    Architecture 章節中,我們會詳細講解Shiro的設計,對于現在來說,只需要知道Shiro SecurityManager 是Shiro 環境中的核心且每個程序都必須存在一個SecurityManager 就足夠了.
    因此,在Tutorial 程序中,我們要做的第一件事就是建立SecurityManager實例.
    配置
    雖然我們可以直接實例化SecurityManager類, Shiro的SecurityManager 實現包含足夠的配置選項和內部組件,使其可以從Java源代碼中進行構建- 但可以使用靈活的文本配置格式來更簡單地配置SecurityManager.
    為此, Shiro通過基于文本的 INI 配置提供了一個缺省的公共解決方案 . 現在,人們都厭倦了使用笨重的XML文件,INI易于閱讀,簡單易用,并且需要極少依賴。稍后你也會看到,用一個簡單的對象圖導航,INI可像SecurityManager一樣有效地配置簡單的對象圖.
    多個配置選項
    Shiro的 SecurityManager實現以及所有支持組件都是JavaBeans 兼容的. 這允許Shiro使用特定的配置格式來進行配置,配置格式如 XML (Spring, JBoss, Guice, etc), YAML, JSON, Groovy Builder 標記等等.
    INI只是Shiro的共同解決方案,它允許在任何環境中進行配置.
    shiro.ini
    對于這個簡單應用程序,因此我們使用INI文件來配置Shiro SecurityManager.首先,在與pom.xml相同的目錄中,創建一個 src/main/resources 目錄. 然后使用下面的命令,在新創建的目錄中創建一個shiro.ini文件:

    src/main/resources/shiro.ini
    # =============================================================================
    # Tutorial INI configuration
    #
    # Usernames/passwords are based on the classic Mel Brooks' film
    "Spaceballs" :)
    # =============================================================================
    # -----------------------------------------------------------------------------
    # Users and their (optional) assigned roles # username = password, role1, role2, ..., roleN
    # -----------------------------------------------------------------------------
    [users] root = secret, admin
    guest = guest, guest
    presidentskroob = 12345, president
    darkhelmet = ludicrousspeed, darklord, schwartz
    lonestarr = vespa, goodguy, schwartz
    # -----------------------------------------------------------------------------
    # Roles with assigned permissions
    # roleName = perm1, perm2, ..., permN
    # -----------------------------------------------------------------------------
    [roles]
    admin = *
    schwartz = lightsaber:*
    goodguy = winnebago:drive:eagle5
    正如你看到的,這個配置基本建立一套小的靜態用戶帳戶,對于我們的第一個程序來說,已經足夠了.在后面的章節中,你會看到如何使用更復雜的用戶數據資源,如關系數據庫,LDAP的ActiveDirectory等等.
    引用配置
    現在我們已經定義了一個INI文件, 然后我們就可在Tutorial類中創建SecurityManager實例了. 修改main方法,以反映下面的更新:
    public static void main(String[] args) {
    log.info(
    "My First Apache Shiro Application");
    //1.
    Factory<
    SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    //2.
    SecurityManager securityManager = factory.getInstance();
    //3.
    SecurityUtils.setSecurityManager(securityManager);
    System.exit(0);
    }
    在添加了3行代碼后,我們就在樣例程序中啟用了Shiro!是不是很簡單?
    運行 mvn compile exec:java能看到一切都能成功運行(因為Shiro默認為debug或更低級別,你不會看到任何Shiro日志消息-如果啟動或運行時不出現錯誤,那么一切就工作得很好).
    以下是上面增加代碼所做的事情:
    1. 我們使用Shiro的IniSecurityManagerFactory實現來獲取位于classpath根下的shiro.ini文件.此實現了反映了Shiro對工廠設計模式的支持. classpath: 前輟是一個資源標識符,用于告訴shiro在什么位置加載ini文件(也支持其它前輟,像url: 和file: ). 
    1. 然后調用了factory.getInstance()方法,它會解析INI文件,并返回一個反映配置的SecurityManager實例. 
    1. 在這個簡單示例中,我們將SecurityManager設為了一個可通過JVM來訪問的static (memory) 單例.請注意,如果在單個虛擬機中存在多個啟用了Shiro的應用程序,這是不理想的.對于這個簡單示例,它是OK的, 但在更復雜的應用程序環境中,通常應該將SecurityManager放置在特定應用的內存中(如,放在web app的ServletContext中或Spring, Guice或 JBoss DI 容器實例中).
    使用Shiro
    現在我們建立好了SecurityManager,可以準備使用了, 現在讓我們做一些我們真正關心的東西 - 執行安全操作.
    當為我們的應用程序提供安全時,我們通常問自己最相關的問題是"當前用戶是誰?”或“是否允許當前用戶做某些事情”? 這是經常會問的問題,因此我們會編寫代碼并設計我們的接口:
    應用程序通常會基于用戶存儲進行構建, 并且你希望每個用戶都是安全的. 因此,對于我們來說,最自然的方式是認為應用程序的安全是基于當前用戶的. Shiro的 API從根本上使用了Subject概念來表達當前用戶.
    在幾乎所有環境中,你可以通過下面的調用來獲取當前執行用戶:
    Subject currentUser = SecurityUtils.getSubject();
    使用SecurityUtils.getSubject(), 我們能獲取當前執行的SubjectSubject是一種安全術語,其基本意義為"當前執行用戶的特定安全視圖". 它不稱為'User',因為 'User' 這個詞通常與人類聯系在一起.
    在安全世界中,術語'Subject'可以表示人類,但也可以表示第三過程, cron job, 后臺帳戶,或任何相似的東西.它簡單地表示為當前與軟件交互的事物.
    雖然這只是針對大多數的意圖和目的,你仍然可將Subject認為是Shiro的'User'.
    在一個獨立的應用程序中,getSubject()方法會返回在應用特定位置用戶數據的基礎上返回一個Subject,在服務器環境中(如. web app),它會基于當前線程或傳入請求相關的用戶數據中來獲取Subject.
    現在你有了Subject,你能用它來做什么呢?
    如果你想在應用程序其會話期間對當前用戶做些什么,你可以獲取它們的會話:
    Session session = currentUser.getSession(); session.setAttribute( "someKey", "aValue" );
    Session是Shiro特有的實例,它提供了類似常規HttpSessions大部分的功能,但也存在額外的東西以及一個重大區別:它不需要HTTP環境!
    如果部署在web應用程序中,默認情況下,Session是基于HttpSession的. 但在非web環境中,就像這個簡單的tutorial程序, Shiro會默認自動使用其企業級會話管理.
    這意味著,無論是哪種部署環境,無論在哪一層,在你的應用程序中都可以使用同一套API. 這打開了應用程序的新世界,因為任何應用程序都可以不強制使用 HttpSession或EJB有狀態會話bean的前提下獲取Session. 除此之外,任何client技術現在都可以共享會話數據.
    所以現在你可以獲取Subject及其會話了.像檢查它們是否允許做某些事情以及檢查其角色與權限這樣真正有用的事情又怎樣呢?
    我們只能為一個已知的用戶做這些檢查。上面的Subject實例只能代表當前用戶,但當前用戶是誰呢? 這里,它們是匿名的- 也就是說,除非它們至少登錄了一次,才知道是已知的. 因此, 讓我們按下面這樣做:
    if ( !currentUser.isAuthenticated() ) {
    //collect user principals and credentials in a gui specific manner
    //such as username/password html form, X509 certificate, OpenID, etc.
    //We'll use the username/password example here since it is the most common.
    UsernamePasswordToken token =
    new UsernamePasswordToken("lonestarr", "vespa");
    //this is all you have to do to support 'remember me' (no config - built in!):
    token.setRememberMe(
    true);
    currentUser.login(token);
    }
    這就是它了!不能再容易了.
    但如果登錄失敗了呢? 你可以捕獲各種各樣的異常,這些異常將明確地告訴你發生了什么,并允許你進行相應地處理:
    try {
    currentUser.login( token );
    //if no exception, that's it, we're done!
    } catch ( UnknownAccountException uae ) {
    //username wasn't in the system, show them an error message?
    } catch ( IncorrectCredentialsException ice ) {
    //password didn't match, try again? }
    catch ( LockedAccountException lae ) {
    //account for that username is locked - can't login. Show them a message?
    }
    ... more types exceptions to check
    if you want ...
    }
    catch ( AuthenticationException ae ) {
    //unexpected condition - error?
    }
    你可以捕獲多種不同類型的異常,或者拋出Shiro未出現的自定義異常.參考AuthenticationException JavaDoc 來了解更多.
    溫馨提示
    最佳安全實踐是向用戶提示一般的登錄失敗消息,因為你不想幫助攻擊者試圖闖入你的系統.
    Ok, 到現在為止,我們有了一個登錄用戶.還有什么事情可以做呢?
    讓我們看看它是誰:
    //print their identifying principal (in this case, a username):
    log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );
    我們也可以測試它是否有指定的角色:
    if ( currentUser.hasRole( "schwartz" ) ) {
    log.info(
    "May the Schwartz be with you!" );
    }
    else {
    log.info(
    "Hello, mere mortal." );
    }
    我們還可以測試它是否有某種類型實體上的權限:
    if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
    log.info(
    "You may use a lightsaber ring. Use it wisely.");
    }
    else {
    log.info(
    "Sorry, lightsaber rings are for schwartz masters only.");
    }
    此外,我們可以執行一個非常強大的實例級權限檢查 - 查看用戶是否有能力訪問一個特定類型的實例:
    if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
    log.info(
    "You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " + "Here are the keys - have fun!");
    }
    else {
    log.info(
    "Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
    }
    非常簡單,對嗎?
    最后,當用戶在程序程序中完成了使用,它們可以登出:
    currentUser.logout(); //removes all identifying information and invalidates their session too.
    最終Tutorial類
    在添加了上述代碼后,以下是最終的Tutorial類文件.只要你喜歡,你可以隨意編輯,并修改安全檢查(和INI配置)來玩它:
    Final src/main/java/Tutorial.java
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class Tutorial {
    private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
    public static void main(String[] args) {
    log.info(
    "My First Apache Shiro Application");
    Factory<
    SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);
    // get the currently executing user:
    Subject currentUser = SecurityUtils.getSubject();
    // Do some stuff with a Session (no need for a web or EJB container!!!)
    Session session = currentUser.getSession();
    session.setAttribute(
    "someKey", "aValue");
    String value = (String) session.getAttribute("someKey");
    if (value.equals("aValue")) {
    log.info(
    "Retrieved the correct value! [" + value + "]");
    }
    // let's login the current user so we can check against roles and permissions:
    if (!currentUser.isAuthenticated()) {
    UsernamePasswordToken token =
    new UsernamePasswordToken("lonestarr", "vespa");
    token.setRememberMe(
    true);
    try {
    currentUser.login(token);
    }
    catch (UnknownAccountException uae) {
    log.info(
    "There is no user with username of " + token.getPrincipal()); }
    catch (IncorrectCredentialsException ice) {
    log.info(
    "Password for account " + token.getPrincipal() + " was incorrect!");
    }
    catch (LockedAccountException lae) {
    log.info(
    "The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it.");
    }
    // ... catch more exceptions here (maybe custom ones specific to your application?
    catch (AuthenticationException ae) {
    //unexpected condition? error?
    }
    }
    //say who they are:
    //print their identifying principal (in this case, a username):
    log.info(
    "User [" + currentUser.getPrincipal() + "] logged in successfully.");
    //test a role:
    if (currentUser.hasRole("schwartz")) {
    log.info(
    "May the Schwartz be with you!");
    }
    else {
    log.info(
    "Hello, mere mortal.");
    }
    //test a typed permission (not instance-level)
    if (currentUser.isPermitted("lightsaber:weild")) {
    log.info(
    "You may use a lightsaber ring. Use it wisely.");
    }
    else {
    log.info(
    "Sorry, lightsaber rings are for schwartz masters only.");
    }
    //a (very powerful) Instance Level permission:
    if (currentUser.isPermitted("winnebago:drive:eagle5")) {
    log.info(
    "You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!");
    }
    else {
    log.info(
    "Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
    }
    //all done - log out!
    currentUser.logout();
    System.exit(0);
    }
    }
    總結
    希望這本入門教程幫助你了解如何在一個基本應用中設置Shiro,以及Shiro的主要設計理念 Subject 和SecurityManager。
    但這是一個相當簡單的應用。你可能會問自己,“如果我不想使用INI用戶帳戶而是要連接到一個更復雜的用戶數據源,又該怎么做呢?”
    要回答這個問題,需要深入一點了解Shiro的架構和支持的配置機制。我們將下一個Shiro的Architecture 進行講解.
    Apache Shiro 架構
    Apache Shiro的設計目標是通過直觀而簡單的方式來簡化應用程序安全的開發. Shiro的核心設計模型-在一個上下文里與應用程序進行交互.
    軟件應用程序程序通常是基于用戶存儲來設計的. 也就是說,你會經常設計用戶如何與軟件進行交互的接口或服務.
    例如,你可能會說, "如果用戶與應用程序交互時已經登錄了,我應該向其展示一個按鈕,它們可以點擊來查看它們的帳戶信息.如果它們沒有登錄,我將向它們展示一個登錄按鈕."
    此示例語句暗示了應用程序在很大程度上是為了滿足用戶的需求而編寫的。即使'用戶'是另一個軟件系統,而不是一個人,你仍然要編寫代碼來反映誰(或什么)目前正在與您的軟件交互。
    高層次概述
    在最高概念層面,Shiro的架構包含了3個主要概念:Subject, SecurityManager 和 Realms. 下面的圖表是這些組件如何交互的高級概述,我們將覆蓋下面的每一個概念:



    Subject:
    正如我們在 Tutorial中提到的, Subject本質上當前執行用戶的特定安全視圖.
    "User”往往意味著一個人類,一個Subject可以是一個人,但它也可能代表一個第三方服務,后臺賬戶,cron作業,或任何類似的東西-基本上是當前與軟件交互的任何東西. 
    Subject 實例都綁定在SecurityManager上. 當你與Subject交互時,這些交互都會轉換成特定subject與SecurityManager的交互. 
    SecurityManager:
    SecurityManager 是Shiro架構的心臟,它扮演了一種保護傘(umbrella)對象,協調其內部的安全組件共同形成一個對象圖.
    然而,一旦應用程序配置了SecurityManager及其內部對象圖,它就會退居幕后,應用開發人員幾乎把他們的所有時間都花在Subject API調用上。
    后面我們會詳細討論SecurityManager, 但要意識到,當你與Subject交互時,其實是幕后的SecurityManager在執行Subject的安全操作.這可以從上面的圖中得到反映.
    Realms:
    Realms扮演的是Shiro與應用程序安全數據的橋梁或連接器.
    當它與像用戶帳戶這樣的相關安全數據進行實際交互時,如執行認證(login) 和授權(訪問控制), Shiro會查看為應用程序中配置的一個或多個Realms. 
    從這種意義上說,Realm本質上是一個帶有安全特征的數據訪問對象(DAO): 它封裝了數據源的連接細節,并按需將相關數據提供給Shiro.
    當配置Shiro時,你必須至少指定一個Realm來用于驗證或授權. SecurityManager可配置多個Realms, 但至少要配置一個. 
    Shiro提供了多個開箱即用的Realms來連接多種安全數據源,如LDAP, 關系型數據庫(JDBC),像INI和屬性文件這樣的文本配置源.
    如果默認的Realms不能滿足你的需要,你可以插入你自己的Realm實現來表示自定義數據源. 
    像其它內部組件一樣, Shiro SecurityManager會對如何使用Realms來獲取安全身份數據(表示為Subject實例)進行管理.
    詳細架構
    下圖展示了Shiro的核心架構概念以及每種概念的簡短概述:

    與軟件進行交互的實體(用戶,第三方服務,cron作業等等)特定安全視圖. 
    正如上面提到的, SecurityManager是Shiro架構的心臟.它主要充當保護傘對象,協調其管理組件來確保工作順利進行. 同時,它也會管理每個應用程序用戶的Shiro視圖,因此它知道如何執行每個用戶上的操作.
    認證負責驗證用戶的登錄. 當一個用戶試圖登錄時,認證將執行驗證邏輯. 認證組件知道如何協調一個或多個存儲相關用戶信息的. 從Realms中獲取的數據用于驗證用戶的身份,以確保其合法性.
    如果配置了多個Realm, AuthenticationStrategy 會協調Realms來確定在哪些情況下認證是成功或是失敗的(例如,如果某個realm是成功的,但其它是失敗的l,那么認證是成功的嗎?
    是否需要所有realms都成功?還是只需要第一個成功?). 
    授權是在應用程序中負責確定訪問控制的組件.最終說來它只是允許用戶做什么或不做什么的機制.類似于Authenticator,Authorizer也知道如何協調多個后端數據源來訪問角色和權限信息.
    Authorizer可使用這些信息來決定一個用戶是否允許來執行給定的操作. 
    SessionManager知道如何來創建和管理用戶生命周期,以為所有環境中的用戶提供強大的會話體驗. 在安全框架的世界中,這是獨特的特性- Shiro有管理任何環境中用戶會話的能力, 即使這些環境處于非Web/Servlet或在EJB容器環境.
    默認情況下,如果可行的話,Shiro會使用現有的會話機制, (例如. Servlet容器),但如果不存在這樣的環境,如獨立程序或非web環境,它會使用內置的企業會話管理來提供相同的編程體驗. SessionDAO的存在允許使用任何數據源來存儲會話. 
    SessionDAO可代表SessionManager來執行會話持久化操作(CRUD).這允許在會話管理框架中插入任何數據存儲. 
    緩存管理器用于創建和管理其它Shiro組件使用的緩存實例生命周期.因為Shiro在執行認證,授權和會話管理時,可訪問多個后端數據源,當使用這些數據源時,緩存通常是框架中提高性能的最好架構特征.
    任何一個現代開源或企業級緩存產品都可以插入到Shiro中,以提供快速,高效的用戶體驗. 
    加密是企業級安全框架中一個很自然的功能. Shiro的加密包包含了易于使用和理解的加密 Ciphers, Hashes (aka digests)表現以及不同的編碼實現.
    包中的所有類都是精心設計為易于使用和理解. 任何使用過Java本地加密的人,都可以輕易地駕馭它.
    Shiro的加密APIs簡化了復雜的Java機制,使得加密對于普通人來說也能簡單使用. 
    正如上面所提到的, Realms扮演的是Shiro與應用程序安全數據之間的橋梁或連接器.
    當它與像用戶帳戶這樣的相關安全數據進行實際交互時,如執行認證(login) 和授權(訪問控制), Shiro會查看為應用程序中配置的一個或多個Realms.
    你可以根據需要來配置多個Realms  (通常情況下一個數據源一個Realms) ,Shiro會按照需要來為認證和授權協調數據.
    SecurityManager
    因為Shiro的API鼓勵使用以Subject為中心的編程模型,大部分應用程序開發者會極少直接與SecurityManager交互(框架開發者有時可能會發現它很有用).
    即使是這樣,了解SecurityManager的功能,特別是在為應用配置時, 也是極其重要的.
    設計
    如前所述,應用程序安全管理器會為所有應用程序用戶執行安全操作和管理用戶狀態. 在Shiro的默認SecurityManager實現中包含:
    • Authentication(認證)
    • Authorization(授權)
    • Session Management(會話管理)
    • Cache Management(緩存管理)
    • Realm coordination(Realm協作)
    • Event propagation(事件傳播)
    • "Remember Me" Services(記住我服務)
    • Subject creation(Subject創建)
    • Logout(登出)
    等等.
    但在單個組件中,管理著多個功能. 而且,如果一切功能都集中到單一的實現類中,會使這些操作變得靈活和可定制化是相當困難的.
    為了簡化配置和開啟靈活的配置性/可插拔性, Shiro的實現設計上是高度模塊化的- SecurityManager實現及其類層次并沒有做這么多工作.
    相反,SecurityManager實現幾乎充當的是輕量級容器組件, 幾乎把所有行為/操作都委派給了內嵌/包裝組件.上面的詳細框架圖就反映了這種包裝設計.
    雖然是由組件來實際執行邏輯,SecurityManager實現知道如何以及何時來協調組件來完成正確行為.
    SecurityManager 實現同時也是JavaBeans兼容的,這允許你通過標準的Java訪問器/修改器方法(get*/set*)來輕松地定制可插拔組件.
    這意味著Shiro的架構模塊可以很容易配置自定義行為。
    簡單配置
    由于JavaBeans的兼容性, 使其很容易地通過支持JavaBeans的自定義組件來配置SecurityManager,如Spring, Guice, JBoss等等.
    下面我們就會涵蓋 Configuration .
    Apache Shiro 配置
    Shiro設計為可工作于任何環境, 從簡單的命令行應用程序到大型企業集群應用程序. 由于環境的多樣性,存在著多種配置機制. 本章講述Shiro核心支持的配置機制.
    多個配置選項
    Shiro的SecurityManager實現以及所有支持組件都是與JavaBeans兼容的.這允許Shiro使用特定配置格式,如常規Java, XML (Spring, JBoss, Guice, etc), YAML, JSON, Groovy Builder標記來配置.
    編程配置
    創建SecurityManager并使用可用的最簡單方式是創建一個org.apache.shiro.mgt.DefaultSecurityManager. 例如:
    Realm realm = //實例化或獲取一個Realm實例.我們后面會討論Realms. SecurityManager securityManager = new DefaultSecurityManager(realm); //通過靜態內存,使SecurityManager 實例對整個應用程序可用: SecurityUtils.setSecurityManager(securityManager);
    令人驚訝的是,雖只有3行代碼,但你現在有了一個適合于許多應用且功能齊全的Shiro環境。多么容易!
    SecurityManager 對象圖
    正如在Architecture章節中討論的, Shiro的SecurityManager實現本質上內嵌特定安全的組件模塊對象圖.
    因為它們也是JavaBeans兼容的,你可調用任何內嵌組件上的 getter和setter方法來配置SecurityManager和它的內部對象圖.
    例如,如果你想配置SecurityManager實例來使用自定義的SessionDAO來定制 Session Management,你可以直接使用內置的SessionManager的setSessionDAO來設置SessionDAO:
    ...
    DefaultSecurityManager securityManager =
    new DefaultSecurityManager(realm);
    SessionDAO sessionDAO =
    new CustomSessionDAO();
    ((DefaultSessionManager)securityManager.getSessionManager()).setSessionDAO(sessionDAO);
    ...
    使用直接方法調用,你可以配置SecurityManager的對象圖的任何部分.
    雖像程序化定制一樣簡單,但它并不代表大多數現實世界應用程序的理想配置。
    有以下幾個原因為什么編程配置可能不適合您的應用程序:
    • 它需要你了解和實例化一個直接實現.如果不需要了解具體和查找位置,這是很好的. 
    • 由于Java的類型安全特性,你需要在get*方法中對特定實現進行類型轉換. 因此太多的轉換是丑陋的,冗長的,并緊密耦合了你的實現類.
    • SecurityUtils.setSecurityManager 方法調用使得實例化了的SecurityManager實例化了一個 VM 靜態單例, 在這里是很好的,但對于多數應用程序來說,如果在同一個JVM中運行了多個開啟了Shiro的應用程序,將會引發問題.本身來說,如果實例是應用程序級單例的,這會更好,但不是靜態內存引用. 
    • 為了讓Shiro配置生效,每次你都需要重新編譯代碼.
    但,即使有這些警告,直接編程操作方案對于內存受限的環境,仍然是價值的,像智能手機應用程序.如果你的應用程序不會運行內存受限環境中,你會發現基于文本的配置更加簡單和閱讀.
    INI 配置
    大部分應用程序都受益于基于文本的配置,這樣可以獨立于源碼進行修改,甚至使得那于不熟悉Shiro API的人來說,也更易于理解.
    為了確保通用基于文本配置的解決方案能工作于任何環境(有很小的依賴),Shiro支持使用 INI format 來構建SecurityManager 對象圖和它的支撐組件.
    INI易于閱讀和配置,并可以簡單建立而適用于大部分應用程序.
    從INI創建SecurityManager
    下面有兩個例子來講述如何基于INI配置來構建SecurityManager.
    從INI資源來構建SecurityManager
    我們可以從INI資源路徑來創建SecurityManager實例. 資源可通過文件系統, classpath,或URLs來獲取,此時其前輟分別為file:, classpath:, 或 url: .
    這個例子使用Factory來從classpath根路徑下來攝取shiro.ini文件,并返回SecurityManager實例:

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.util.Factory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    ...
    Factory<
    SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);
    從INI實例來構建SecurityManager
    INI配置也通過編程方式來構建,即通過org.apache.shiro.config.Ini 類. Ini 類的功能類似于 JDK java.util.Properties 類,但還額外支持分段名稱的分割.
    For example:
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.util.Factory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.config.Ini;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    ...
    Ini ini =
    new Ini(); //按需填充 Ini實例
    ...
    Factory<
    SecurityManager> factory = new IniSecurityManagerFactory(ini);
    SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);
    現在,我們知道如何從INI配置來構建一個SecurityManager, 讓我們明確地了解如何定義一個Shiro INI配置.
    INI 分段
    INI是一個包含由唯一分段組織的key/value對的文本配置. 每個段中Keys是唯一的,而不是整個配置(這一點不像JDK Properties).
    每個段都可像單個Properties定義進行查看.
    注釋行可以#或;開頭
    下面是分段的例子:
    # ======================= # Shiro INI configuration # =======================
    [main]
    # 這里用于定義對象及其屬性,如securityManager, Realms 以及任何需要用于構建
    SecurityManager的配置
    [users]
    # 'users' 分段用于簡單部署
    # 當你只需要少量靜態定義的用戶帳戶時
    [roles]
    # 'roles' 分段用于簡單部署
    # 當你只需要少量靜態定義的角色時
    [urls] # 'urls' 分段用于web應用程序中作基于url的基礎安全控制
    # 我們會Web文檔中討論這個分段
    [main]
    [main]分段是你配置應用程序SecurityManager實例,及其任何依賴,如Realms.
    像SecurityManager或其依賴這樣的配置對象實例聽起來對于INI來說,是一件困難的事情,因為在這里我們只能使用name/value對.
    但是通過一點點的約定和對對象圖的理解,你會發現你可以做得很好。Shiro用這些假設使一個簡單的配置機制成為了可能。
    我們常喜歡把這種方法稱為“窮人的“依賴注入,雖然不比全面的Spring/ Guice / JBoss的XML文件強大,但你會發現它沒有過多的復雜性。
    當然也可使用其他的配置機制,但對于Shiro來說,他們不是必須的.
    只是為了刺激你的欲望,這里有一個有效的[main]配置實例:
    [main]
    sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
    myRealm = com.company.security.shiro.DatabaseRealm
    myRealm.connectionTimeout = 30000
    myRealm.username = jsmith
    myRealm.password = secret
    myRealm.credentialsMatcher = $sha256Matcher
    securityManager.sessionManager.globalSessionTimeout = 1800000
    定義對象
    細想下面的[main] 分段片斷:
    [main] myRealm = com.company.shiro.realm.MyRealm ...
    這行實例化了一個com.company.shiro.realm.MyRealm類型的新對象,并為了以后的引用和配置,使其在名稱myRealm名稱下可用.
    如果實例的對象實現了org.apache.shiro.util.Nameable接口, 那么可在對象上使用名稱值(myRealm)來調用Nameable.setName.
    設置對象屬性
    原始類型值
    簡單原始類型屬性可以使用=號來賦值:
    ...
    myRealm.connectionTimeout = 30000
    myRealm.username = jsmith ...
    這些行的配置轉換成方法調用為:
    ...
    myRealm.setConnectionTimeout(30000);
    myRealm.setUsername(
    "jsmith"); ...
    這怎么可能呢?它假定所有的對象都是java bean兼容的POJOs。
    在后臺,Shiro默認使用Apache Commons BeanUtils來設置這些屬性。所以盡管INI值是文本,BeanUtils知道如何將字符串值的原始類型,然后調用相應的JavaBean的setter方法。
    引用值
    如果需要設置的值不是原始類型而是其它對象呢? 你可以使用$來引用先前定義的實例.例如:
    ...
    sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
    ...
    myRealm.credentialsMatcher = $sha256Matcher ...
    這會簡單地定位名為sha256Matcher 的對象,然后使用 BeanUtils 在 myRealm 實例上來設置這個對象(通過調用myRealm.setCredentialsMatcher(sha256Matcher)方法).
    內嵌屬性
    在=的左邊使用點號分隔法來對內嵌屬性進行設值.例如:
    ...
    securityManager.sessionManager.globalSessionTimeout = 1800000 ...
    用BeanUtils轉換成下面的邏輯為:
    securityManager.getSessionManager().setGlobalSessionTimeout(1800000);
    圖的遍歷可以根據需要來設置深度: object.property1.property2....propertyN.value = blah
    BeanUtils Property Support
    Any property assignment operation supported by the BeanUtils.setProperty method will work in Shiro's [main] section, including set/list/map element assignments.
    See the Apache Commons BeanUtils Website and documentation for more information.
    Byte 數組值
    由于原始字節數組不能文本格式進行指定,我們必須使用字節數組的文本編碼.這些值可以通過Base64 編碼字符串(默認)或 Hex編碼字符串進行指定.
    默認是Base64 ,因為 Base64編碼需要更少的實際文本來表現值- 它有一個更大的編碼字母表,這意味著你的令牌更短(對于文本配置更好).
    # The 'cipherKey' attribute is a byte array. By default, text values
    #
    for all byte array properties are expected to be Base64 encoded:
    securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA== ...
    然而,如果你選擇使用Hex編碼, 你必須使用0x ('zero' 'x')作為字符串的前輟:
    securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008
    Collection屬性
    Lists, Sets 和Maps 也可以像其它屬性來設置 -要么直接設置,要么通過內嵌屬性設置. 對于sets和Lists,只需要以逗號分隔來指定值或對象引用的集合:
    sessionListener1 = com.company.my.SessionListenerImplementation
    ...
    sessionListener2 = com.company.my.other.SessionListenerImplementation
    ...
    securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2
    對于Maps,你可以以逗號分隔的key-value對列表來指定, 每個key-value對都由冒號來分隔
    object1 = com.company.some.Class
    object2 = com.company.another.
    Class
    ...
    anObject = some.class.with.a.Map.property
    anObject.mapProperty = key1:$object1, key2:$object2
    In the above example, the object referenced by $object1 will be in the map under the String key key1, i.e. map.get("key1") returns object1. You can also use other objects as the keys:
    anObject.map = $objectKey1:$objectValue1, $objectKey2:$objectValue2 ...
    注意事項
    順序影響
    雖然INI格式非常方便和易于理解,但它并沒有像其它基于text/XML配置機制強大. 使用上述機制時,要理解的最重要的事情是順序帶來的影響.
    小心
    Each object instantiation and each value assignment is executed in the order they occur in the 在每個[main] 分段中,每個對象實例和及值的分配都是出現順序來執行的.
    這些行最終都會轉換成對avaBeans getter/setter 方法的調用,且這些方法也是按相同順序來調用的!在編寫配置時,要當心.
    覆蓋實例
    任何對象都可以通過配置文件后面新定義的實例來覆蓋.因此對于下面的例子,第二個myRealm定義將覆蓋前面一個:
    ...
    myRealm = com.company.security.MyRealm
    ...
    myRealm = com.company.security.DatabaseRealm ...
    譯者注:如果這兩個key在同一個段內,就與先前的描述違背了.
    這個導致myRealm成為com.company.security.DatabaseRealm的實例,先前的實例將不再使用(垃圾回收掉).
    默認SecurityManager
    你可能已經注意到了,上面的例子中沒有定義 SecurityManager 實例, 我們跳到右邊只設置了一個內嵌屬性:
    myRealm = ...
    securityManager.sessionManager.globalSessionTimeout = 1800000 ...
    這是因為securityManager實例是特定的一個- 它已經為你實例化好了,因此你不需要了解具體的SecurityManager實現.
    當然,如果你想指定你自己的實現,你可以定義你的實現來進行覆蓋:
    ... securityManager = com.company.security.shiro.MyCustomSecurityManager ...
    當然,極少需要這樣- Shiro的SecurityManager實現是可充分定制的,通??梢园慈魏涡枰M行配置.
    [users]
    [users] 分段允許你定義一些靜態用戶帳戶集合. 這在一個非常小的用戶帳戶或用戶帳戶不需要在運行時動態創建環境中有用. 下面是例子:
    [users]
    admin = secret lonestarr = vespa, goodguy, schwartz darkhelmet = ludicrousspeed, badguy, schwartz
    Automatic IniRealm
    只定義非空的[users]或[roles]分段會自動觸發org.apache.shiro.realm.text.IniRealm實例的創建,并使得[main]分段下的iniRealm可用.
    你可以像上面一樣來配置其它任何對象.
    行格式
    [users]分段中的每行都有下面的格式:
    username = password, roleName1roleName2, ..., roleNameN
    • 等號左邊的值是用戶名
    • 等號右邊的第一個值是用戶的密碼.密碼是必須的.
    • 密碼后面以逗號分隔的值是為用戶分配的角色.角色名稱是可選的.
    密碼加密
    如果你不想[users]分段中的密碼是明文的,你可以使用你喜歡的hash算法(MD5, Sha1, Sha256)來對其加密,并使用結果字符串來作為密碼值.
    默認情況下期望的密碼字符串是Hex編碼的,但你可以使用Base64編碼來代替(見下面).
    Easy Secure Passwords
    為了節省時間, 你可以使用Shiro的 Command Line Hasher, 它會將密碼hash成其它類型的資源.對于加密 INI [users] 密碼是非常方便的.
    一旦你指定了哈稀化的文本值, 你必須告訴Shiro它們是加密的. 你可為隱式創建的iniRealm 在[main]分段中來配置適當的對應你指定的哈希算法的CredentialsMatcher實現:
    [main]
    ...
    sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
    ...
    iniRealm.credentialsMatcher = $sha256Matcher
    ...
    [users]
    # user1 = sha256-hashed-hex-encoded password, role1, role2,
    ...
    user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, ...
    你可在CredentialsMatcher上配置任何屬性,如反映哈稀策略的對象,例如, 是否使用鹽(salting)或者執行多少次hash迭代.
    參考org.apache.shiro.authc.credential.HashedCredentialsMatcher JavaDoc來更好的理解哈稀策略.
    例如,如果你想對用戶密碼使用Base64編碼來代替默認的Hex,你可以按如下來指定:
    [main]
    ...
    # true = hex, false = base64:
    sha256Matcher.storedCredentialsHexEncoded =
    false
    [roles]
    [roles]分段允許你使用Permissions 來關聯定義在[users]分段中的角色.
    再次說明,這對于少量角色或在運行時不需要動態創建角色的環境有用.下面是一個例子:
    [roles]
    # 'admin' 的角色擁有所有權限, 使用通配符'*'來表示 admin = *
    # 'schwartz' 角色可在任何lightsaber上執行任何操作(*): schwartz = lightsaber:*
    # 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
    行格式
    [roles]分段中的每行必須按下面的格式來定義role-to-permission(s) key/value:
    rolename = permissionDefinition1permissionDefinition2, ..., permissionDefinitionN
    這里的permissionDefinition 是任意的字符串,但大部分人為了靈活性,會使用符合org.apache.shiro.authz.permission.WildcardPermission 格式的字符串. 參考Permissions 文檔來了解更多關于權限的信息
    內部逗號
    注意,如果在單個permissionDefinition 中存在逗號分隔符(如. printer:5thFloor:print,info),你必須使用雙引號,以避免解析錯誤:
    "printer:5thFloor:print,info"
    無權限的角色
    如果角色不需要關聯權限,那么則不需要在[roles]分段中列出.如果角色不存在,只需要在[users]分段中定義角色就足夠創建角色了.
    [urls]
    這個分段及其它選項將在Web章節中進行講解.
    posted on 2016-08-18 17:32 胡小軍 閱讀(2501) 評論(0)  編輯  收藏 所屬分類: Apache Shiro

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲成a人片在线看| 国产精品高清免费网站| 国产成人啪精品视频免费网| 日韩久久无码免费毛片软件| 久久久影院亚洲精品| 中国在线观看免费高清完整版| 深夜a级毛片免费无码| 少妇中文字幕乱码亚洲影视| 青青草国产免费久久久下载| 女同免费毛片在线播放| 亚洲人成网站在线在线观看| 亚洲五月综合缴情在线观看| 免费a级毛片高清视频不卡| 国产黄在线观看免费观看不卡| 亚洲国产成人精品久久| 精品亚洲视频在线观看 | a成人毛片免费观看| 亚洲免费中文字幕| 伊伊人成亚洲综合人网7777| 最新猫咪www免费人成| 久草免费福利视频| 黄网站色视频免费观看45分钟| 亚洲一区免费观看| 亚洲午夜精品一级在线播放放| av大片在线无码免费| 中文字幕久无码免费久久| 亚洲女女女同性video| 亚洲高清在线播放| 国产亚洲老熟女视频| 国产青草视频免费观看97| 韩国免费一级成人毛片| 无码人妻一区二区三区免费看 | 国产亚洲美女精品久久久2020| 我想看一级毛片免费的| 99视频有精品视频免费观看| 国产精品无码永久免费888| 日本亚洲中午字幕乱码| 亚洲AV无码无限在线观看不卡 | 区三区激情福利综合中文字幕在线一区亚洲视频1 | 亚洲综合区图片小说区| 伊人久久大香线蕉亚洲五月天 |