• 使用spring security实现角色继承的权限控制


    使用角色继承

    spring security通过启用方法拦截,控制访问权限

    注解 EnableGlobalMethodSecurity

    注解@EnableGlobalMethodSecurity可以开启spring security的全局安全设置,其自动化配置位于类GlobalMethodSecurityConfiguration中。

    该注解提供了三种类型的安全设置:

    prePostEnabled

    是否开启方法前后拦截,默认为false。

    设置true

    • 可开启@PreAuthorize@PostAuthorize, 这两个注解可接受一个EL表达式,非常灵活的控制权限。从字面意思可直观看出,一个用于进入方法前,一个用于进入方法后,后者等同于放开权限,但是控制返回。

    • 可开启@PreFilter@PostFilter,从字面意思可以看出,用于方法前后的两个注解,该注解用于操作入参和出参。

    securedEnabled

    是否开启@Secure,默认false,该注解仅接受一个角色/权限,一般用于粗劣度控制。

    jsr250Enabled

    使用较少,从字面意思可以看出属于jsr250的范畴,目前位于jakarta项目。

    启用jsr250,可以使用如 @DenyAll @AllowAll @RolesAllowed等注解。

    使用@PreAuthorize开启基于角色继承的权限控制

    树形结构的组织架构是一种比较常见的架构形式,基于组织架构的角色授予也通常会表显出一种树形结构,节点之上的角色通常会拥有子节点的权限,也表现出一种树形结构。

    比如:

    角色架构
    经理
    组长
    成员
    组织架构
    组A
    项目经理
    组B
    组员1
    组员2
    组员3

    此时文字表示即:Role_经理 > Role_组长 > Role_成员

    在接口方法上使用@PreAuthorize控制权限的写法即 @PreAuthorize("hasRole('Role_经理')") [依此类推]

    为了实现角色继承,需要使用接口RoleHierarchy

    RoleHierarchy接口

    该接口只有一个方法,返回了角色的层级关系。

    非常简单,不啰嗦,直接上代码。

    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        Map<String, List<String>> map = MapBuilder.create(new HashMap<String, List<String>>())
                .put("ROLE_经理", Lists.newArrayList("ROLE_组长", "ROLE_成员"))
                .put("ROLE_组长", Lists.newArrayList("ROLE_成员"))
                .build();
        roleHierarchy.setHierarchy(RoleHierarchyUtils.roleHierarchyFromMap(map));
        return roleHierarchy;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    只需要提供一个该接口的实现并注册即可

    RoleHierarchyImpl是spring已经给我们的一种实现

    解释

    为什么这么简单即可实现角色的继承控制,其实就是一个AOP的问题。

    找到@PreAuthorize是怎么实现功能的接口。
    ReactiveMethodSecurityConfiguration中定义了一个方法拦截器

    @Bean
    public PrePostAdviceReactiveMethodInterceptor securityMethodInterceptor(AbstractMethodSecurityMetadataSource source, MethodSecurityExpressionHandler handler) {
    
    	ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(
    			handler);
    	ExpressionBasedPreInvocationAdvice preAdvice = new ExpressionBasedPreInvocationAdvice();
    	preAdvice.setExpressionHandler(handler);
    
    	return new PrePostAdviceReactiveMethodInterceptor(source, preAdvice, postAdvice);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这里头的条条框框不细说,吧唧吧唧很多,最后就是拦截@PreAuthorize这个注解,然后解析这个注解里面的表达式。

    跟代码最后会进入到MethodSecurityExpressionRoot这个里头,但是这个是依赖其父类SecurityExpressionRoot来实现的,这个父类提供的一系列final方法,都可以用到@PreAuthorize注解中(既然是EL表达式,当然支持逻辑运算与或非),找到hasRole方法开始跟代码(也许聪明如你,通过idea的插件,直接找到了这里,.)

    private Set<String> getAuthoritySet() {
    	if (roles == null) {
    		Collection<? extends GrantedAuthority> userAuthorities = authentication
    				.getAuthorities();
    
    		if (roleHierarchy != null) {
    			userAuthorities = roleHierarchy
    					.getReachableGrantedAuthorities(userAuthorities);
    		}
    
    		roles = AuthorityUtils.authorityListToSet(userAuthorities);
    	}
    
    	return roles;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这里获取用户的角色时(字面意思看起来叫权限,角色=权限的集合),判断了如果注入了角色继承就会计算一把角色继承关系。

    再顺着这里反过来找,最后会找到前面说的GlobalMethodSecurityConfiguration

    在方法afterSingletonsInstantiated中有一段:

    RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
    if (roleHierarchy != null) {
    	this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
    }
    
    • 1
    • 2
    • 3
    • 4

    此处获取了RoleHierarchy的一个单例实例,并注入进来。

  • 相关阅读:
    频谱分析过程中的混叠现象、栅栏现象和泄漏现象
    【JavaScript】时间日期:Date对象
    Wireshark TS | FIN 之后不关闭连接疑问
    【GO语言基础】前言
    “豫”见超融合,私有云浪潮开启新一线
    EASEX绘制卡通头像
    程序员的刻板印象,都是真的吗?
    JAVA知识管理系统计算机毕业设计Mybatis+系统+数据库+调试部署
    软件测试正在面试银行的可以看下这些面试题
    第十四届蓝桥杯模拟赛(第二期)
  • 原文地址:https://blog.csdn.net/z313410164/article/details/126038755