在业务中,我们会需要对一些操作进行用户级别的权限控制,例如,根据ID删除某条数据的时候,如果该数据并不是当前用户创建的,那么直接被删除了,显然是不合理的,这个时候,我们就需要对操作进行用户级别的权限控制。
1.基于spring security实现
开启配置
@EnableGlobalMethodSecurity(prePostEnabled = true)
基于@PreAuthorize 做前置的权限判断
@Service
public class SecurityPermission {
public boolean hasPermission(String id){
return true;
}
}
@RequiredArgsConstructor
@Service
public class UserService {
@PreAuthorize("@securityPermission.hasPermission(#id)")
public void create(String id){
}
}
@Test
public void create(){
JWTUserDetails userDetails = JWTUserDetails.builder().token("").operateId(1L).build();
Authentication authentication = new JWTAuthenticationToken(userDetails);
SecurityContextHolder.getContext().setAuthentication(authentication);
userService.create("1111");
}
hasPermission 的返回值是true,所以直接就过了,下面把返回值改成false,试试单元测试
org.springframework.security.access.AccessDeniedException: 不允许访问
spring security 提供的支持还不只这些,这里就不展开说,下回可以单独的篇幅专门讲。其实应用级别的,主要是需要一个方向或者思路,剩下的基本都可以通过查资料解决。
如果项目没有依赖spring security模块,那么我们还可以自己参考spring security自己实现
2.自己通过aop实现
写一个注解用于作为切面点
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ActPermission {
String value();
}
由于处理逻辑中需要对spel表达式支持,这里,定义一个 EvaluationContext
public class ActPermissionEvaluationContext extends MethodBasedEvaluationContext {
public ActPermissionEvaluationContext(Object rootObject, Method method, Object[] arguments, ParameterNameDiscoverer parameterNameDiscoverer) {
super(rootObject, method, arguments, parameterNameDiscoverer);
}
}
具体实现逻辑
@Aspect
@Component
public class ActPermissionAspect implements ApplicationContextAware {
private BeanResolver beanResolver;
@Pointcut(value = "@annotation(ActPermission)")
public void actPermissionAspect() {
}
@Before(value = "actPermissionAspect()")
public void before(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
ActPermission actPermission = AnnotationUtils.getAnnotation(methodSignature.getMethod(), ActPermission.class);
if(Objects.isNull(actPermission)){
return;
}
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new ActPermissionEvaluationContext(joinPoint, methodSignature.getMethod(), joinPoint.getArgs(),new DefaultParameterNameDiscoverer() );
context.setBeanResolver(this.beanResolver);
Boolean result = parser.parseExpression(actPermission.value()).getValue(context,Boolean.class);
if(Objects.isNull(result) || !result){
throw new RuntimeException("has no permission");
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanResolver = new BeanFactoryResolver(applicationContext);
}
}
用法跟@PreAuthorize基本类似,都是基于spel表达式实现的
@Service
public class ArticleService {
@ActPermission("@articleService.hasPermission(#id)")
public void updateArticle(String id){
System.out.println("update success");
}
public boolean hasPermission(String id){
return false;
}
}
自定义注解中的spel表达式,内容无法自动补全高亮,找了很久也没找到解决办法。有大佬知道,请指点指点。