基於RBAC的權限控制淺析(結合Spring Security)
嗯,昨天面試讓講我的項目,讓我講講項目里權限控制那一塊的,講的很爛。所以整理一下。
按照面試官的提問流程來講:
一、RBAC是個啥東西了?
RBAC(Role-Based Access Control ),即基於角色的訪問控制模型,我的項目是基於RBAC0模型.由於之相對應的數據實體構成.由用戶表,角色映射表,角色表,權限表,權限映射表構成.
圖1 RBAC0模型圖
二、你可以講講權限控制大概執行流程嗎?
用戶登錄之後首先進行身份驗證,成功之後獲取當前用戶的所有角色,之後根據角色加載對應的權限菜單,這裡默認不加載沒有權限的菜單,當存在直接輸入URL路徑的情況時,對於登錄用戶的每一個請求,都會通過鑒權處理,分析角色.最後通過權限的判斷分析是否可以訪問菜單資源.
在 spring Security,對用登錄的請先通過FilterInvocationSecurityMetadataSource的實現類獲取當前請求,分析需要的角色,該類的主要功能就是通過當前的請求地址,獲取該地址需要的用戶角色。
1、獲取當前訪問路徑的URL路徑
2、獲取所有資源URL,即所有的菜單URL路徑
3、當前的訪問URL和返回的每個URL基於Ant風格比較,如果相等,獲取當前訪問URL的所有角色。如果沒有相等的,定義資源為公告資源,並且給予一個公告資源的角色。
4、當為公共資源時,判斷用戶是否登錄。登錄放行。返回資源
5、當為角色資源時,登錄用戶的角色列表和該資源的角色列表進行比較,如果有相同角色,放行,返回資源
6、當即不是公共資源也沒有相匹配的角色的時候。拋異常,沒有權限
圖2 系統訪問控制流程圖
代碼:
鑒權:
@Component public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Autowired MenuService menuService; //路徑比較工具 AntPathMatcher antPathMatcher = new AntPathMatcher(); Logger logger = Logger.getLogger("com.liruilong.hros.config.ustomFilterInvocationSecurityMetadataSource"); /** * @return java.util.Collection<org.springframework.security.access.ConfigAttribute> * 返回值是 Collection<ConfigAttribute>,表示當前請求 URL 所需的角色。 * @Author Liruilong * @Description 當前請求需要的角色,該方法的參數是一個 FilterInvocation, 開發者可以從 Filterlnvocation 中提取出當前請求的 URL, * @Date 18:13 2019/12/24 * @Param [object] **/ @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { //獲取當前請求路徑 String requestUrl = ((FilterInvocation) object).getRequestUrl(); logger.warning(requestUrl); //獲取所有的菜單url路徑 List<Menu> menus = menuService.getAllMenusWithRole(); // AntPathMatcher,主要用來實現 ant 風格的 URL 匹配。 for (Menu menu : menus) { if (antPathMatcher.match(menu.getUrl(), requestUrl)) { //擁有當前菜單權限的角色 List<Role> roles = menu.getRoles(); String[] strings = new String[roles.size()]; for (int i = 0; i < roles.size(); i++) { strings[i] = roles.get(i).getName(); } return SecurityConfig.createList(strings); } } // 沒匹配上的資源都是登錄,或者為公共資源 return SecurityConfig.createList("ROLE_LOGIN"); }
@Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { for (ConfigAttribute configAttribute : configAttributes) { String needRole = configAttribute.getAttribute(); if ("ROLE_LOGIN".equals(needRole)) { if (authentication instanceof AnonymousAuthenticationToken) { throw new AccessDeniedException("尚未登錄,請登錄!"); } else { return; } } Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for (GrantedAuthority authority : authorities) { if (authority.getAuthority().equals(needRole)) { return; } } } throw new AccessDeniedException("權限不足,請聯繫管理員!"); }
三、你可以把對應的SQL和表結構寫一下嗎?
加載所有的菜單資源;返回所有的菜單資源和對應的角色集合,Service端和訪問的URL的比較,存在判斷角色。(鑒權)
select m.*,r.`id` as rid,r.`name` as rname,r.`namezh` as rnamezh from menu m,menu_role mr,role r where m.`id`=mr.`mid` and mr.`rid`=r.`id` order by m.`id`
根據用戶ID返回當前用戶的全部菜單資源(授權)
select m1.`id`,m1.url,m1.`path`,m1.`component`,m1.`iconCls`,m1.`name`,m1.`requireAuth`,m1.keepAlive,m1.enabled, m2.id as id2,m2.url as url2,m2.name as name2,m2.`component` as component2,m2.`iconCls` as iconCls2,m2.`keepAlive` as keepAlive2,m2.`path` as path2,m2.`requireAuth` as requireAuth2,m2.enabled as enabled2,m2.parentId as parentId2 from menu m1,menu m2 where m1.`id`=m2.`parentId` and m1.`id`!=1 and m2.`id` in(select mr.`mid` from hr_role h_r,menu_role mr where h_r.`rid`=mr.`rid` and h_r.`hrid`=#{hrId}) and m2.`enabled`=true order by m1.`id`,m2.`id`
圖2 ERBAC數據實體關係圖
用戶登錄之後首先進行身份驗證,成功之後獲取當前用戶的所有角色,之後根據角色加載對應的權限菜單,這裡默認不加載沒有權限的菜單,當存在直接輸入URL路徑的情況時,對於登錄用戶的每一個請求,都會通過鑒權處理,分析角色.最後通過權限的判斷分析是否可以訪問菜單資源.
用戶表:
角色表:
用戶角色映射表:
權資源表: