AOP,是一种面向切面编程,可以通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
在软件开发中,鉴权(Authentication)是一项非常重要的安全措施,用于验证用户身份和权限。在应用程序中,我们通常会使用AOP(Aspect-Oriented Programming)来实现鉴权功能,以便在需要进行鉴权的地方进行统一的处理。
一种常用的实现AOP鉴权的方式是使用自定义注解。通过定义一个自定义注解,并在需要进行鉴权的方法上加上该注解,我们可以在运行时通过AOP切面来拦截方法调用,并进行鉴权操作。
第一种实现逻辑
首先,我们需要定义一个自定义注解,用于标识需要进行鉴权的方法。可以使用Java的注解机制来实现,如下所示:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authenticated {
String[] roles() default {};
}
在上面的代码中,我们定义了一个名为Authenticated的注解,并指定了它的作用目标为方法。Authenticated注解还包含了一个可选的roles属性,用于指定允许访问该方法的用户角色。
接下来,我们需要编写一个AOP切面来实现鉴权逻辑。可以使用Spring框架提供的@Aspect注解来定义一个切面类,并使用@Around注解来实现方法拦截和鉴权逻辑,如下所示:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AuthorizationAspect {
@Around("@annotation(authenticated)")
public Object authenticate(ProceedingJoinPoint joinPoint, Authenticated authenticated) throws Throwable {
// 获取当前用户的角色
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String[] userRoles = authentication.getAuthorities().stream().map(Object::toString).toArray(String[]::new);
// 获取方法需要的角色
String[] requiredRoles = authenticated.roles();
// 鉴权逻辑
boolean authorized = false;
for (String requiredRole : requiredRoles) {
for (String userRole : userRoles) {
if (userRole.equals(requiredRole)) {
authorized = true;
break;
}
}
}
// 如果鉴权通过,则继续执行原方法,否则抛出鉴权异常
if (authorized) {
return joinPoint.proceed();
} else {
throw new AuthorizationException("Access denied");
}
}
}
在上面的代码中,我们定义了一个名为AuthorizationAspect的切面类,并使用@Around注解来标识需要拦截的方法。在authenticate方法中,我们首先获取当前用户的角色,然后与方法需要的角色进行比较,判断是否有权限访问该方法。
最后,我们在需要进行鉴权的方法上加上@Authenticated注解,并指定允许访问该方法的用户角色,如下所示:
@Authenticated(roles = {"admin", "user"})
public void someMethod() {
// 需要鉴权的方法逻辑
}
通过上述步骤,我们就实现了使用自定义注解方式来实现AOP鉴权。在运行时,AOP切面会拦截带有@Authenticated注解的方法调用,并进行鉴权操作。如果鉴权通过,则继续执行原方法,否则抛出鉴权异常。
第二种实现逻辑
新建一个注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionAnnotation {
String id() default "";
String name() default "";
}
新建切面类
@Aspect
@Component
public class PermissionAspect {
@Pointcut("@annotation(xxx.xxx.xxx.PermissionAnnotation)")
public void permissionPointCut() {
// Do nothing because of it's a pointcut
}
@Around("permissionPointCut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature sign = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = sign.getMethod();
PermissionAnnotation annotation = method.getAnnotation(PermissionAnnotation.class);
String id= annotation.id();
String name= annotation.name();
if (checkPermission(id, name)) {
// 有权限,业务方法执行
Object result = proceedingJoinPoint.proceed();
// 业务方法执行完可进行额外操作
} else {
// 没有权限
}
}
private boolean checkPermission() {
// 权限判断
if (xxx) {
return true;
}
return false;
}
}
业务方法使用
@Override
@PermissionAnnotation(id= "${id}")
public void deleteUser(String id) {
//业务代码
}
其实这俩种实现逻辑基于的原理是一样的,就是看实际应用需要的是何种形式,当然也会有第三种第四种…
总结起来,使用自定义注解方式实现AOP鉴权可以提高代码的可读性和可维护性