shiro实战系列(八)之安全管理器
Apache Shiro 提供安全框架界獨一無二的東西:一個完整的企業級 Session 解決方案,從最簡單的命令行及智能手機 應用到最大的集群企業 Web 應用程序。?? 這對許多應用有著很大的影響——直到 Shiro 出現,如果你需要 session 支持,你需要部署你的應用程序到 Web 容 器或使用 EJB 有狀態會話 Bean。Shiro 的 Session 支持比這兩種機制的使用和管理更為簡單,而且它在適用于任何程序,不論容器。? 即使你在一個 Servlet 或 EJB 容器中部署你的應用程序,仍然有令人信服的理由來使用 Shiro 的 Session 支持而不是容 器的。下面是一個 Shiro 的 Session 支持的最可取的功能列表:
Features
?
(1)POJO/J2SE based(IoC friendly)
- Shiro 的一切(包括所有 Session 和 Session Management 方面)都是基于接口和 POJO 實現。這可以讓你輕松地配置所有擁有任何 JavaBeans 兼容配置格式(如 JSON,YAML,Spring XML 或類 似的機制)的會話組件。你也可以輕松地擴展 Shiro 的組件或編寫你自己所需的來完全自定義 session management。
?
(2)Easy Custom Session Storage
- 因為 Shiro 的 Session 對象是基于 POJO 的,會話數據可以很容易地存儲在任意 數量的數據源。這允許你自定義你的應用程序會話數據的確切位置——例如,文件系統,聯網的分布式緩存, 關系數據庫,或專有的數據存儲。
?
(3) Container-Independent Clustering
?- Shiro 的會話可以很容易地聚集通過使用任何隨手可用的網絡緩存產品,像 Ehcache + Terracotta,Coherence,GigaSpaces,等等。這意味著你可以為 Shiro 配置會話群集一次且僅一次, 無論你部署到什么容器中,你的會話將以相同的方式聚集。不需要容器的具體配置!
?
(4)Heterogeneous Client Access
?- 與 EJB 或 web 會話不同,Shiro 會話可以被各種客戶端技術“共享”。例如,一 個桌面應用程序可以“看到”和“共享”同一個被使用的物理會話通過在 Web 應用程序中的同一用戶。我們 不知道除了 Shiro 以外的其他框架能夠支持這一點。
?
(5) Event Listeners
?- 事件監聽器允許你在會話生命周期監聽生命周期事件。你可以偵聽這些事件和對自定義應用 程序的行為作出反應——例如,更新用戶記錄當他們的會話過期時。
?
(6)Host Address Retention
– Shiro Sessions 從會話發起地方保留 IP 地址或主機名。這允許你確定用戶所在,并作 出相應的反應(通常是在 IP 分配確定的企業內部網絡環境)。
?
(7) Inactivity/Expiration Support
- 由于不活動導致會話過期如預期的那樣,但它們可以延續很久通過 touch()方法 來保持它們“活著”,如果你希望的話。這在 RIA(富互聯網應用)環境非常有用,用戶可能會使用桌面應用程 序,但可能不會經常與服務器進行通信,但該服務器的會話不應過期。
?
(8) Transparent Web Use - Shiro 的網絡支持,充分地實現和支持關于 Sessions(HttpSession 接口和它的所有相關 的 API)的 Servlet2.5 規范.這意味著你可以使用在現有 Web 應用程序中使用 Shiro 會話,并且你不需要改變任 何現有的 Web 代碼。 Can be used for SSO - 由于 Shiro 會話是基于 POJO 的,它們可以很容易地存儲在任何數據源,而且它們可以跨 程序“共享”如果需要的話。我們稱之為"poor man's SSO",并它可以用來提供簡單的登錄體驗,由于共享的 會話能夠保留身份驗證狀態。
?
一、 Using Sessions
幾乎與所有其他在 Shiro 中的東西一樣,你通過與當前執行的 Subject 交互來獲取 Session:
Subject currentUser = SecurityUtils.getSubject();????????????????????????
Session session = currentUser.getSession();
session.setAttribute("someKey", someValue);??
subject.getSession()方法是調用 currentUser.getSubject(true)的快捷方式。
?
對于那些熟悉 HttpServletRequest API 的,Subject.getSession(boolean create)方法與 HttpServletRequest.getSession(boolean create)方法有著異曲同工之效。
(1)??? 如果該 Subject 已經擁有一個 Session,則 boolean 參數被忽略且 Session 被立即返回。
(2)??? 如果該 Subject 還沒有一個 Session 且 create 參數為 true,則創建一個新的會話并返回該會話。
(3)??? 如果該 Subject 還沒有一個 Session 且 create 參數為 false,則不會創建新的會話且返回 null。
?
二、Any Application(所有應用)
getSession 要求能夠在任何應用程序工作,甚至是非 Web 應用程序。???????????????????? 當開發框架代碼來確保一個 Session 沒有被創建是沒有必要的時候,subject.getSession(false)可以起到很好的作用。?? 當你獲取了一個 Subject 的 Session 后,你可以用它來做許多事情,像設置或取得 attribute,設置其超時時間,以及 更多。請參見 Session 的 JavaDoc 來了解一個單獨的會話能夠做什么。
?
三、The SessionManager
SessionManager,名如其意,在應用程序中為所有的 subject 管理 Session—— 創建,刪除,inactivity(失效)及驗證,等等。如同其他在 Shiro 中的核心結構組件一樣,SessionManager 也是一個由 SecurityManager 維護的頂級組件。?? 默認的 SecurityManger 實現是默認使用立即可用的 DefaultSessionManager。DefaultSessionManager 的實現提供一個 應用程序所需的所有企業級會話管理,如 Session 驗證,orphan cleanup,等等。這可以在任何應用程序中使用。?
四、Web Applications
?
?Web 應用程序使用不同 SessionManager 實現。請參見 Web 文檔獲取 web-specific Session Management 信息。?? 像其他被 SecurityManager 管理的組件一樣,SessionManager 可以通過 JavaBean 風格的getter/setter 方法在所有 Shiro 默認 SecurityManager 實現(getSessionManager()/setSessionManager())上獲取或設置值。或者例如,如果在使用 shiro.ini 配置:
?
但從頭開始創建一個 SessionManager 是一個復雜的任務且是大多數人不想親自做的情。Shiro 的立即可用的 SessionManager 實現是高度可定制的和可配置的,并滿足大多數的需要。本文檔的其余部分假定你將使用 Shiro 的 默認 SessionManager 實現,當覆蓋配置選項時。但請注意,你基本上可以創建或插入任何你想要的東西?? 。
?
五、Session Timeout
?默認地,Shiro 的 SessionManager 實現默認是 30 分鐘會話超時。也就是說,如果任何 Session 創建后閑置(未被使 用,它的上次訪問時間未被更新)的時間超過了 30 分鐘,那么該 Session 就被認為是過期的,且不允許再被使用。
?? 你可以設置 SessionManager 默認實現的 globalSessionTimeout 屬性來為所有的會話定義默認的超時時間。例如,如 果你想超時時間是一個小時而不是 30 分鐘:??
?
?
六、? Per-Session Timeout
上面的 globalSessionTimeout 值默認是為新建的 Session 使用的。你可以在每一個會話的基礎上控制超時時間通過設 置單獨的會話超時時間值。與上面的 globalSessionTimeout 一樣,該值以毫秒(不是秒)為時間單位。
?
七、Session Listeners
Shiro 支持 SessionListener 概念來允許你對發生的重要會話作出反應。你可以實現 SessionListener 接口(或擴展易用 的 SessionListenerAdapter)并與相應的會話操作作出反應。?? 由于默認的 SessionManager sessionListeners 屬性是一個集合,你可以對 SessionManager 配置一個或多個 listener 實 現,就像其他在 shiro.ini 中的集合一樣:
?
?
八、Configure a SessionDAO
Shiro 的默認配置本地 SessionManagers 使用僅內存 Session 存儲。這是不適合大多數應用程序的。大多數生產應用 程序想要配置提供的 EHCache(見下文)支持或提供自己的 SessionDAO 實現。?? 請注意 Web 應用程序默認使用基于 servlet 容器的 SessionManager,且沒有這個問題。這也是使用 Shiro 本地 SessionManager 的唯一問題。?
?
九、EHCache SessionDAO
EHCache 默認是沒有啟用的,但如果你不打算實現你自己的 SessionDAO,那么強烈地建議你為 Shiro 的 SessionManagerment 啟用 EHCache 支持。EHCache SessionDAO 將會在內存中保存會話,并支持溢出到磁盤,若內存 成為制約。這對生產程序確保你在運行時不會隨機地“丟失”會話是非常好的。
?
十、Use EHCache as your default????
如果你不準備編寫一個自定義的 SessionDAO,則明確地在你的 Shiro 配置中啟用 EHCache。EHCache 帶來的好處遠 不止在 Sessions,緩存驗證和授權數據方面。更多信息,請參見 Caching 文檔。
?
十一、Container-Independent Session Clustering????
如果你急需獨立的容器會話集群,EHCache 會是一個不錯的選擇。你可以顯式地在 EHCache 之后插入 TerraCotta, 并擁有一個獨立于容器集群的會話緩存。不必再擔心 Tomcat,JBoss,Jetty,WebSphere 或 WebLogic 特定的會話集 群!?? 為會話啟用 EHCache 是非常容易的。首先,確保在你的 classpath 中有 shiro-ehcache-<version>.jar 文件(請參見 Download 頁面或使用 Maven 或 Ant+Ivy)。?? 當在 classpath 中后,這第一個 shiro.ini 實例向你演示怎樣為所有 Shiro 的緩存需要(不只是會話支持)使用 EHCache:
?
?
最后一行,securityManager.cacheManager = $cacheManager,為所有 Shiro 的需要配置了一個 CacheManager。該 CacheManager 實例會自動地直接傳送到 SessionDAO(通過 EnterpriseCacheSessionDAO 實現 CacheManagerAware 接 口的性質)。?? 然后,當SessionManager要求EnterpriseCacheSessionDAO去持久化一個Session時,它使用一個EHCache支持的 Cache 實現去存儲 Session 數據。??
?
十二、? Web Applications?
當使用 Shiro 本地的 SessionManager 實現時不要忘了分配 SessionDAO 是一項功能。Web 應用程序默認使用基于容器 的 SessionManager,它不支持 SessionDAO。如果你想在 Web 應用程序中使用基于 EHCache 的會話存儲,配置一個 如上所解釋的 Web SessionManager。
?
十三、EHCache Session Cache Configuration
默認地,EhCacheManager 使用一個 Shiro 特定的 ehcache.xml 文件來建立 Session 緩存區以及確保 Sessions 正常存取 的必要設置。?? 然而,如果你想改變緩存設置,或想配置你自己的 ehcache.xml 或 EHCache net.sf.ehcache.CacheManager 實例,你需 要配置緩存區來確保 Sessions 被正確地處理。?? 如果你查看默認的 ehcache.xml 文件,你會看到接下來的 shiro-activeSessionCache 緩存配置:
<cache name="shiro-activeSessionCache"?????
maxElementsInMemory="10000"
overflowToDisk="true"
eternal="true"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="600"/>
?
如果你希望使用你自己的 ehcache.xml 文件,那么請確保你已經為 Shiro 所需的定義了一個類似的緩存項。很有可能 你會改變 maxElementsInMemory 的屬性值來吻合你的需要。然而,至少下面兩個存在于你自己配置中的屬性是非常 重要的:
(1)??? overflowToDisk="true" - 這確保當你溢出進程內存時,會話不丟失且能夠被序列化到磁盤上。
(2)??? ?eternal="true" - 確保緩存項(Session 實例)永不過期或被緩存自動清除。這是很有必要的,因為 Shiro 基于 計劃過程完成自己的驗證。如果我們關掉這項,緩存將會在 Shiro 不知道的情況下清掃這些 Sessions,這可能 引起麻煩。
?
十四、EHCache Session Cache Name
默認地,EnterpriseCacheSessionDAO 向 CacheManager 尋求一個名為"shiro-activeSessionCache"的 Cache。該緩存的 name/region 將在 ehcache.xml 中配置,如上所述。?? 如果你想使用一個不同的名字而不是默認的,你可以在 EnterpriseCacheSessionDAO 上配置名字,例如:
只要確保在 ehcahe.xml 中有一項與名字匹配且你已經配置好了如上所述的 overflowToDisk="true"和 eternal="true"。
?
十五、Custom Session IDs
Shiro 的 SessionDAO 實現使用一個內置的 SessionIdGenerator 組件來產生一個新的 Session ID 當每次創建一個新的會 話的時候。該 ID 生成后,被指派給新近創建的 Session 實例,然后該 Session 通過 SessionDAO 被保存下來。?? 默認的 SessionIdGenerator 是一個 JavaUuidSessionIdGenerator,它能產生基于 Java UUIDs 的 String IDs。該實現能夠 支持所有的生產環境。?? 如果它不符合你的需要,你可以實現 SessionIdGenerator 接口并在 Shiro 的 SessionDAO 實例上配置該實現。例如, 在 shiro.ini 中:
?
十六、Session Validation & Scheduling
Sessions 必須被驗證,這樣任何無效(過期或停止)的會話能夠從會話數據存儲中刪除。這保證了數據存儲不會由于不 能再次使用的會話而導致寫入超時。?? 由于性能上的原因,僅僅在 Sessions 被訪問(也就是 subject.getSession())時驗證它們是否停止或過期。這意味著, 如果沒有額外的定期驗證,Session orphans(孤兒)將會開始填充會話數據存儲。
?
一個常見的說明孤兒的例子是 Web 瀏覽器中的場景:比方說,用戶登錄到 Web 應用程序并創建了一個會話來保留 數據(身份驗證狀態,購物車等)。如果用戶不注銷,并在應用程序不知道的情況下關閉了瀏覽器,則他們的會話 實質上是“躺在”會話數據存儲的(孤兒)。SessionManager 沒有辦法檢測用戶不再使用他們的瀏覽器,同時該會 話永遠不會被再次訪問(它是孤兒了)。?? 會話孤兒,如果它們沒有定期 清除,將會填充會話數據存儲(這是很糟糕的)。因此,為了防止丟放孤兒, SessionManager 實現支持 SessionValidationScheduler 的概念。SessionValidationScheduler 負責定期地驗證會話以確保 它們是否需要清理。??
?
十七、Default SessionValidationScheduler
默認可用的 SessionValidationScheduler 在所有環境中都是 ExecutorServiceSessionValidationScheduler,它使用 JDK ScheduledExecutorService 來控制驗證頻率。?? 默認地,該實現每小時執行一次驗證。你可以通過指定一個新的 ExecutorServiceSessionValidationScheduler 實例并指 定不同的間隔(以毫秒為單位)改變速率來更改驗證頻率:
?
十七、Custom SessionValidationScheduler
如果你希望提供一個自定義的 SessionValidationScheduler 實現,你可以指定它作為默認的 SessionManager 實例的一 個屬性。例如,在 shiro.ini 中:
?
十八、Disabling Session Validation
在某些情況下,你可能希望禁用會話驗證項,由于你建立了一個超出了 Shiro 控制的進程來為你執行驗證。例如, 也許你正在使用一個企業的 Cache 并依賴于緩存的 Time To Live 設置來自動地去除舊的會話。或者也許你已經制定 了一個計劃任務來自動清理一個自定義的數據存儲。在這些情況下你可以關掉 session validation scheduling:
當會話從會話數據存儲取回數據時它仍然會被驗證,但這會禁用掉 Shiro 的定期驗證。
?
十九、Enable Session Validation somewhere
如果你關閉了 Shiro 的 session validation scheduler,你必須通過其他的機制(計劃任務等)來執行定期的會話驗證。 這是保證會話孤兒不會填充數據存儲的唯一方法。??
?
二十、Invalid Session Deletion
正如我們上面所說的,進行定期的會話驗證主要目的是為了刪除任何無效的(過期或停止)會話來確保它們不會填 充會話數據存儲。?? 默認地,某些應用程序可能不希望 Shiro 自動地刪除會話。例如,如果一個應用程序已經提供了一個 SessionDAO 備 份數據存儲查詢,也許是應用程序團隊希望舊的或無效的會話在一定的時間內可用。這將允許團隊對數據存儲運行 查詢來判斷,例如,在上周某個用戶創建了多少個會話,或一個用戶會話的持續時間,或與之類似報告類型的查詢。??
?
在這些情形中,你可以關閉 invalid session deletion 項。例如,在 shiro.ini 中:
?
請注意!如果你關閉了它,你得為確保你的會話數據存儲不耗盡它的空間復雜。你必須自己從你的數據存儲中刪除 無效的會話!?? 還要注意,即使你阻止了 Shiro 刪除無效的會話,你仍然應該使用某種會話驗證方式——要沒通過 Shiro 的現有驗證 機制,要么通過一個你自己提供的自定義的機制(見上述的"Disabling Session Validation"獲取更多)。驗證機制將會 更新你的會話記錄以反映無效的狀態(例如,什么時候它是無效的,它最后一次被訪問是什么時候,等等),即使 你在其他的一些時間將手動刪除它們。?? 如果你配置 Shiro 來讓它不會刪除無效的會話,你得為確保你的會話數據存儲不會耗盡它的空間負責。你必須親自 從你的數據存儲刪除無效的會話!??????????????????????? 另外請注意,禁用會話刪除并不等同于禁用 session validation schedule(會話驗證調度)。你應該總是使用一個會話 驗證調度機制——無論是 Shiro 直接支持或者是你自己的。??
?
?
Sessions and subject State
?
一、 Stateful Applications(Sessions allowed)
默認地,Shiro 的 SecurityManager 實現使用一個 Subject 的 Session 作為一種策略來為接下來的引用存儲 Subject 的身 份 ID(PrincipalCollection)和驗證狀態(subject.isAuthenticated())。這通常發生在一個 Subject 登錄后或當一個 Subject 的身份 ID 通過 Remember 服務被發現后。
?
下面是使用這種默認方式的好處:
?
?
??任何服務于請求,調用或消息的應用程序可以用請求/調用/消息的有效載荷關聯會話 ID,且這是 Shiro 用入站 請求關聯用戶所有所必須的。例如,如果使用 Subject.Builder,這是需要獲取相關的 Subject 所需的一切: Serializable sessionId = //get from the inbound request or remote method invocation payload?? Subject requestSubject = new Subject.Builder().sessionId(sessionId),buildSubject();??
?
?
這給大多數 Web 應用程序及任何編寫遠程處理或消息框架的人帶來了令人難以置信的方便(這事實上是 Shiro 的 Web 支持在自己的框架代碼內關聯 Subject 和 ServletRequest)。
?
任何"RememberMe"身份基于一個能夠在第一次訪問就能持久化到會話的初始請求。這確保了 Subject 被記住 的身份可以跨請求保存而不需要反序列化及將它解釋到每個請求。例如,在一個 Web 應用程序中,沒有必要 去讀取每一個請求的加密 RememberMe Cookie,如果該身份在會話中是已知的。這可是一個很好的性能提升。
?
這給大多數 Web 應用程序及任何編寫遠程處理或消息框架的人帶來了令人難以置信的方便(這事實上是 Shiro 的 Web 支持在自己的框架代碼內關聯 Subject 和 ServletRequest)。
?
?
任何"RememberMe"身份基于一個能夠在第一次訪問就能持久化到會話的初始請求。這確保了 Subject 被記住 的身份可以跨請求保存而不需要反序列化及將它解釋到每個請求。例如,在一個 Web 應用程序中,沒有必要 去讀取每一個請求的加密 RememberMe Cookie,如果該身份在會話中是已知的。這可是一個很好的性能提升
?
?
二、Stateless Applications(Sessionless)
?
雖然上述的默認策略對于大多數應用程序而言是很好的(通常是可取的),但這對于嘗試盡可能無狀態的應用程序 來說是不合適的。許多無狀態的架構規定在請求中不能存在持久狀態,這種情況下的 Sessions 不會被允許(一個會 話其本質代表了持久狀態)。?? 但這一要求帶來一個便利的代價——Subject狀態不能跨請求保留。這意味著有這一要求的應用程序必須確保Subject 狀態可以在每一個請求中以其他的方式代表。?? 這幾乎總是通過驗證每個由應用程序處理的請求/調用/消息來完成的。例如,大多數無狀態 Web 應用程序通常支持 這一點通過執行 HTTP 基本驗證,允許瀏覽器驗證每一個代表最終用戶的請求。遠程或消息框架必須確保 Subject 的 身份和憑證連接到每一個調用或消息的有效載荷,通常是由框架代碼執行。??
?
三、Disabling Subject State Session Storage
在 Shiro 1.2 及以后開始,應用程序想禁用 Shiro 的內部實現策略——將 Subject 狀態持久化到會話,可以禁用所有 Subject 的這一項,通過下面的操作:?? 在 shiro.ini 中,在 securityManager 上配置下面的屬性:
?
四、Shiro's Needs vs. Your Needs
使用 Sessions 作為存儲策略將禁用 Shiro 本身的實現。它沒有完全地禁用 Sessions。如果你的任何代碼顯式地調用 subject.getSession()或 subject.getSession(true),一個 session 仍然會被創建
?
五、A Hybrid Approach
上面的 shiro.ini 配置中的(securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled = false)這一行將 會禁用 Shiro 為所有的 Subject 使用 Session 作為一種實現策略。
?
但,如果你想使用混合的方法呢?如果某些對象應該有會話而某些沒有?這種混合法方法能夠給許多應用程序帶來 好處。例如:
1.也許 human Subject(如 Web 瀏覽器用戶)由于上面提供的好處能夠使用 Session。 2.也許 non-human Subject(如 API 客戶端或第三方應用程序)不應該創建 session 由于它們與軟件的交互可能會 間歇或不穩定。
3.也許所有某種確定類型的 Subject 或從某一確定位置訪問系統的應該將狀態保持在會話中,但所有其他的不應 該。?? 如果你需要這個混合方法,你可以實現一個 SessionStorageEvaluator
?
六、SessionStorageEvaluator
在你想究竟控制哪個 Subject 能夠在它們的 Session 中保存它們的狀態的情況下,你可以實現 org.apache.shiro.mgt.SessionStorageEvaluator 接口,并告訴 Shiro 哪個 Subject 支持會話存儲。?? 該接口只有一個方法:
?
關于更詳細的 API 說明,請參見 SessionStorageEvaluator 的 JavaDoc。?? 你可以實現這一接口,并檢查 Subject,為了你可能做出這一決定的任何信息。
?
七、Subject Inspection
但實現 isSessionStorageEnabled(subject)接口方法時,你可以一直查看 Subject 并訪問任何你需要用來作出決定的東西。 當然所有期望的 Subject 方法都是可用的(getPrincipals()等),但特定環境的 Subject 實例也是有價值的。?? 例如,在 Web 應用程序中,如果該決定必須基于當前 ServletRequest 中的數據,你可以獲取該 request或該 response, 因為運行時的 Subjce 實例實際上就是一個 WebSubject 實例:
?
八、Web Applications
通常 Web 應用程序希望在每一個請求的基礎上容易地啟用或禁用會話的創建,不管是哪個 Subject 正在執行請求。 這經常在支持 REST 及 Messaging/RMI 構架上使用來產生很好的效果。例如,也許正常的終端用戶(使用瀏覽器的人) 被允許創建和使用會話,但遠程的 API 客戶端使用 REST 或 SOAP,不該擁有會話(因為它們在每一個請求上驗證, 常見 REST/SOAP 體系結構)。?? 為了支持這種hybrid/per-request的能力,noSessionCreation過濾器被添加到Shiro的默認為Web應用程序啟用的“池”。 該過濾器將會阻止在請求期間創建新的會話來保證無狀態的體驗。在 shiro.ini 的[urls]項中,你通常定義該過濾器在 所有其它過濾器之前來確保會話永遠不會被使用。
這個過濾器允許現有會話的任何會話操作,但不允許在過濾的請求創建新的會話。也就是說,一個請求或沒有會話 存在的 Subject 調用下面四個方法中的任何一個時,將會自動地觸發一個 DisabledSessionException 異常:
httpServletRequest.getSession()
httpServletRequest.getSession(true)
subject.getSession()
subject.getSession(true)??
?
如果一個 Subject 在訪問 noSessionCreation-protected-URL 之前已經有一個會話,則上述的四種調用仍然會如預期般 工作。?? 最后,在所有情況下,下面的調用將始終被允許:
?
httpServletRequest.getSession(false)
subject.getSession(false)
轉載于:https://www.cnblogs.com/youcong/p/9164367.html
總結
以上是生活随笔為你收集整理的shiro实战系列(八)之安全管理器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洛谷1527(bzoj2738)矩阵乘法
- 下一篇: 优盘属性怎么设置 如何设置优盘属性