易文康,程 驊,程耕國
(武漢科技大學 信息科學與工程學院,武漢 430081)
隨著互聯網技術的飛速發展,學校、企業和政府機關等機構所建立的信息管理系統,其管理的對象日益復雜,用戶所能訪問的數據資源日趨龐大,導致用戶的權限管理和日常維護變得越來越繁瑣。為在實現資源信息共享的同時,避免出現授權用戶越權訪問未授權的系統資源以及未授權用戶非法訪問系統資源等一系列Web[1]系統中的信息安全問題[2],需要進一步深入研究基于角色的訪問控制技術[3-4]和Shiro的授權機制,確保用戶對系統數據資源的訪問都是經過授權的,并防止非法用戶的訪問。這對保證系統中數據資源的安全性、保密性和完整性是非常必要的。本文主要研究如何運用Shiro安全框架[5]來阻止Web系統中的越權訪問控制(Broken Access Control,BAC)。
越權訪問[6]即跨越權限訪問,是Web應用中一種常見的安全缺陷,其產生原因是沒有對訪問用戶提交的數據參數進行權限檢查或者缺少跨域訪問的限制。越權訪問可以直接繞過基礎的網絡安全服務防御,它通過前端對數據或者參數進行請求構造、遍歷,在取得完整的Web應用程序或者數據庫權限前就可以得到相關用戶的個人信息。若沒有對越權訪問采取有效的解決或防御措施,將極有可能泄露客戶個人、企業和政府等機構的重要信息,存在很大的網絡信息安全隱患,造成不可估量的經濟損失。
針對Web系統訪問控制中越權訪問的安全隱患,研究者提出了各種解決方案,其中Shiro框架逐漸受到人們的重視。本文通過使用Shiro框架的授權方式和一種權限訪問控制算法來解決此安全問題。
Shiro是Apache 系列的一個Java開源安全開發框架[7],其本身具有良好的健壯性和易用性。Shiro提供了認證、授權、會話管理、加密等功能,可以為學校、企業和政府機關等機構提供信息安全解決方案。同時,Shiro本身集成了許多保護Java應用的特性,如支持Web應用[8]、線程和并發、緩存機制和測試工具等。與其他安全框架相比,Shiro的安全認證和授權方式比較簡潔且容易操作,編寫的代碼量顯著減少。本文主要研究如何將Shiro的授權功能應用在Web網絡安全問題中,解決越權訪問的問題。
當用戶登錄系統并進行數據操作訪問Web數據庫[9]時,可能由于本身系統設計上存在的安全漏洞[10]或者用戶權限分配和管理上存在的缺陷,導致授權用戶越權訪問未授權的數據庫資源或者未授權用戶非法訪問數據庫資源。為了阻止這些安全隱患的發生,可在Web應用中集成Shiro安全框架。
首先在Web工程里的web.xml配置文件中定義Shiro Servlet過濾器實現Web應用和Shiro框架的集成。該過濾器會過濾用戶發送來的所有請求,根據實際需求執行特定的邏輯判斷,當請求滿足一定要求時才允許通過,從而保證用戶在進行Web數據庫訪問時,會先對用戶的訪問權限進行判斷,若擁有相應的權限,則執行對應的業務邏輯操作,訪問數據庫并得到數據。數據庫訪問流程如圖1所示。

圖1 基于Shiro的數據庫訪問流程
授權,其實質就是訪問控制[11-12],控制系統用戶能夠訪問Web系統中的哪些資源,能執行哪些操作(如訪問頁面、編輯數據等),這些一般是通過系統管理員分配給用戶的角色和權限來決定的。Shiro支持權限的概念,能友好地與Web系統集成[13],還能夠通過運用通配符靈活地對權限進行匹配和檢查。目前,Shiro提供以下3種方式的授權:
1)編程式授權。通過編寫if/else等一系列Java的授權代碼塊來實現當前用戶的授權操作,例如:
Subject currentUser=SecurityUtils.getSubject();
if (currentUser.hasRole("read")) {
//有角色"read"權限
} else {
//沒有角色"read"權限}
2)JSP/GSP標簽式授權。在JSP/GSP頁面通過相應的標簽來實現授權,例如:
3)注解式授權。在執行的Java方法上放置相應的注解來實現系統用戶的授權操作(前提是開發的系統需要支持面向切面的編程[14]),例如:
@RequiresPermissions("user:read")
//擁有角色"user"中的"read"權限
void someMethod();
以前訪問數據資源時,需要先對訪問的url進行權限配置,在spring-shiro.xml中編寫大量的Java代碼,與最開始的Hibernate框架相同,需要編寫大量的hbm配置文件,顯得十分冗余,不利于后期維護,因而迫使開發者們逐漸選擇注解形式來進行程序的開發和授權等操作。本文采用基于Shiro框架的JSP/GSP標簽式授權和注解式授權來實現權限操作,配置非常簡潔方便,顯著減少了授權部分的代碼量,同時還方便后期的管理和維護。
以如下Shiro權限注解為例進行分析:
@RequiresPermissions("user:find","user:add",…)
void someMethod();
注解@RequiresPermissions要求當前登錄用戶必須同時具備user:find和user:add權限時,才能繼續執行注解下面的方法someMethod(),否則系統將會拋出異常AuthorizationException。
在Web系統中,該注解作用于Java方法上,表示對訪問這些方法的用戶進行訪問攔截,當判斷出用戶擁有該權限時才能繼續訪問,使得在用戶訪問后臺數據資源時,先判定其是否擁有訪問此方法的權限,對阻止越權訪問有一定的作用。但在設計Web系統時,可能在開發授權這塊考慮不周或者部分業務邏輯代碼遺漏權限判斷功能,導致用戶在未授權的情況下越權訪問這部分數據,甚至非法訪客通過一些黑客技術來非法訪問系統數據庫中的數據資源,泄露了客戶的個人隱私和一些敏感重要的商業信息,造成無法挽回的損失。
為避免越權訪問情況的發生,本文在Shiro提供的權限注解@RequiresPermissions基礎上,提出一個作用于Java類上的改進的權限注解。
2.3.1 實現方法
根據Shiro提供的作用于Java方法的權限注解@RequiresPermissions,本文提出一個改進的作用于Java類的權限注解@ClassPermissions,實現步驟為:
1)編寫一個ClassPermissions.java,在其中定義基于類的權限注解@ClassPermissions:
public @interface ClassPermissions {
String[] value();}
2)在Spring[15]配置文件spring-shiro.xml中添加:
3)繼承AuthorizingAnnotationHandler類,將構造函數中new的對象替換成自己的AOP實現。關鍵代碼如下:
public ClassAuthorizingAnnotationHandler() {
super(ClassPermissions.class);}
@Override
public void assertAuthorized(Annotation an) throws AuthorizationException {
ClassPermissions per = (ClassPermissions) an;
String[] permissions =per.value();
getSubject().checkPermissions(permissions);
return;} }
4)把spring所提供的AopAllianceAnnotations-AuthorizingMethodInterceptor重寫一個自己的,修改權限驗證攔截器棧的設置,修改權限驗證的攔截器。關鍵代碼如下:
interceptors.add(new ClassAnnotationMethod
Interceptor(resolver));
//自定義
interceptors.add(new ClassAuthorizingAnnotation
MethodInterceptor());
5)實現所需的權限驗證攔截器,根據spring所提供的類AuthorizingAnnotationMethodInterceptorl重寫一個自己的類。關鍵代碼如下:
publicClassAuthorizingAnnotationMethod-
Interceptor (){
super(new ClassAuthorizingAnnotationHandler());}
public ClassAuthorizingAnnotationMethod-
Interceptor(AnnotationResolver resolver){
super(new ClassAuthorizingAnnotationHandler(),
resolver);}
6)實現所需的權限處理器,繼承spring提供的類AuthorizationAttributeSourceAdvisor,實現作用于類的權限注解,獲取類上的注解以及類下所有方法的注解。關鍵代碼如下:
private static final Class extends Annotation>[ ] annotationClass=new Class[] {//注解權限
ClassPermissions.class,
RequiresPermissions.class,……};
//匹配帶有注解的方法
@Override
public boolean matches(Method m,Classc){
boolean flag = super.matches(m,c);
//若方法上沒有權限注解,則獲取類上權限注解
if(!flag && isAuthzAnnotationPresent(c) && isWebAnnota tionPresent(m)){
flag = true;}
return flag;}
//查看方法上是否有權限注解
private boolean isAuthzAnnotationPresent(Method m) { for(Class extends Annotation> ann:
annotationClass {
Annotation a=AnnotationUtils.findAnnotation(m,ann);
if (a !=null) {return true;}
}return false;}
2.3.2 類與方法之間的安全作用機制
對集成了Shiro框架的Web系統各個功能模塊的url訪問都需要角色權限。當用戶訪問時,后臺會根據前端的 url地址定位到所對應方法上的權限注解。spring會先掃描Shiro注解類的matches方法(上述第6個步驟),并通過返回true/false的方式來判斷某個方法是否帶有Shiro權限注解。若返回true,則再判斷當前訪問操作是否滿足方法上的注解權限;若返回false,表示方法上沒有權限注解,則會去獲取所屬類上的權限注解@ClassPermissions,再判斷當前訪問操作的權限。
結合上節中實現的作用于Java類上的權限注解@ClassPermissions,本文提出一個基于Shiro標簽式授權和注解式授權的權限訪問控制算法。
算法基于Shiro標簽和注解的訪問控制算法
輸入訪問者的請求request=
輸出訪問者得到的視圖
步驟1根據請求request獲得user對應的角色信息,并通過角色ID (useId)獲取Shiro中保存的對應的權限信息。
步驟2Shiro過濾器攔截所有的請求,對每個請求進行權限判斷。
步驟3若是前端請求,判斷相應方法上的JSP/GSP權限標簽。若擁有該權限,則跳轉至步驟7,否則跳轉至步驟8。
步驟4若是后臺請求,判斷相關Java方法上是否有權限注解。若有,則跳轉至步驟5,否則跳轉至步驟6。
步驟5若擁有權限注解@RequiresPermissions中標識的權限,則跳轉至步驟7,否則跳轉至步驟8。
步驟6獲取并判斷所屬Java類上的權限注解@ClassPermissions,若擁有注解中標識的權限,則跳轉至步驟7,否則跳轉至步驟8。
步驟7允許訪問,獲取數據庫中的匹配信息,然后跳轉至步驟9。
步驟8禁止訪問,拋出異常。
步驟9輸出經過權限過濾后的數據信息。
用戶登錄時發出請求request=
基于 Shiro的授權流程如下:首先系統會調用Subject.isPermitted("權限串")方法,然后委托給securityManager進行處理,通過securityManager內部的Authorizer(默認是實現ModularRealmAuthorizer)來進行真正的授權處理。ModularRealmAuthorizer會調用Realm的授權方法doGetAuthorizationInfo,從數據庫查詢權限數據,返回ModularRealmAuthorizer,ModularRealmAuthorizer會調用PermissionResolver進行權限串比對。若從Realm中獲取的當前Subject的角色信息(即權限串)與傳入的isPermitted("權限串")相匹配,則返回ture,有訪問權限;否則返回false,沒有訪問權限。
對于后臺的訪問請求,系統會先定位到具體的Java方法上,并查找方法上的權限注解,例如@RequiresPermissions("user:find")。若方法上存在權限注解,則按照上述Shiro的授權流程來進行授權處理;若方法上沒有權限注解,則會去獲取所在類上的權限注解@ClassPermissions("user:find")再進行授權處理,從而實現對后臺訪問請求的權限控制。
綜上所述,基于權限訪問控制的用戶授權流程如圖2所示。

圖2 基于權限訪問控制的用戶授權流程
3.3.1 安全性
一個集成了Shiro的Web系統,基本每個功能模塊的Controller層都有同樣的add、list等方法,這些方法不處理業務邏輯,只是把請求從Controller層轉到Service層處理完后,將結果再轉給相應的視圖。此時只需在Controller層編寫抽象類,并實現這些方法,再使用@RequestMapping標記此類。其他需要實現增刪改查功能的Controller都繼承此抽象類,每個類只需編寫自己的視圖地址即可。此時,需要注解@RequiresPermissions能實現對類的注解。但是,根據文獻[5]和Apache Shiro官網可知,該注解只能作用在方法上,導致其無法在Controller層的抽象類上實現權限驗證功能。
本文提出的權限注解@ClassPermissions可針對某個類進行注解,解決了上述技術難題。其相應的權限訪問控制算法與Apache Shiro官網提供的@RequiresPermissions的權限驗證過程相比,相當于增加了3.1節中步驟4和步驟6的判斷,避免了開發過程中某些方法上缺少權限驗證的問題,提高了系統的安全性。同時,不再需要對Controller層的所有方法進行注解,節省了大量的開發時間,使得授權更加靈活,便于管理和維護。
3.3.2 有效性
本文采用某公司的角色信息管理系統進行效果測試。該系統類似于文獻[13]中的系統,其采用了B/S體系結構,以Spring+SpringMVC+Hibernate開源框架設計開發,整合了Shiro框架,使用MySQL數據庫。
創建一個沒有任何操作權限的普通用戶,測試100個url:http://localhost:8080/spring/rest/…①…/…②…,其中:①表示系統的功能模塊名;②表示每個模塊中增刪改查等方法的方法名,觀察并記錄網頁的輸出結果;然后在各個功能模塊的Controller層上實現權限注解@ClassPermissions,再觀察輸出結果。實驗結果對比如表1所示。由對比數據可知,實現類上的注解后可有效地阻止越權訪問。

表1 訪問結果對比
3.3.3 普適性
只要開發的Web應用系統支持面向切面的編程,即可很容易地實現作用于Java類的權限注解@ClassPermissions,從而實現方法和類上的權限控制。因此,本文算法在實際開發應用中具有很好的普遍適用性。
針對Web系統中的越權訪問問題,本文提出一種基于Shiro安全框架的權限訪問控制算法,通過協同使用作用于Java方法和類上的權限注解,實現整個Web系統后臺的授權。與之前僅采用Shiro本身提供的權限注解來實現系統授權相比,該算法可有效阻止越權訪問的發生,提高系統的安全性,同時降低開發者工作的復雜度,使操作更加靈活方便。下一步工作將繼續運用Shiro和其他安全框架來解決Web系統中存在的各種安全問題。