34

基于RBAC的权限控制浅析(结合Spring Security)

 4 years ago
source link: http://www.cnblogs.com/liruilong/p/12993346.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

嗯,昨天面试让讲我的项目,让我讲讲项目里权限控制那一块的,讲的很烂。所以整理一下。

按照面试官的提问流程来讲:

一、RBAC是个啥东西了?

RBAC Role-Based Access Control ),即基于角色的访问控制模型,我的项目是 基于 RBAC0 模型 . 由于之相对应的数据实体构成 . 由用户表 , 角色映射表 , 角色表 , 权限表 , 权限映射表构成 .

ymU3iaI.png!web

1 RBAC0 模型图

二、你可以讲讲权限控制大概执行流程吗?

用户登录之后首先进行身份验证,成功之后获取当前用户的所有角色,之后根据角色加载对应的权限菜单,这里默认不加载没有权限的菜单,当存在直接输入URL路径的情况时,对于登录用户的每一个请求,都会通过鉴权处理,分析角色.最后通过权限的判断分析是否可以访问菜单资源.

在 spring Security,对用登录的请先通过 FilterInvocationSecurityMetadataSource 的实现类获取当前 请求,分析需要的角色,该类的主要功能就是通过当前的请求地址,获取该地址需要的用户角色。

1、获取当前访问路径的URL路径

2、获取所有资源URL,即所有的菜单URL路径

3、当前的访问URL和返回的每个URL基于Ant风格比较,如果相等,获取当前访问URL的所有角色。如果没有相等的,定义资源为公告资源,并且给予一个公告资源的角色。

4、当为公共资源时,判断用户是否登录。登录放行。返回资源

5、当为角色资源时,登录用户的角色列表和该资源的角色列表进行比较,如果有相同角色,放行,返回资源

6、当即不是公共资源也没有相匹配的角色的时候。抛异常,没有权限

BbyeY3R.png!web

图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和表结构写一下吗?

BFzYFz3.png!web

加载所有的菜单资源;返回所有的菜单资源和对应的角色集合,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`

E36fMvR.png!web

2 ERBAC数据实体关系图

用户登录之后首先进行身份验证 , 成功之后获取当前用户的所有角色 , 之后根据角色加载对应的权限菜单 , 这里默认不加载没有权限的菜单 , 当存在直接输入 URL 路径的情况时 , 对于登录用户的每一个请求 , 都会通过鉴权处理 , 分析角色 . 最后通过权限的判断分析是否可以访问菜单资源 .

用户表:

eYVRVfJ.png!web

角色表:

Fv6ZFb3.png!web

用户角色映射表:

vUzaAn2.png!web

权资源表:

BFnyeaF.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK