乐趣区

关于java:如何在-Spring-Security-中自定义权限表达式

在后面的文章中,松哥曾经和小伙伴们聊了 Spring Security 中的权限表达式了,还没看过的小伙伴们能够先看下,本文将在前文的根底上持续欠缺:

  • Spring Security 中,想在权限中应用通配符,怎么做?

1. SpEL 回顾

通过上篇文章的学习,小伙伴们曾经晓得了,在 Spring Security 中,@PreAuthorize、@PostAuthorize 等注解都是反对 SpEL 表达式的。

在 SpEL 表达式中,如果上来就间接写要执行的办法名,那么就阐明这个办法是 RootObject 对象中的办法,如果要执行其余对象的办法,那么还须要写上对象的名字,例如如下两个例子:

@PreAuthorize("hasAuthority('system:user:add')")
public String add() {return "add";}

下面这个例子中,表达式中的办法是 hasAuthority,没有写对象名,那么就阐明这个办法是 SpEL 中 RootObject 对象中的办法。

@PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
@GetMapping("/list")
public TableDataInfo list(SysOperLog operLog) {startPage();
    List<SysOperLog> list = operLogService.selectOperLogList(operLog);
    return getDataTable(list);
}

下面这个例子中,权限注解中的表达式办法是 @ss.hasPermi('monitor:operlog:list'),其中 ss 是指 Spring 容器中的一个对象名,hasPermi 则是这个对象中的办法。

好啦,通过后面文章的学习,这些基本知识大家都曾经把握了。

2. 如何自定义

其实下面给进去的第二个例子就是一个自定义的例子。

不过,这种自定义形式太自在了,自在到没有在 Spring Security 架构内实现这件事。所以,明天我想和小伙伴们聊一聊,如何在不应用第三方对象的状况下,来自定义一个权限判断的表达式。

首先小伙伴们晓得,咱们在 @PreAuthorize 注解中应用的不必加对象名就能调用的权限办法,如 hasAuthorityhasPermissionhasRolehasAnyRole 等,基本上都是由 SecurityExpressionRoot 及其子类提供的,精确来说是由 MethodSecurityExpressionRoot 类提供的。

MethodSecurityExpressionRoot 类实际上继承自 SecurityExpressionRoot,只不过减少了过滤对象以及返回值对象。咱们来看下 MethodSecurityExpressionRoot 的办法摘要:

再来看看 SecurityExpressionRoot 中的办法:

这些就是 RootObject 对象中的所有办法了,也是咱们可能在 @PreAuthorize 注解中应用的所有办法了。

那么当初想在已有办法上持续扩大新办法,那么咱们能够通过自定义类继承自 SecurityExpressionRoot 对象,扩大这个 RootObject 对象,在该对象中持续增加新的办法,进而实现自定义权限表达式。

好啦,说干就干,开搞!

本文的案例在前文的根底上持续实现,所以这里我就不从头开始写了。

3. 自定义 ExpressionRoot

首先咱们自定义一个类继承自 SecurityExpressionRoot 并实现 MethodSecurityExpressionOperations 接口(原本间接继承自 MethodSecurityExpressionRoot 即可,然而因为这个类不是 public 的,没法继承,所以咱们就实现 MethodSecurityExpressionOperations 接口即可):

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private Object filterObject;
    private Object returnObject;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     * Creates a new instance
     *
     * @param authentication the {@link Authentication} to use. Cannot be null.
     */
    public CustomSecurityExpressionRoot(Authentication authentication) {super(authentication);
    }

    /**
     * 判断以后对象是否具备某一个权限
     * @param permission
     * @return
     */
    public boolean hasPermission(String permission) {
        // 获取以后登录用户所具备的权限
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (GrantedAuthority authority : authorities) {if (antPathMatcher.match(authority.getAuthority(), permission)) {return true;}
        }
        return false;
    }

    /**
     * 是否具备多个权限中的任意一个权限
     * @param permissions
     * @return
     */
    public boolean hasAnyPermissions(String... permissions) {if (permissions == null || permissions.length == 0) {return false;}
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (GrantedAuthority authority : authorities) {for (String permission : permissions) {if (antPathMatcher.match(authority.getAuthority(), permission)) {return true;}
            }
        }
        return false;
    }

    public boolean hasAllPermissions:(String... permissions) {Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        if (permissions == null || permissions.length == 0) {return false;}
        for (String permission : permissions) {
            boolean flag = false;
            for (GrantedAuthority authority : authorities) {if (antPathMatcher.match(authority.getAuthority(), permission)) {flag = true;}
            }
            if (!flag) {return false;}
        }
        return true;
    }

    @Override
    public void setFilterObject(Object filterObject) {this.filterObject = filterObject;}

    @Override
    public Object getFilterObject() {return filterObject;}

    @Override
    public void setReturnObject(Object returnObject) {this.returnObject = returnObject;}

    @Override
    public Object getReturnObject() {return returnObject;}

    @Override
    public Object getThis() {return this;}
}

加了 @Override 注解的办法,都是普普通通的惯例办法,没啥好说的。咱们本人次要实现了三个办法,别离是:

  • hasPermission:判断以后用户是否具备某一个给定的权限。
  • hasAnyPermissions:判断以后用户是否具备给定的多个权限中的某一个。
  • hasAllPermissions:判断以后用户是否具备所有的给定的权限。

这里边的逻辑我就不啰嗦了,都是根本的 Java 语法而已。

另外,用 AntPathMatcher 做比对是为了反对通配符,这个在上篇文章中曾经说过了,这里不再赘述。

Spring Security 中,MethodSecurityExpressionRoot 的配置是通过 DefaultMethodSecurityExpressionHandler 来实现的,当初咱们自定义了 CustomSecurityExpressionRoot,那也得有一个 Handler 来配置 CustomSecurityExpressionRoot,所以,再来一个类继承自 DefaultMethodSecurityExpressionHandler,如下:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication);
        root.setTrustResolver(getTrustResolver());
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setRoleHierarchy(getRoleHierarchy());
        return root;
    }
}

在 createSecurityExpressionRoot 办法中创立一个 CustomSecurityExpressionRoot 对象,对象的 TrustResolver、权限评估器以及角色层级等,通通都用默认的计划即可。

配置实现后,再配置一下 CustomMethodSecurityExpressionHandler 这个 Bean 即可,如下:

@Bean
CustomMethodSecurityExpressionHandler customMethodSecurityExpressionHandler() {return new CustomMethodSecurityExpressionHandler();
}

好啦,这就注入胜利了。

接下来,咱们就能够在权限注解中应用这个自定义的办法了:

@PreAuthorize("hasPermission('system:user:add')")
public String add() {return "add";}

这个自定义权限表达式的思路,说到底还是在 Spring Security 体系中玩,个人感觉这种形式更正当一些。

在 TienChin 我的项目中,松哥也将依照这种思路去革新 RuoYi-Vue 脚手架。届时在 TienChin 我的项目视频中,我再和大伙细聊,对视频感兴趣的小伙伴,戳这里:TienChin 我的项目配套视频来啦。

退出移动版