在后面的文章中,松哥曾经和小伙伴们聊了 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 注解中应用的不必加对象名就能调用的权限办法,如 hasAuthority
、hasPermission
、hasRole
、hasAnyRole
等,基本上都是由 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 我的项目配套视频来啦。