Spring Security保護(hù)業(yè)務(wù)層

保護(hù)業(yè)務(wù)層

Spring Security支持添加授權(quán)層(或者基于授權(quán)的數(shù)據(jù)處理)到應(yīng)用中所有Spring管理的bean中。盡管很多的開(kāi)發(fā)人員關(guān)注層的安全蚓让,其實(shí)業(yè)務(wù)層的安全同等重要饭耳,因?yàn)閻阂獾挠脩艨赡軙?huì)穿透web層执解,能夠通過(guò)沒(méi)有UI的前端訪問(wèn)暴露的服務(wù),如使用webservice觅赊。

讓我們查看下面的圖以了解我們將要添加安全層的位置:

添加安全層的位置

Spring Security有兩個(gè)主要技術(shù)以實(shí)現(xiàn)方法的安全:

  1. 事先授權(quán)(Pre-authorization)保證在執(zhí)行一個(gè)方法之前需要滿足特定的要求——例如吮螺,一個(gè)用戶要擁有特定的GrantedAuthority鸠补,如ROLE_ADMIN紫岩。不能滿足聲明的條件將會(huì)導(dǎo)致方法調(diào)用失敗勋陪;
  2. 事后授權(quán)(Post-authorization)保證在方法返回時(shí)硫兰,調(diào)用的安全實(shí)體滿足聲明的條件呻粹。這很少被使用,但是能夠在一些復(fù)雜交互的業(yè)務(wù)方法周?chē)峁╊~外的安全層腮郊。

事先和事后授權(quán)在面向?qū)ο笤O(shè)計(jì)中提供了所謂的前置條件和后置條件(preconditions and ostconditions)衅鹿。前置條件和后置條件允許開(kāi)發(fā)者聲明運(yùn)行時(shí)的檢查大渤,從而保證在一個(gè)方法執(zhí)行時(shí)特定的條件需要滿足泵三。在安全的事前授權(quán)和事后授權(quán)中俺抽,業(yè)務(wù)層的開(kāi)發(fā)人員需要對(duì)特定的方法確定明確的安全信息磷斧,并在接口或類(lèi)的API聲明中添加期望的運(yùn)行時(shí)條件弛饭。正如你可能想象的那樣孩哑,這需要大量的規(guī)劃以避免不必要的影響。

保護(hù)業(yè)務(wù)層方法的基本知識(shí)

讓我們以JBCP Pets中業(yè)務(wù)層的幾個(gè)方法為例闡述怎樣為它們應(yīng)用典型的規(guī)則丛晌。

我們對(duì)JBCP Pets的基礎(chǔ)代碼進(jìn)行了重新組織以實(shí)現(xiàn)三層的設(shè)計(jì)澎蛛,作為修改的一部分我們抽象出了前面章節(jié)已經(jīng)介紹到的修改密碼功能到業(yè)務(wù)層谋逻。不同于用web MVC的控制器直接訪問(wèn)JDBC DAO毁兆,我們選擇插入一個(gè)業(yè)務(wù)服務(wù)以提供要求的附加功能。下圖對(duì)此進(jìn)行了描述:

應(yīng)用架構(gòu)的業(yè)務(wù)層.png

我們能夠看到在例子中com.packtpub.springsecurity.service.IuserService接口代表了應(yīng)用架構(gòu)的業(yè)務(wù)層,而這對(duì)我們來(lái)說(shuō)梅桩,是一個(gè)合適位置來(lái)添加方法級(jí)的安全宿百。

添加@PreAuthorize方法注解
我們第一個(gè)的設(shè)計(jì)決策就是要在業(yè)務(wù)層上添加方法安全犀呼,以保證用戶在修改密碼前已經(jīng)作為系統(tǒng)的合法用戶進(jìn)行了登錄坐儿。這通過(guò)為業(yè)務(wù)接口方法定義添加一個(gè)簡(jiǎn)單的注解來(lái)實(shí)現(xiàn)貌矿,如下:
public interface IUserService { @PreAuthorize("hasRole('ROLE_USER')") public void changePassword(String username, String password); }
這就是保證合法、已認(rèn)證的用戶才能訪問(wèn)修改密碼功能所要做的所有事情罪佳。Spring Security將會(huì)使用運(yùn)行時(shí)的面向方面編程的切點(diǎn)(aspect oriented programming (AOP) pointcut)來(lái)對(duì)方法執(zhí)行before advice逛漫,并在安全要求未滿足的情況下拋出AccessDeniedException異常。

讓Spring Security能夠使用方法注解,我們還需要在dogstore-security.xml中做一個(gè)一次性的修改赘艳,通過(guò)這個(gè)文件我們已經(jīng)進(jìn)行了Spring Security其他的配置酌毡。只需要在<http>聲明之前,添加下面的元素即可:
<global-method-security pre-post-annotations="enabled"/>
校驗(yàn)方法安全
不相信如此簡(jiǎn)單蕾管?那我們將ROLE_USER聲明修改為ROLE_ADMIN〖咸ぃ現(xiàn)在用用戶guest(密碼guest)登錄并嘗試修改密碼。你會(huì)在嘗試修改密碼時(shí)掰曾,看到如下的出錯(cuò)界面:
如果查看Tomcat的控制臺(tái)掏熬,你可以看到很長(zhǎng)的堆棧信息岗屏,開(kāi)始是這樣的:
DEBUG - Could not complete request o.s.s.access.AccessDeniedException: Access is denied at o.s.s.access.vote.AffirmativeBased.decide at o.s.s.access.intercept.AbstractSecurityInterceptor.beforeInvocation ... at $Proxy12.changePassword(Unknown Source) at com.packtpub.springsecurity.web.controller.AccountController. submitChangePasswordPage
基于訪問(wèn)拒絕的頁(yè)面以及指向changePassword方法的堆棧信息似袁,我們可以看到用戶被合理的拒絕對(duì)業(yè)務(wù)方法的訪問(wèn),因?yàn)槿鄙賀OLE_ADMIN的GrantedAuthority联予。你可以測(cè)試修改密碼功能對(duì)管理員用戶依舊是可以訪問(wèn)的。
我們只是在接口上添加了簡(jiǎn)單的聲明就能夠保證方法的安全,這是不是太令人興奮了?
讓我們介紹一下實(shí)現(xiàn)方法安全的其它方式吓著,然后進(jìn)入功能的背后以了解其怎樣以及為什么能夠生效纺裁。

幾種實(shí)現(xiàn)方法安全的方式

除了@PreAuthorize注解以外丧鸯,還有幾種其它的方式來(lái)聲明在方法調(diào)用前進(jìn)行授權(quán)檢查的需求。我們會(huì)講解這些實(shí)現(xiàn)方法安全的不同方式,并比較它們?cè)诓煌h(huán)境下的優(yōu)勢(shì)與不足篮愉。

遵守JSR-250標(biāo)準(zhǔn)規(guī)則

JSR-250, Common Annotations for the Java Platform定義了一系列的注解泣刹,其中的一些是安全相關(guān)的,它們意圖在兼容JSR-250的環(huán)境中很方便地使用。Spring框架從Spring 2.x釋放版本開(kāi)始就兼容JSR-250韭邓,包括Spring Security框架诗力。
盡管JSR-250注解不像Spring原生的注解富有表現(xiàn)力纳鼎,但是它們提供的注解能夠兼容不同的Java EE應(yīng)用服務(wù)器實(shí)現(xiàn)如Glassfish映九,或面向服務(wù)的運(yùn)行框架如Apache Tuscany倦逐。取決于你應(yīng)用對(duì)輕便性的需求您单,你可能會(huì)覺(jué)得犧牲代碼的輕便性但減少對(duì)特定環(huán)境的要求是值得的凤优。

要實(shí)現(xiàn)我們?cè)诘谝粋€(gè)例子中的規(guī)則棍辕,我們需要作兩個(gè)修改:

  1. 首先在dogstore-security.xml文件中:
    <global-method-security jsr250-annotations="enabled"/>

  2. 其次,@PreAuthorize注解需要修改成@RolesAllowed注解拍顷。
    正如我們可能推斷出的那樣匾乓,@RolesAllowed注解并不支持SpEL表達(dá)式,所以它看起來(lái)很像我們?cè)诘诙?jié)中提到的URL授權(quán)芒帕。我們修改IuserService定義如下:
    @RolesAllowed("ROLE_USER") public void changePassword(String username, String password);
    正如前面的練習(xí)那樣垛叨,如果不相信它能工作伦糯,嘗試修改ROLE_USER 為ROLE_ADMIN并進(jìn)行測(cè)試。
    要注意的是,也可以提供一系列允許的GrantedAuthority名字敛纲,使用Java 5標(biāo)準(zhǔn)的字符串?dāng)?shù)組注解語(yǔ)法:
    @RolesAllowed({"ROLE_USER","ROLE_ADMIN"}) public void changePassword(String username, String password);

  3. JSR-250還有兩個(gè)其它的注解:@PermitAll 和@DenyAll喂击。它們的功能正如你所預(yù)想的,允許和禁止對(duì)方法的任何請(qǐng)求载慈。

【類(lèi)層次的注解惭等。注意方法級(jí)別的安全注解也可以使用到類(lèi)級(jí)別上珍手!如果提供了方法級(jí)別的注解办铡,將會(huì)覆蓋類(lèi)級(jí)別的注解。如果業(yè)務(wù)需要在整個(gè)類(lèi)上有安全策略的話琳要,這會(huì)非常有用寡具。要注意的是使用這個(gè)功能要有良好的注釋的編碼規(guī)范,這樣開(kāi)發(fā)人員能夠很清楚的了解類(lèi)和方法的安全特性稚补⊥】

@Secured注解實(shí)現(xiàn)方法安全

Spring本身也提供一個(gè)簡(jiǎn)單的注解,類(lèi)似于JSR-250 的@RolesAllowed注解课幕。

@Secured注解在功能和語(yǔ)法上都與@RolesAllowed一致厦坛。唯一需要注意的不同點(diǎn)是要使用這些注解的話,要在<global-method-security>元素中明確使用另外一個(gè)屬性:
<global-method-security secured-annotations="enabled"/>
因?yàn)锧Secured與JSR標(biāo)準(zhǔn)的@RolesAllowed注解在功能上一致乍惊,所以并沒(méi)有充分的理由在新代碼中使用它杜秸,但是它能夠在Spring的遺留代碼中運(yùn)行。

使用Aspect Oriented Programming (AOP)實(shí)現(xiàn)方法安全

實(shí)現(xiàn)方法安全的最后一項(xiàng)技術(shù)也可能是最強(qiáng)大的方法润绎,它還有一個(gè)好處是不需要修改源代碼撬碟。作為替代,它使用面向方面的編程方式為一個(gè)方法或方法集合聲明切點(diǎn)(pointcut)莉撇,而增強(qiáng)(advice)會(huì)在切點(diǎn)匹配的情況下進(jìn)行基于角色的安全檢查呢蛤。AOP的聲明只在Spring Security的XML配置文件中并不涉及任何的注解。

以下就是聲明保護(hù)所有的service接口只有管理權(quán)限才能訪問(wèn)的例子:
<global-method-security> <protect-pointcut access="ROLE_ADMIN" expression="execution(* com.packtpub.springsecurity.service.I*Service.*(..))"/> </global-method-security>
切點(diǎn)表達(dá)式基于Spring AOP對(duì)AspectJ的支持棍郎。但是其障,Spring AspectJ AOP僅支持AspectJ切點(diǎn)表達(dá)式語(yǔ)言的一個(gè)很小子集——可以參考Spring AOP的文檔以了解其支持的表達(dá)式和其它關(guān)于Spring AOP編程的重要元素。
注意的是涂佃,可以指明一系列的切點(diǎn)聲明励翼,以指向不同的角色和切點(diǎn)目標(biāo)。以下的就是添加切點(diǎn)到DAO中一個(gè)方法的例子:
<global-method-security> <protect-pointcut access="ROLE_USER" expression="execution(* com.packtpub.springsecurity.dao.IProductDao.getCategories(..)) && args()"/> <protect-pointcut access="ROLE_ADMIN" expression="execution(* com.packtpub.springsecurity.service.I*Service.*(..))"/> </global-method-security>
意在新增的切點(diǎn)中巡李,我們添加了一些AspectJ的高級(jí)語(yǔ)法抚笔,來(lái)聲明Boolean邏輯以及其它支持的切點(diǎn),而參數(shù)可以用來(lái)確定參數(shù)的類(lèi)型聲明侨拦。
同Spring Security其它允許一系列安全聲明的地方一樣殊橙,AOP風(fēng)格的方法安全是按照從頂?shù)降椎捻樞蜻M(jìn)行的,所以需要按照最特殊到最不特殊的順序來(lái)寫(xiě)切點(diǎn)。
使用AOP來(lái)進(jìn)行編程即便是經(jīng)驗(yàn)豐富的開(kāi)發(fā)人員可能也會(huì)感到迷惑膨蛮。如果你確定要使用AOP來(lái)進(jìn)行安全聲明叠纹,除了Spring AOP的參考手冊(cè)外,強(qiáng)烈建議你參考一些這個(gè)專(zhuān)題相關(guān)的書(shū)籍敞葛。AOP實(shí)現(xiàn)起來(lái)比較復(fù)雜誉察,尤其是在解決不按照你預(yù)期運(yùn)行的配置錯(cuò)誤時(shí)更是如此。
比較方法授權(quán)的類(lèi)型
以下的快速參考表可能在你選擇授權(quán)方法檢查時(shí)派上用場(chǎng):

大多數(shù)使用Java 5的Spring Security用戶傾向于使用JSR-250注解惹谐,以達(dá)到在IT組織間最大的兼容性和對(duì)業(yè)務(wù)類(lèi)(以及相關(guān)約束)的重用持偏。在需要的地方,這些基本的聲明能夠被Spring Security本身實(shí)現(xiàn)的注解所代替氨肌。
如果你在不支持注解的環(huán)境中(Java 1.4或更早版本)中使用Spring Security鸿秆,很不幸的是,關(guān)于方法安全的執(zhí)行你的選擇可能會(huì)很有限怎囚。即使在這樣的情況下卿叽,對(duì)AOP的使用也提供了相當(dāng)豐富的環(huán)境來(lái)開(kāi)發(fā)基本的安全聲明。

方法的安全保護(hù)是怎樣運(yùn)行的恳守?
方法安全的訪問(wèn)決定機(jī)制——一個(gè)給定的請(qǐng)求是否被允許——在概念上與web請(qǐng)求的訪問(wèn)決定邏輯是相同的考婴。AccessDecisionManager使用一個(gè)AccessDecisionVoters集合,其中每一個(gè)都要對(duì)能否進(jìn)行訪問(wèn)做出允許催烘、拒絕或者棄權(quán)的的投票沥阱。AccessDecisionManager匯集這些投票器的結(jié)果并形成一個(gè)最終能否允許處罰方法的決定。
Web請(qǐng)求的訪問(wèn)決策沒(méi)有這么復(fù)雜颗圣,這是因?yàn)橥ㄟ^(guò)ServletFilters對(duì)安全請(qǐng)求做攔截(以及請(qǐng)求拒絕)都相對(duì)很直接喳钟。因?yàn)榉椒ǖ挠|發(fā)可能發(fā)生在任何的地方,包括沒(méi)有通過(guò)Spring Security直接配置的代碼在岂,Spring Security的設(shè)計(jì)者于是選擇Spring管理的AOP方式來(lái)識(shí)別奔则、評(píng)估以及保護(hù)方法的觸發(fā)。
下圖在總體上展現(xiàn)了方法觸發(fā)授權(quán)決策的主要參與者:

方法觸發(fā)授權(quán)決策的主要參與者.png

我們 能夠看到Spring Security的.s.s.access.intercept.aopalliance.MethodSecurityInterceptor被標(biāo)準(zhǔn)的Spring AOP運(yùn)行時(shí)觸發(fā)以攔截感興趣的方法調(diào)用蔽午。通過(guò)上面的流程圖易茬,是否允許方法調(diào)用的邏輯就相對(duì)很清晰了。
此時(shí)及老,我們可能會(huì)比較關(guān)心方法安全功能的性能抽莱。顯然,MethodSecurityInterceptor不能在應(yīng)用中每個(gè)方法調(diào)用的時(shí)候觸發(fā)——那方法或類(lèi)上的注解是如何做到AOP攔截的呢骄恶?
首先食铐,AOP織入默認(rèn)不會(huì)對(duì)所有Spring管理的bean觸發(fā)。相反僧鲁,如果<global-method-security>在Spring Security配置中定義虐呻,一個(gè)標(biāo)準(zhǔn)的Spring AOP o.s.beans.factory.config.BeanPostProcessor將會(huì)被注冊(cè)象泵,它將會(huì)探查AOP配置是否有AOP增強(qiáng)器(advisors)需要織入(以及攔截)。這個(gè)工作流是Spring標(biāo)準(zhǔn)的AOP處理(名為AOP自動(dòng)織入)斟叼,并不是Spring Security所特有的偶惠。所有的BeanPostProcessors在spring ApplicationContext初始化時(shí)執(zhí)行,在所有的Spring Bean配置生效后朗涩。
Spring的AOP自動(dòng)織入功能查詢所有注冊(cè)的PointcutAdvisors忽孽,查看是否有AOP切點(diǎn)匹配方法的調(diào)用并使用AOP增強(qiáng)(advice)。Spring Security實(shí)現(xiàn)了o.s.s.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor類(lèi)谢床,它會(huì)檢查所有配置的方法安全病建立適當(dāng)?shù)腁OP攔截兄一。注意的是,只有聲明了方法安全的接口和類(lèi)才會(huì)被AOP代理萤悴。

【強(qiáng)烈建議在接口上聲明AOP規(guī)則(以及其它的安全注解)瘾腰,而不是在實(shí)現(xiàn)類(lèi)上。使用類(lèi)(通過(guò)Spring的CGLIB代理)進(jìn)行聲明可能會(huì)導(dǎo)致應(yīng)用出現(xiàn)不可預(yù)知的行為改變覆履,通常在正確性方面比不上在接口定義安全聲明(通過(guò)AOP)》驯。】

MethodSecurityMetadataSourceAdvisor將AOP影響方法行為的決定委托給.s.s.access.method.MethodSecurityMetadataSource的實(shí)例硝全。不同的方法安全注解都擁有自己的MethodSecurityMetadataSource,它將用來(lái)檢查每個(gè)方法和類(lèi)并添加在運(yùn)行時(shí)執(zhí)行的增強(qiáng)(advice)楞抡。
以下的圖展現(xiàn)了這個(gè)過(guò)程是如何發(fā)生的:

AOP影響方法行為的決定委托.png

取決于你的應(yīng)用中配置的Sprin Bean的數(shù)量伟众,以及擁有的安全方法注解的數(shù)量,添加方法安全代理將會(huì)增加初始化ApplicationContext的時(shí)間召廷。但是凳厢,一旦上下文初始化完成,對(duì)單個(gè)的代理bean來(lái)說(shuō)性能的影響可以忽略不計(jì)了竞慢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末先紫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子筹煮,更是在濱河造成了極大的恐慌遮精,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件败潦,死亡現(xiàn)場(chǎng)離奇詭異本冲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)劫扒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)檬洞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沟饥,你說(shuō)我怎么就攤上這事添怔』犯辏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵澎灸,是天一觀的道長(zhǎng)院塞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)性昭,這世上最難降的妖魔是什么拦止? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮糜颠,結(jié)果婚禮上汹族,老公的妹妹穿的比我還像新娘。我一直安慰自己其兴,他們只是感情好顶瞒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著元旬,像睡著了一般榴徐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匀归,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天坑资,我揣著相機(jī)與錄音,去河邊找鬼穆端。 笑死袱贮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的体啰。 我是一名探鬼主播攒巍,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼荒勇!你這毒婦竟也來(lái)了柒莉?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤枕屉,失蹤者是張志新(化名)和其女友劉穎常柄,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體搀擂,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡科汗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年桌粉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了估盘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跌宛。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖威恼,靈堂內(nèi)的尸體忽然破棺而出品姓,到底是詐尸還是另有隱情寝并,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布腹备,位于F島的核電站衬潦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏植酥。R本人自食惡果不足惜镀岛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望友驮。 院中可真熱鬧漂羊,春花似錦、人聲如沸卸留。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)耻瑟。三九已至旨指,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匆赃,已是汗流浹背淤毛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留算柳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓姓言,卻偏偏與公主長(zhǎng)得像瞬项,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子何荚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容