Spring Secutity和Apache Shiro是Java領域的兩大主流開源安全框架,也是權限系統設計的主要技術選型。本文主要介紹Spring Secutity的實現原理,並基於Spring Secutity設計基於RBAC的權限系統。html
爲什麼把Spring Secutity做爲權限系統的技術選型,主要考慮瞭如下幾個方面:java
Spring Security | Apache Shiro | |
---|---|---|
認證 | 支持多種認證方式(如密碼、匿名、預認證) | 簡單登陸認證 |
鑑權 | 功能鑑權、數據鑑權 | 功能鑑權 |
多源適配 | Mem、JDBC、DAO、LDAP、 OpenID、OAuth等 |
LDAP、JDBC、Kerberos、 ActiveDirectory等 |
加密 | 支持多種加密方式 | 簡單加密方式 |
運行環境 | 依賴Spring | 可獨立運行 |
開放性 | 開源、Spring生態基礎 | 開源 |
複雜度 | 複雜、較重 | 簡單、靈活 |
權限系統通常包含兩大核心模塊:認證(Authentication)和鑑權(Authorization)。spring
官方給出的Spring Security的核心架構圖以下:
數據庫
核心架構解讀:express
經過對源碼的分析,我把Spring Security的核心領域模型設計整理以下:
安全
全局抽象模型解讀:bash
理清Spring Security的定製點後,就能夠在系統內部集成Spring Security了。架構
這裏使用預認證的方式,以適配第三方認證系統。AbstractPreAuthenticatedProcessingFilter提供了預認證的擴展點,基於該抽象類實現一個自定義認證過濾器。app
public class MyPreAuthFilter extends AbstractPreAuthenticatedProcessingFilter { @Override protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { // 從第三方系統獲取用戶ID return userId; } @Override protected Object getPreAuthenticatedCredentials(HttpServletRequest request) { return ""; } }
Spring Security會根據預認證過濾器getPreAuthenticatedPrincipal返回的用戶ID信息,加載用戶角色等初始信息。這裏須要實現UserDetailsManager接口,提供用戶信息管理器。框架
@Service public class MyUserManager implements UserDetailsManager { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 從數據庫加載用戶信息 return user; } // 其餘管理接口 }
UserDetails內包含了GrantedAuthority接口類型的權限信息抽象,通常能夠基於它自定義角色和權限。Spring Security使用一種接口形式表達角色和權限,角色和權限的差異是角色的ID是以"ROLE_"爲前綴。
public class MyRole implements GrantedAuthority { private final String role; @Override public String getAuthority() { return "ROLE_" + role; } } public class MyAuthority implements GrantedAuthority { private final String authority; @Override public String getAuthority() { return authority; } }
接下來註冊自定義認證過濾器和用戶管理器,這裏須要實現WebSecurityConfigurerAdapter進行Web安全配置。
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY) public class MySecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserDetailsManager userDetailsManager; @Bean protected AuthenticationProvider createPreAuthProvider() { // 註冊用戶管理器 PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsManager)); return provider; } @Override protected void configure(HttpSecurity http) throws Exception { // 註冊預認證過濾器 http.addFilter(new MyPreAuthFilter(authenticationManager())); } }
這樣,最簡單的Spring Security框架集成內系統內部已經完成了。在系統的任意服務接口上可使用以下方式進行鑑權。
public interface MyService { @PreAuthorize("hasAuthority('QUERY')") Object getById(String id); @PreAuthorize("hasRole('ADMIN')") void deleteById(String id); }
PreAuthorize註解表示調用前鑑權,Spring使用默認使用動態代理技術生成鑑權邏輯。註解內配置了SpringEL表達式來定製鑑權方式。上述代碼中,hasAuthority會檢查用戶是否有QUERY權限,hasRole會檢查用戶是否有ADMIN角色。
使用動態代理的方式進行AOP,只容許在接口層面進行權限攔截,若是想在任意的方法上進行權限攔截,那麼就須要藉助於AspectJ的方式進行AOP。首先將註解EnableGlobalMethodSecurity的mode設置爲AdviceMode.ASPECTJ,而後添加JVM啓動參數,這樣就能夠在任意方法上使用Spring Security的註解了。
-javaagent:/path/to/org/aspectj/aspectjweaver/1.9.4/aspectjweaver-1.9.4.jar
以上仍是隻是以用戶的身份信息(角色/權限)進行權限,靈活度有限,也發揮不了Spring Security的數據鑑權的能力。要使用數據鑑權,須要實現一個Spring Bean。
@Component public class MyPermissionEvaluator implements PermissionEvaluator { @Override public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { // 自定義數據鑑權 return false; } @Override public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) { // 自定義數據鑑權 return false; } }
PermissionEvaluator會被自動註冊到Spring Security框架,並容許在註解內使用以下方式進行鑑權。
@PreAuthorize("hasPermission(#id, 'QUERY')") Object func1(String id) { } @PreAuthorize("hasPermission(#id, 'TABLE', 'QUERY')") Object func2(String id) { }
其中,func1的註解表示校驗用戶是否對id有QUERY權限,代碼邏輯路由到MyPermissionEvaluator的第一個接口。func2的註解表示校驗用戶是否對TABLE類型的id有QUERY權限,代碼邏輯路由到MyPermissionEvaluator的第二個接口。PermissionEvaluator提供了權限系統中數據鑑權的擴展點,稍後會描述如何利用該擴展點定製基於RBAC的權限系統。
構建基於RBAC(Role Based Access Control)的權限系統,須要明確用戶、角色、權限、資源這幾個核心的概念類的含義和它們之間的關係。
如下是咱們設計的基於RABC的權限核心領域模型:
通常狀況下,系統內須要權限管控的資源是沒法用戶自定義的,由於資源會耦合大量的業務邏輯,因此咱們提供了自 資源工廠,經過配置化的方式構建業務模塊所需的資源。而用戶、角色、權限,以及受權記錄都是能夠經過相應的管理器進行查詢更新。
另外,資源抽象容許表達資源的繼承和組合關係,繼而表達更復雜的資源模型,資源統一鑑權的流程爲:
綜上,基於統一資源抽象和資源配置化構建,能夠實現資源的統一構建,繼而實現統一鑑權。
本文從Spring Security的架構和原理出發,描述了開源安全框架對於認證和鑑權模塊的設計思路和細節。並提供了系統內集成Spring Security的方法,結合RBAC通用權限系統模型,討論了統一資源構建和統一鑑權的設計和實現。若是你也須要設計一個新的權限系統,但願本文對你有所幫助。