自定义安全表达式
/**
* Base root object for use in Spring Security expression evaluations.
*
* @author Luke Taylor
* @since 3.0
*/
public abstract class SecurityExpressionRoot implements SecurityExpressionOperations
SecurityExpressionRoot 是所有安全表达式的基类,我们需要做的就是:
- 继承 SecurityExpressionRoot,自定义 MethodSecurity 表达式
- 继承 DefaultMethodSecurityExpressionHandler,自定义方法级别的安全校验处理器
- 继承 GlobalMethodSecurityConfiguration,添加自定义的表达式处理器
一、MethodSecurityConfig
开启方法级别的安全校验,注入登录用户时加载 UserDetails 的DB服务对象,通过 createExpressionHandler 注入自定义的表达式处理器。
/**
* 配置 MethodSecurity 表达式
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
private final LoginDetailService loginDetailService;
public MethodSecurityConfig(LoginDetailService loginDetailService) {
this.loginDetailService = loginDetailService;
}
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new ResourceMethodSecurityExpressionHandler(loginDetailService);
}
}
二、ResourceMethodSecurityExpressionHandler
接收 UserDetailsService,并创建一个处理表达式的操作类
/**
* 自定义方法级别的安全校验处理器
*/
public class ResourceMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final LoginDetailService loginDetailService;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
public ResourceMethodSecurityExpressionHandler(LoginDetailService loginDetailService) {
this.loginDetailService = loginDetailService;
}
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
final ResourceMethodSecurityExpressionRoot root = new ResourceMethodSecurityExpressionRoot(authentication, loginDetailService);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
三、ResourceMethodSecurityExpressionRoot
仿照 hasAuthority,编写一个自己的实现
/**
* 自定义 MethodSecurity 表达式
*/
@Slf4j
public class ResourceMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
private LoginDetailService loginDetailService;
private Object filterObject;
private Object returnObject;
private Object target;
public ResourceMethodSecurityExpressionRoot(Authentication authentication, LoginDetailService loginDetailService) {
super(authentication);
this.loginDetailService = loginDetailService;
}
/**
* 自定义接口,是否允许对该id的访问
*/
public boolean canReadCourse(String courseId) {
log.debug("method params courseId", courseId);
log.debug("current principal {}", getPrincipal());
return true;
}
public final boolean hasGlobalAuthority(String authority) {
return hasAnyGlobalAuthority(authority);
}
public final boolean hasAnyGlobalAuthority(String... authorities) {
return hasAnyGlobalAuthorityName(null, authorities);
}
private boolean hasAnyGlobalAuthorityName(String prefix, String... roles) {
final String username = ((UserDetails) getPrincipal()).getUsername();
final UserDetails details = loginDetailService.loadUserByUsername(username);
if (details.getAuthorities() != null) {
final Set<String> roleSet = details.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
for (String role : roles) {
String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
if (roleSet.contains(defaultedRole)) {
return true;
}
}
}
return false;
}
private static String getRoleWithDefaultPrefix(String defaultRolePrefix, String role) {
if (role == null) {
return role;
}
if (defaultRolePrefix == null || defaultRolePrefix.length() == 0) {
return role;
}
if (role.startsWith(defaultRolePrefix)) {
return role;
}
return defaultRolePrefix + role;
}
@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 target;
}
public void setTarget(Object target) {
this.target = target;
}
@Override
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
super.setRoleHierarchy(roleHierarchy);
}
}