1. 程式人生 > >基於RBAC的許可權控制淺析(結合Spring Security)

基於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路徑的情況時,對於登入使用者的每一個請求,都會通過鑑權處理,分析角色.最後通過許可權的判斷分析是否可以訪問選單資源.

使用者表:

 

 角色表:

 

使用者角色對映表:

 

權資源表:

 

&n