天天看點

Spring Security實作動态權限管理

我所了解的動态權限就是RBAC(Role-Based Access Control)。

就是可以自定義角色,配置角色可以通路哪些URL。然後給不同的角色設定不同的角色。

為什麼用Spring Security?聽說Spring Security是基于Shiro的。Shiro沒用過。之是以用Spring Security是因為它安全。廢話!是因為可以幫你防禦csrf等攻擊。其實作在的Chrome浏覽器的同源政策已經很不錯了,想csrf也沒那麼容易。Spring Security能防住多少我也不知道,總之比什麼都不做好。XSS的話大家做好過濾就好了。比如Spring MVC就可以過濾掉一些,頁面用<code>&lt;c:out/&gt;</code> 标簽輸出。跑題了啊,其實我也就是想裝個X而已。

一直想用Spring Security實作動态權限管理,這回搞定了,總算了結了一個心願。

隻想啰嗦一句。之前Spring MVC的配置檔案在getServletConfigClasses這個方法裡,現在要挪到getRootConfigClasses方法裡。至于為什麼,文檔裡說了,下面的注釋裡也有,注釋都來自官方文檔。

其實很簡單。自定義一個類。在類裡加一個方法:

然後在要使用的地方加一個<code>@PreAuthorize("@mySecurityService.hasPermission(authentication, #model)")</code> 就OK了。

注意這裡的authentication.getAuthorities();别小看這個,這可是大有來頭!

Spring Security總共分為兩大塊:anthentication和authorization。

剛開始學Spring Security的時候這兩個地方你都會卡住!為什麼?

Spring Security有一個預設的anthentication。它會幫你生成一個登入用的form表單。蛋疼的是這個form表單隻有username和password兩個參數。問題來了,我想加個驗證碼怎麼辦? 不隻這樣,預設的authentication會自動幫你注入寫死的角色。就是你在配置檔案裡配置的ROLE_USER,ROLE_ADMIN等等。我想要自定義角色,我想要動态角色怎麼辦?卡住了不是?

如果你使用預設的authentication那麼此時authentication.getAuthorities();拿到的角色就是固定的(你在配置檔案裡配置的)。

authorization你會卡在什麼地方?文檔裡舉的例子是<code>@PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')")</code> 看到了吧,這裡的ROLE就是固定的,而不是動态的。

是以你需要的是@PreAuthorize(“canIAccess(authorities)”)。即自定義一個decide方法,并且把動态角色傳給這個decide方法!

怎麼做?

登入(authenticate)的時候想帶一個驗證碼,我們需要自定義一個form表單。 想要動态角色就要在登入成功的時候把使用者的角色資訊從db中查出來,把它注入到Spring的某個類中,具體哪個類我忘了,文檔裡有提到,你得仔細看文檔“Architecture and Implementation”這一節。 然後在decide方法裡拿到之前注入的角色資訊。

說起來容易,做起來可沒那麼容易!

比如說你的驗證碼參數,在認證(authenticate)的時候如何擷取?你可能會說,先把captcha存到session裡。隻要在認證的時候拿到request對象不就行了嗎?關鍵是你拿的到嗎?!

想在認證的時候拿到request太不容易了:

首先得在BeanPostProcessor裡注冊我們自定義的認證源:MyWebAuthenticationDetailsSource

在MyWebAuthenticationDetailsSource的buildDetails方法裡傳回一個自定義的詳細認證類MyWebAuthenticationDetails

在詳細認證類裡讓Spring自動幫我們注入request對象:

最後在我們自定義的認證裡拿到MyWebAuthenticationDetails,進而拿到request

注意這個CustomUserService是我們自定義的UserDetailsService。就是通過這個類把使用者資訊從db裡查出來。為了友善我就直接在service裡寫死了,懶得定義dao,從db裡查了。

CustomRole也是我們自定義的ROLE。這樣你可以在自定義的ROLE加其他字段,這裡我隻定義一個了name字段:

自定義認證就在CustomAuthenticationProvider的authenticate方法裡完成。前面做了那麼多工作就為了區區一個request。這裡有一句maimapi不知當講不當講。

在authenticate方法的最後return new UsernamePasswordAuthenticationToken(user, password, authorities); 就把authorities注入到Spring的某個類裡了。 是以在MySecurityService的hasPermission方法裡我們可以拿到authentication,進而拿到authorities:

曲折啊,你說這句maimapi當講不當講?