您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何使用@Secured注解限制方法調(diào)用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
在Spring Securoty實(shí)現(xiàn)方法級(jí)別的安全性最常見(jiàn)的方法是使用特定的注解.將這些注解應(yīng)用到需要保護(hù)的方法上.
Spring Security 提供了三種不同方式的安全注解.
Spring 自帶的 @Security 注解.
JSR-250 的 @RolesAllow 注解.
表達(dá)式驅(qū)動(dòng)的注解: @PreAythorize , @PostAuthorize ,@PreFilter , @PostFilter .
@Secured和@RolesAllowed方案非常類似,能夠基于用戶所授予的權(quán)限限制對(duì)方法的訪問(wèn)。
當(dāng)我們需要在方法上定義更靈活的安全規(guī)則時(shí),Spring Security提供了@PreAuthorize和@PostAuthorize,
而@PreFilter/@PostFilter能夠過(guò)濾方法返回的以及傳入方法的集合。
下面就開(kāi)始介紹以上注解具體的使用方法.
為了降低初學(xué)者的學(xué)識(shí)成本,我們使用最簡(jiǎn)單的使用內(nèi)存的用戶存儲(chǔ)來(lái)演示.
/** * @author itguang * @create @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{ //基于內(nèi)存的用戶存儲(chǔ) @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("itguang").password("123456").roles("USER").and() .withUser("admin").password("123456").roles("ADMIN"); } }
在Spring中如果要啟用基于注解的方法安全性,需要在配置類上使用@EnableGlobalMethodSecurity,如下所示:
/** * @author itguang * @create @Configuration @EnableGlobalMethodSecurity(securedEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration
除了@EnableGlobalMethodSecuroty注解外,我們注意到配置類還擴(kuò)展了 GlobalMethodSecurotyConfiguration .
在前面的文章中我們介紹過(guò) web安全的配置類擴(kuò)展了 WebSecurityConfiguration ,與此類似,這個(gè)類能勾為方法級(jí)別的安全性提供精細(xì)的配置.
我們還可以看到 @EnableGlobalMethodSecuroty注解 的參數(shù) securedEnabled 設(shè)置為了了true,這樣的話Spring Security就會(huì)包裝帶有
@Secured 注解的方法,例如:
@RequestMapping("/admin") @Secured("ROLE_ADMIN") public String admin(){ return "admin"; }
由于方法上添加了 @Secured(“ROLE_ADMIN”) 注解,當(dāng)我們?yōu)g覽器訪問(wèn) /admin 這個(gè)路徑時(shí),Spring就會(huì)執(zhí)行 admin()方法,Spring Security的切面
就會(huì)判斷當(dāng)前用戶是否有 ROLE_ADMIN
的權(quán)限.
看到這里你可能會(huì)有疑惑,為什么是 ROLE_ADMIN
呢,我們的SecurityConfigure 配置類中命名設(shè)置的是 ADMIN
啊. .withUser("admin").password("123456").roles("ADMIN");
,其實(shí)這是Spring Security 自動(dòng)為我們添加了 ROLE_
前綴,
如果我們要寫成 .withUser("admin").password("123456").roles("ADMIN");
的話,Spring Security就會(huì)報(bào)錯(cuò),
java.lang.IllegalArgumentException: ROLE_ADMIN cannot start with ROLE_ (it is automatically added)
可以看到Spring Security 提醒我們這是自動(dòng)添加的前綴,因此我們么有必要再添加 ROLE_
前綴.
但是,如果我們要為基于方法的攔截添加 @Secured 注解時(shí),就必須添加 ROLE_
的前綴.比如: @Secured("ROLE_ADMIN")
.
這里只給出主要的代碼,詳細(xì)代碼請(qǐng)參考源碼,本文最后會(huì)給出.
按照上面的配置完畢,我們啟動(dòng)項(xiàng)目,訪問(wèn) http://localhost/admin ,會(huì)提示讓我們登陸,我們首先輸入一個(gè)沒(méi)有 ROLE_ADMIN
權(quán)限的用戶,
用戶名:itguang,密碼:123456. 點(diǎn)擊登陸,會(huì)發(fā)現(xiàn)瀏覽器返回下面的頁(yè)面:
http狀態(tài)碼為 403,很明顯就是無(wú)權(quán)限訪問(wèn).接下來(lái)我們?cè)谟?admin 用戶登陸,機(jī)會(huì)發(fā)現(xiàn)瀏覽器正確返回了 admin
字符串.
另外,@Secured會(huì)使用一個(gè)String數(shù)組作為參數(shù),每個(gè)String值就是一個(gè)權(quán)限,調(diào)用這個(gè)方法的用戶至少要具備其中一個(gè)權(quán)限.如下面示例:
@RequestMapping("/hello") @Secured({"ROLE_ADMIN","ROLE_USER"}) public String hello() { return "hello Spring Security"; }
itguang 和 admin 這兩個(gè)用戶都可以訪問(wèn).如果方法被沒(méi)有被認(rèn)證的用戶或者沒(méi)有相應(yīng)權(quán)限的用戶訪問(wèn),就會(huì)拋出一個(gè)Spring Security 異常.
(可能是AuthenticationException或AccessDeniedException的子類),他們是非檢查時(shí)異常,這個(gè)一場(chǎng)病最終必須被捕獲或者處理.
如果被保護(hù)的方法是在web中被調(diào)用的,這個(gè)異常會(huì)被Spring Security 的過(guò)濾器自動(dòng)處理.否則的話,你需要編寫代碼來(lái)處理這個(gè)異常.
@RolesAllowed注解和@Secured注解在各個(gè)方面基本上都是一致的。唯一顯著的區(qū)別在于@RolesAllowed是JSR-250定義的Java標(biāo)準(zhǔn)注解.
如果選擇使用@RolesAllowed的話,需要將@EnableGlobalMethodSecurity的jsr250Enabled屬性設(shè)置為true,以開(kāi)啟此功能.
/** * @author itguang * @create @Configuration @EnableGlobalMethodSecurity(securedEnabled = true,jsr250Enabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration //基于內(nèi)存的用戶存儲(chǔ) @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("itguang").password("123456").roles("USER").and() .withUser("admin").password("123456").roles("ADMIN"); } }
盡管我們這里只是啟用了jsr250Enabled,但需要說(shuō)明的一點(diǎn)是這與securedEnabled并不沖突。這兩種注解風(fēng)格可以同時(shí)啟用.
如下:
@RequestMapping("/test1") @RolesAllowed("ROLE_ADMIN") public String test1(){ return "test1"; }
這兩個(gè)注解都有一個(gè)不足之處,它們只能根據(jù)用戶有沒(méi)有授予特定的權(quán)限來(lái)限制方法的調(diào)用,在判斷方式是否執(zhí)行方面,無(wú)法使用其他的因素.
接下來(lái),我們看一下如何組合使用SpEL與Spring Security所提供的方法調(diào)用前后注解,實(shí)現(xiàn)基于表達(dá)式的方法安全性.
Spring Security 3.0引入了幾個(gè)新注解,它們使用SpEL能夠在方法調(diào)用上實(shí)現(xiàn)更有意思的安全性約束。
@PreAuthorize :在方法調(diào)用之前,基于表達(dá)式的計(jì)算結(jié)果來(lái)限制對(duì)方法的訪問(wèn)
@PostAuthorize 允許方法調(diào)用,但是如果表達(dá)式計(jì)算結(jié)果為false,將拋出一個(gè)安全性異常
@PostFilter 允許方法調(diào)用,但必須按照表達(dá)式來(lái)過(guò)濾方法的結(jié)果
@PreFilter 允許方法調(diào)用,但必須在進(jìn)入方法之前過(guò)濾輸入值
這些注解的參數(shù)都可接受一個(gè)SPEL 表達(dá)式.表達(dá)式可以是任意合法的SPEL表達(dá)式.
如果表達(dá)式的計(jì)算結(jié)果為true,那么安全規(guī)則通過(guò),否則就會(huì)失敗。安全規(guī)則通過(guò)或失敗的結(jié)果會(huì)因?yàn)樗褂米⒔獾牟町惗兴煌?/p>
我們需要將@EnableGlobalMethod-Security注解的 prePostEnabled 屬性設(shè)置為true,從而啟用它們:
/** * @author itguang * @create @Configuration @EnableGlobalMethodSecurity(securedEnabled = true,jsr250Enabled = true,prePostEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration //基于內(nèi)存的用戶存儲(chǔ) @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("itguang").password("123456").roles("USER").and() .withUser("admin").password("123456").roles("ADMIN"); } }
現(xiàn)在方法調(diào)用前后的注解都已經(jīng)啟用了.
@PreAuthorize和@PostAuthorize,它們能夠基于表達(dá)式的計(jì)算結(jié)果來(lái)限制方法的訪問(wèn)。
在定義安全限制方面,表達(dá)式帶了極大的靈活性。通過(guò)使用表達(dá)式,只要我們能夠想象得到,就可以定義任意允許訪問(wèn)或不允許訪問(wèn)方法的條件。
@PreAuthorize和@PostAuthorize之間的關(guān)鍵區(qū)別在于表達(dá)式執(zhí)行的時(shí)機(jī)。
@PreAuthorize的表達(dá)式會(huì)在方法調(diào)用之前執(zhí)行,如果表達(dá)式的計(jì)算結(jié)果不為true的話,將會(huì)阻止方法執(zhí)行。
與之相反,@PostAuthorize的表達(dá)式直到方法返回才會(huì)執(zhí)行,然后決定是否拋出安全性的異常。
在方法調(diào)用之前驗(yàn)證權(quán)限,看下面的例子:
/** * @author itguang * @create @RestController public class UserController @RequestMapping("/addUser") @PreAuthorize("hasRole('ROLE_USER') and #userEntity.password>8 or hasRole('ROLE_ADMIN')") public String addUser(UserEntity userEntity){ return "addUser ok"; } }
這段代碼是什么意思呢? 首先我們讓訪問(wèn) /addUser 的用戶 必須是擁有 ROLE_USER 的用戶,并且密碼長(zhǎng)度大于8,或者擁有 ROLE_ADMIN 權(quán)限的.
這如果如使用@Secured或者RoleAllowd 是實(shí)現(xiàn)不了的.而使用 @PreAuthorized 恰好能適用這些場(chǎng)景.
表達(dá)式中 #userEntity 直接使用了方法中的同名參數(shù),這使得Spring Security 能夠檢查傳入方法的參數(shù).并將這些參數(shù)用于認(rèn)證決策的指定.
在方法調(diào)用之后驗(yàn)證權(quán)限,看下面的例子:
@RequestMapping("/getUser/{username}") @PostAuthorize("returnObject.username == principal.username") public UserEntity getUser(@PathVariable(value = "username") String username) { //模擬從數(shù)據(jù)庫(kù)中查找 UserEntity userEntity = new UserEntity(username); return
啟動(dòng)項(xiàng)目,我們首先訪問(wèn):http://localhost/getUser/itguang,會(huì)讓我們登陸,我們用itguang 用戶登陸,發(fā)現(xiàn)能夠正確返回,
然后我們?cè)诖嘶A(chǔ)上再訪問(wèn) http://localhost/getUser/admin ,就會(huì)返回http狀態(tài)碼403,禁止訪問(wèn).說(shuō)明配置生效.
下面我們來(lái)解釋一下上面的代碼,為了方便的訪問(wèn)受保護(hù)方法的返回對(duì)象,Spring Security 在SPEL中提供了名為 returnObject的返回變量.
在這里我們知道返回對(duì)象是一個(gè)UserEntity,所以可以直接 returnObject.username 取得里面的參數(shù).
principal 是另一個(gè)Spring Security 內(nèi)置的特殊變量,它代表了當(dāng)前認(rèn)證用戶的主要信息,通常是用戶名和權(quán)限列表.
如果我們希望使用表達(dá)式來(lái)保護(hù)方法的話,那使用@PreAuthorize和@PostAuthorize是非常好的方案。但是,有時(shí)候限制方法調(diào)用太嚴(yán)格了。
有時(shí),需要保護(hù)的并不是對(duì)方法的調(diào)用,需要保護(hù)的是傳入方法的數(shù)據(jù)和方法返回的數(shù)據(jù).
事后對(duì)方法的返回值進(jìn)行過(guò)濾,如下:
@RequestMapping("getAll") @PreAuthorize("hasRole('ROLE_USER')") @PostFilter("filterObject.enabled == true") public List<UserEntity> getAllUser(){ ArrayList<UserEntity> list = new ArrayList<>(); list.add(new UserEntity("test1","123456",true)); list.add(new UserEntity("test1","123456",false)); return
我們使用了 @PreAuthorize("hasRole('ROLE_USER')") @PostFilter("filterObject.enabled == true")
這兩個(gè)注解,
表明我們希望,用戶必須擁有 ROLE_USER
權(quán)限,并且返回用戶屬性 enabled為true的所有用戶.
表達(dá)式中的 filterObject 引用的是方法返回值List中的某一個(gè)元素,在這里是 UserEntity,并且過(guò)濾出 enabled為true的UserEntity,所以,
我們?yōu)g覽器訪問(wèn) http://localhost/getAll,并用itguang用戶登錄后,返回的只有一條用戶的信息.
如果你覺(jué)得自己的安全表達(dá)式難以控制了,那么就應(yīng)該看一下如何編
寫自定義的許可計(jì)算器(permission eval(232, 232, 232); background: rgb(249, 249, 249);">
@RequestMapping("/delete") @PreAuthorize("ROLE_USER") @PreFilter("hasPermission(targetObject,'delete')") public String getAllUser(List<UserEntity> list){ //從數(shù)據(jù)庫(kù)中刪除數(shù)據(jù) //... return "ok"; }
上面代碼實(shí)際上是在問(wèn)一個(gè)問(wèn)題,”當(dāng)前用戶有權(quán)限刪除目標(biāo)對(duì)象嗎?” 如果有的話,表達(dá)式計(jì)算為true,該對(duì)象會(huì)被刪除,
但是,hasPermission()是哪來(lái)的呢?它的意思是什么?更為重要
的是,它如何知道用戶有沒(méi)有權(quán)限刪除targetObject所對(duì)應(yīng)的
User?
hasPermission()函數(shù)是Spring Security為SpEL提供的擴(kuò)展,它為開(kāi)發(fā)者提供了一個(gè)時(shí)機(jī),能夠在執(zhí)行計(jì)算的時(shí)候插入任意的邏輯。
我們所需要做的就是編寫并注冊(cè)一個(gè)自定義的許可計(jì)算器.
“如何使用@Secured注解限制方法調(diào)用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。