找不到之前的Github项目了,我把它放在Gitcode上了,这个是别人大佬的项目
这是一个微服务项目
我们先看ruoyi-common
ruoyi-common-auth
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ruoyi.cloud</groupId>
<artifactId>ruoyi-common</artifactId>
<version>1.1.0-SNAPSHOT</version>
</parent>
<artifactId>ruoyi-common-auth</artifactId>
<dependencies>
<!-- 系统模块 -->
<dependency>
<groupId>com.ruoyi.cloud</groupId>
<artifactId>ruoyi-system-api</artifactId>
<version>${ruoyi.version}</version>
</dependency>
</dependencies>
</project>
以下是对这个 `pom.xml` 文件的解释:
1. `<project>` 标签:这是 Maven 项目配置的根标签。 - `xmlns` 和 `xsi:schemaLocation` 属性定义了 Maven POM 模型的命名空间和模式位置。
2. `<modelVersion>` 标签:指定了使用的 Maven 项目对象模型(POM)的版本,这里是 `4.0.0` 。
3. `<parent>` 标签:定义了父项目的坐标,包括组 ID(`com.ruoyi.cloud`)、项目 ID(`ruoyi-common`)和版本(`1.1.0-SNAPSHOT`)。这意味着当前项目继承自这个父项目的配置。
4. `<artifactId>` 标签:指定当前项目的 ID 为 `ruoyi-common-auth` 。
5. `<dependencies>` 标签:用于定义项目的依赖。 - 这里有一个依赖项: - `<groupId>`、`<artifactId>` 和 `<version>` 一起确定了依赖的具体模块。组 ID 为 `com.ruoyi.cloud`,项目 ID 为 `ruoyi-system-api`,版本通过 `${ruoyi.version}` 来引用一个可能在其他地方定义的变量。
回忆一下在pom.xml中parent的作用
在 Maven 的 `pom.xml` 文件中,`<parent>` 元素的主要作用是实现项目配置的继承。 通过指定父项目的坐标(`<groupId>`、`<artifactId>` 和 `<version>`),当前项目可以继承父项目中的以下配置:
1. 依赖管理(`<dependencyManagement>`):父项目中声明的依赖管理配置,子项目可以直接引用,无需重复指定版本等信息。
2. 插件配置(`<pluginManagement>`):父项目中对插件的配置,子项目可以继承和选择性地覆盖。
3. 通用属性:例如项目的编码方式、构建配置等通用属性。 这样可以减少子项目中重复的配置,保持配置的一致性和可维护性。
当多个子项目具有相似的配置需求时,通过一个共同的父项目来定义这些配置可以提高效率和减少错误。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ruoyi.cloud</groupId>
<artifactId>ruoyi-cloud</artifactId>
<version>1.1.0-SNAPSHOT</version>
</parent>
<artifactId>ruoyi-common</artifactId>
<packaging>pom</packaging>
<modules>
<module>ruoyi-common-core</module>
<module>ruoyi-common-redis</module>
<module>ruoyi-common-log</module>
<module>ruoyi-common-auth</module>
<module>ruoyi-common-swagger</module>
</modules>
</project>
那么看父聚合型pom.xml
以下是对这段 `pom.xml` 配置的解析:
1. 根元素 `<project>` 定义了整个 Maven 项目的配置。
- `xmlns` 和 `xsi:schemaLocation` 定义了 Maven POM 的命名空间和模式位置。
2. `<modelVersion>` 指定了使用的 Maven 项目对象模型版本为 `4.0.0` 。
3. `<parent>` 定义了父项目的信息:
- `groupId` 为 `com.ruoyi.cloud` 。
- `artifactId` 为 `ruoyi-cloud` 。
- `version` 为 `1.1.0-SNAPSHOT` 。当前项目将继承父项目的一些配置。
4. `<artifactId>` 定义当前项目的 ID 为 `ruoyi-common` 。
5. `<packaging>` 被设置为 `pom` ,表示这是一个多模块项目的聚合 POM 。
6. `<modules>` 元素下包含了一系列子模块:
- `ruoyi-common-core`
- `ruoyi-common-redis`
- `ruoyi-common-log`
- `ruoyi-common-auth`
- `ruoyi-common-swagger`
总的来说,这是一个多模块 Maven 项目的聚合 POM 配置,定义了父项目以及包含的子模块。
问题:在pom.xml中<modules>
元素的作用
那么我们接着看
com/ruoyi/common/auth/annotation/HasPermissions.java
package com.ruoyi.common.auth.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>File:HasPermissions.java</p>
* <p>Title: 权限注解</p>
* <p>Description:</p>
* <p>Copyright: Copyright (c) 2019 2019年6月4日 上午10:43:54</p>
* <p>Company: </p>
* @author zmr
* @version 1.0
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HasPermissions
{
String value();
}
还记得之前我写过一篇文章说用自定义注解和AOP实现权限控制吗
这段 Java 代码定义了一个自定义的注解 `HasPermissions` 。
以下是对代码的详细解释:
- `@interface` 关键字用于定义注解。
- `@Target({ElementType.TYPE, ElementType.METHOD})` 表示该注解可以应用于类和方法。
- `@Retention(RetentionPolicy.RUNTIME)` 表示该注解在运行时是可获取的。
- 注解中有一个元素 `value` ,使用该注解时需要为其提供一个字符串值。
package com.ruoyi.common.auth.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
这段 Java 代码是在定义一个自定义的注解。
以下是对每行代码的详细解释:
package com.ruoyi.common.auth.annotation;
这行代码指定了该注解所在的包路径为 com.ruoyi.common.auth.annotation
。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
这些是导入了与注解相关的一些类和枚举。
ElementType
:用于指定注解可以应用的元素类型,例如类、方法、变量等。Retention
:用于指定注解的保留策略。RetentionPolicy
:定义了不同的保留级别,如SOURCE
(在源代码中有效)、CLASS
(在编译后的字节码中有效)、RUNTIME
(在运行时通过反射可获取)。Target
:用于指定注解可以应用的目标元素。
总体来说,这段代码是为后续定义具体的注解属性和行为做准备的。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HasPermissions
{
String value();
}
这段代码定义了一个名为 `HasPermissions` 的自定义注解。
- `@Target({ElementType.TYPE, ElementType.METHOD})` :表示这个注解可以应用于类(`TYPE`)和方法(`METHOD`)。
- `@Retention(RetentionPolicy.RUNTIME)` :表示该注解在运行时仍然是可获取的,这意味着在程序运行时可以通过反射机制来获取这个注解的信息。
- 注解定义了一个名为 `value` 的属性,类型为 `String` 。在使用这个注解时,必须为 `value` 属性提供一个字符串值。
问题:在java中自定义注解是怎么样的,自定义注解的方法
在 Java 中自定义注解的一般步骤如下:
- 使用
@interface
关键字来定义注解:
public @interface MyAnnotation {
// 定义注解的属性
}
-
为注解定义属性:
属性可以是各种基本数据类型、字符串、枚举、其他注解或它们的数组。
例如:
public @interface MyAnnotation {
int number();
String name();
MyEnum myEnum();
}
其中 MyEnum
是一个自定义的枚举类型。
- 为属性指定默认值(可选):
public @interface MyAnnotation {
int number() default 0;
String name() default "defaultName";
MyEnum myEnum() default MyEnum.VALUE1;
}
4.可以设置属性的访问修饰符,通常为 public
-
应用注解:
在需要的类、方法、变量等元素上使用自定义的注解。
例如:
@MyAnnotation(number = 10, name = "John")
public class MyClass {
// 类的内容
}
-
在运行时通过反射机制获取和处理注解信息:
可以使用 Java 的反射 API 来获取被注解元素上的注解,并获取注解的属性值,从而实现相应的逻辑处理。
自定义注解可以用于实现各种自定义的元数据标记和处理逻辑,增强代码的灵活性和可扩展性。
我们接着看其他代码
com/ruoyi/common/auth/aspect/PreAuthorizeAspect.java
package com.ruoyi.common.auth.aspect;
import java.lang.reflect.Method;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.auth.annotation.HasPermissions;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.ForbiddenException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.system.feign.RemoteMenuService;
import lombok.extern.slf4j.Slf4j;
@Aspect
@Component
@Slf4j
public class PreAuthorizeAspect
{
@Autowired
private RemoteMenuService sysMenuClient;
@Around("@annotation(com.ruoyi.common.auth.annotation.HasPermissions)")
public Object around(ProceedingJoinPoint point) throws Throwable
{
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
HasPermissions annotation = method.getAnnotation(HasPermissions.class);
if (annotation == null)
{
return point.proceed();
}
String authority = new StringBuilder(annotation.value()).toString();
if (has(authority))
{
return point.proceed();
}
else
{
throw new ForbiddenException();
}
}
private boolean has(String authority)
{
// 用超管帐号方便测试,拥有所有权限
HttpServletRequest request = ServletUtils.getRequest();
String tmpUserKey = request.getHeader(Constants.CURRENT_ID);
if (Optional.ofNullable(tmpUserKey).isPresent())
{
Long userId = Long.valueOf(tmpUserKey);
log.debug("userid:{}", userId);
if (userId == 1L)
{
return true;
}
return sysMenuClient.selectPermsByUserId(userId).stream().anyMatch(authority::equals);
}
return false;
}
}
这段 Java 代码定义了一个切面(`Aspect`)类 `PreAuthorizeAspect` ,用于进行权限控制的前置处理。 以下是对代码的详细解释:
- `@Aspect` :表明这是一个切面类。
- `@Component` :将该类注册为 Spring 容器中的一个组件。
- `@Slf4j` :用于日志记录。
`@Around("@annotation(com.ruoyi.common.auth.annotation.HasPermissions)")` :定义了一个环绕通知,会在被 `@HasPermissions` 注解标注的方法执行前后进行处理。
`around(ProceedingJoinPoint point)` 方法: - 获取方法的签名和对应的方法对象。
- 尝试获取方法上的 `@HasPermissions` 注解,如果没有则直接执行原方法。
- 获取注解的 `value` 值作为权限标识。
- 通过 `has(String authority)` 方法判断当前用户是否具有该权限,如果有则执行原方法,没有则抛出 `ForbiddenException` 异常。 `has(String authority)` 方法:
- 获取当前的 `HttpServletRequest` 对象。 - 从请求头中获取用户标识。
- 如果用户标识为特定的超级管理员(`userId == 1L` ),则认为具有权限。
- 否则通过调用远程服务 `sysMenuClient.selectPermsByUserId(userId)` 来获取用户的权限列表,并判断是否包含指定的权限。 总的来说,这段代码实现了基于注解和用户权限的访问控制功能。
package com.ruoyi.common.auth.aspect;
import java.lang.reflect.Method;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.auth.annotation.HasPermissions;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.ForbiddenException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.system.feign.RemoteMenuService;
import lombok.extern.slf4j.Slf4j;
这段代码定义了一个基于 `AspectJ` 的切面类 `PreAuthorizeAspect` ,用于进行权限控制的相关处理。 具体来说:
1. 导入了一些必要的类和包,包括反射相关的类、`HttpServletRequest` 、`AspectJ` 的注解、异常类、工具类和远程服务等。
2. 使用 `@Aspect` 注解表明这是一个切面,`@Component` 注解使该类成为 Spring 管理的组件。 3. 定义了一个环绕通知 `around` ,它会在被 `@HasPermissions` 注解修饰的方法执行前后进行处理。 - 首先获取方法上的 `@HasPermissions` 注解信息。 - 然后判断当前用户是否具有指定的权限。如果具有权限,则让被拦截的方法正常执行(`point.proceed()`);如果没有权限,则抛出 `ForbiddenException` 异常。
4. 还有一个 `has` 方法用于具体判断用户是否具有给定的权限。判断逻辑包括如果是超级管理员(用户 ID 为 1)则认为具有权限,否则通过调用远程服务 `RemoteMenuService` 来根据用户 ID 获取权限列表,并检查是否包含给定的权限。 总的来说,这段代码的作用是在方法级别实现权限控制的切面逻辑。
@Aspect
@Component
@Slf4j
以下是对这几个注解的解释:
`@Component`:这是 Spring 框架中的一个注解,用于将类标记为 Spring 管理的组件。被 `@Component` 注解的类会被 Spring 容器自动扫描并实例化,以便在应用中使用。 `
@Aspect`:通常用于面向切面编程(AOP)。它标记一个类为切面类,在这个类中可以定义各种切面逻辑,如前置通知、后置通知、环绕通知等。
`@Slf4j`:这是 `lombok` 库提供的一个注解。它会为类自动生成一个 `Logger` 对象,名为 `log`,方便在类中进行日志记录。
总的来说,这几个注解结合使用,可能是在构建一个结合了 Spring 管理、AOP 切面逻辑以及日志记录功能的组件。
public class PreAuthorizeAspect
{
@Autowired
private RemoteMenuService sysMenuClient;
在这段代码中:
`public class PreAuthorizeAspect`:定义了一个名为 `PreAuthorizeAspect` 的公共类。 `@Autowired`:这是 Spring 框架中的注解,用于自动装配(注入)依赖。在这里
,它表示将 `RemoteMenuService` 类型的对象 `sysMenuClient` 自动注入到当前类中,使得在这个类中可以直接使用 `sysMenuClient` 来调用其方法和访问属性。
RemoteMenuService是个啥?选中RemoteMenuService按F4
package com.ruoyi.system.feign;
import java.util.Set;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.ruoyi.common.constant.ServiceNameConstants;
import com.ruoyi.system.feign.factory.RemoteMenuFallbackFactory;
/**
* 菜单 Feign服务层
*
* @author zmr
* @date 2019-05-20
*/
@FeignClient(name = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteMenuFallbackFactory.class)
public interface RemoteMenuService
{
@GetMapping("menu/perms/{userId}")
public Set<String> selectPermsByUserId(@PathVariable("userId") Long userId);
}
这段代码定义了一个名为 `RemoteMenuService` 的 `Feign` 客户端接口,用于与名为 `ruoyi-system` 的服务进行通信,并定义了一个根据用户 ID 获取权限集合的接口方法。
为啥是ruoyi-system,选中ServiceNameConstants.SYSTEM_SERVICE按F4
package com.ruoyi.common.constant;
/**
* 服务名称
*/
public interface ServiceNameConstants
{
/**
* system模块
*/
String SYSTEM_SERVICE = "ruoyi-system";
}
`@FeignClient` 注解中的 `name` 属性指定了要调用的服务名称,即 `ruoyi-system` 。
我用Ctrl+Shift+r找到了ruoyi-system文件,那么除了找pom.xml还有其他办法找到ruoyi-system这个服务吗?
除了在 `pom.xml` 中,您还可能在以下几个地方找到与 `ruoyi-system` 相关的信息:
1. 项目的配置文件:例如 `application.properties` 或 `application.yml` ,可能会有关于服务的配置信息,包括服务的名称、地址等。
2. 项目的文档:如果项目有详细的文档,可能会在其中说明 `ruoyi-system` 的相关信息,例如其功能、依赖关系、配置方式等。
3. 项目的架构设计文档:如果存在,可能会描述各个模块和服务之间的关系,包括 `ruoyi-system` 。
4. 代码中的其他模块或类:可能在其他与服务调用或集成相关的代码部分会有关于 `ruoyi-system` 的更多信息或引用。
5. 项目的构建脚本或部署配置:比如 `Dockerfile` 、 `Kubernetes` 部署配置等,可能会提及相关服务。
当调用 `selectPermsByUserId` 方法时,`Feign` 会根据配置将请求发送到对应的服务,并处理返回结果。 如果调用过程中出现问题,会按照 `fallbackFactory` 指定的 `RemoteMenuFallbackFactory` 类中的逻辑进行处理,以提供回退机制,增强系统的稳定性和容错性。
我们继续解析这个@Fegin这个接口呗
@GetMapping("menu/perms/{userId}")
public Set<String> selectPermsByUserId(@PathVariable("userId") Long userId);
使用 `Spring` 的 `@GetMapping` 注解定义的 `HTTP GET` 接口方法。 `@GetMapping("menu/perms/{userId}")` 表示当接收到 `GET` 请求,且请求路径为 `menu/perms/` 后跟一个用户 ID(路径变量)时,会调用这个方法。
`public Set<String> selectPermsByUserId(@PathVariable("userId") Long userId)` 定义了方法的签名。`@PathVariable("userId")` 用于从请求路径中提取出用户 ID,并将其作为一个 `Long` 类型的参数传递给方法。方法返回一个 `String` 类型的集合。
往下面走继续看
com/ruoyi/common/auth/aspect/PreAuthorizeAspect.java
@Around("@annotation(com.ruoyi.common.auth.annotation.HasPermissions)")
public Object around(ProceedingJoinPoint point) throws Throwable
{
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
HasPermissions annotation = method.getAnnotation(HasPermissions.class);
if (annotation == null)
{
return point.proceed();
}
String authority = new StringBuilder(annotation.value()).toString();
if (has(authority))
{
return point.proceed();
}
else
{
throw new ForbiddenException();
}
}
这段代码定义了一个 `@Around` 环绕通知的方法。 `@Around("@annotation(com.ruoyi.common.auth.annotation.HasPermissions)")` 表示该环绕通知会应用在被 `@HasPermissions` 注解标注的方法上。
这句话通俗来讲就是:如果有某个方法被 `com.ruoyi.common.auth.annotation.HasPermissions` 这个注解标注了,那么在这个方法执行的前后,我(这里指这个 `@Around` 注解所定义的逻辑)就会介入并执行一些额外的操作。
还记的AOP的五种通知吗
: 1. `@Before`(前置通知): - 作用:在目标方法执行之前执行自定义的增强逻辑。 - 特点:无法阻止目标方法的执行,也无法获取目标方法的返回值。
2. `@After`(后置通知): - 作用:在目标方法执行之后执行,无论目标方法是否正常完成或抛出异常。 - 特点:无法获取目标方法的返回值,也无法改变目标方法的执行结果。
3. `@AfterReturning`(返回通知): - 作用:在目标方法正常执行并返回结果之后执行,可以获取到目标方法的返回值,并基于返回值进行额外的处理。 - 特点:只有当目标方法正常完成(没有抛出异常)时才会执行。
4. `@AfterThrowing`(异常通知): - 作用:当目标方法抛出异常时执行,可以获取到抛出的异常对象,并进行异常处理相关的操作。 - 特点:仅在目标方法抛出异常时被触发。
5. `@Around`(环绕通知): - 作用:环绕目标方法的执行,可以在目标方法执行前后进行自定义的处理逻辑。它可以决定是否执行目标方法、修改输入参数、获取返回值、处理异常等。 - 特点:具有最强的控制能力,可以完全控制目标方法的执行流程。
通过合理使用这些通知类型,可以在不修改原有业务代码的情况下,为目标方法添加各种横切关注点的处理逻辑,如日志记录、权限检查、事务管理等。
然后接着解释代码:
public Object around(ProceedingJoinPoint point) throws Throwable
{
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
HasPermissions annotation = method.getAnnotation(HasPermissions.class);
if (annotation == null)
{
return point.proceed();
}
String authority = new StringBuilder(annotation.value()).toString();
if (has(authority))
{
return point.proceed();
}
else
{
throw new ForbiddenException();
}
}
在方法内部,首先获取方法的相关信息和 `@HasPermissions` 注解。
如果方法上没有这个注解,就直接执行原方法(`point.proceed()`)。 如果获取到了注解,并通过 `has(authority)` 方法验证当前具有指定的权限(`authority`),也执行原方法;
否则,抛出 `ForbiddenException` 异常,表示没有权限。 总的来说,这段代码实现了一个基于权限注解的权限检查逻辑。
说白了,在这个 `around` 方法里呀,它先从传进来的那个 `point` 里拿到方法的一些信息。比如说先拿到签名,然后从签名里搞到方法签名,再从方法签名里拿到具体的方法。接着看看这个方法有没有 `HasPermissions` 这个注解。 要是没有这个注解,那就直接让原来的方法正常运行(通过 `point.proceed()` )。 要是有这个注解,就把注解里设定的值弄出来放到 `authority` 里。然后再去检查有没有这个权限(通过 `has(authority)` 这个不知道具体是啥的检查)。 要是有权限,就让原来的方法正常运行;要是没权限,就抛出一个表示禁止访问的异常。
以上代码用到了has这个方法
private boolean has(String authority)
{
// 用超管帐号方便测试,拥有所有权限
HttpServletRequest request = ServletUtils.getRequest();
String tmpUserKey = request.getHeader(Constants.CURRENT_ID);
if (Optional.ofNullable(tmpUserKey).isPresent())
{
Long userId = Long.valueOf(tmpUserKey);
log.debug("userid:{}", userId);
if (userId == 1L)
{
return true;
}
return sysMenuClient.selectPermsByUserId(userId).stream().anyMatch(authority::equals);
}
return false;
}
以下是对 `has` 方法的通俗解释: 这个 `has` 方法主要是用来判断是否具有某个指定的权限。 首先,它通过 `ServletUtils.getRequest` 拿到当前的 `HttpServletRequest` 对象。然后从请求头中获取一个叫 `Constants.CURRENT_ID` 的值,把它转换成 `Long` 类型的 `userId` 。 如果能成功获取到 `userId` ,并且这个 `userId` 是 1 ,就认为有权限,直接返回 `true` 。 如果 `userId` 不是 1 ,就调用 `sysMenuClient.selectPermsByUserId(userId)` 去获取这个用户拥有的权限列表,然后检查这个列表中是否存在当前要判断的 `authority` 权限,如果存在就返回 `true` ,不存在就返回 `false` 。 如果获取 `userId` 失败,就直接返回 `false` ,表示没有权限。
总之,本篇文章我们讲解了com/ruoyi/common/auth/annotation/HasPermissions.java和com/ruoyi/common/auth/aspect/PreAuthorizeAspect.java这两个说白了就是整合了一个注解,将来把这个注解作用在方法上,实现验证
在这个示例中:
- `PreAuthorizeAspect` 这个类整体被称为切面。 -
`@Around("@annotation(com.ruoyi.common.auth.annotation.HasPermissions)")` 标注的 `around` 方法是通知。通俗来讲,通知可以理解为一种 “信号” 或 “触发机制”。当满足通知设置的条件(比如遇到了特定的切点)时,就表示通知被触发了,然后会执行切面中定义的相应逻辑。
这个例子中,当遇到被 HasPermissions
注解修饰的方法(也就是切点)时,@Around
这个通知就被触发了,然后会执行 around
方法里定义的权限检查等逻辑。通知注解要实行切面里的逻辑了,通知触发了切面中定义的与该通知相关的逻辑的执行。
- 被 `@annotation(com.ruoyi.common.auth.annotation.HasPermissions)` 匹配到的、被 `HasPermissions` 注解修饰的方法就是切点。
连接点可以理解为程序执行过程中所有可能被 HasPermissions
注解修饰的方法的执行点。
例如,如果有多个方法都被 HasPermissions
注解修饰了,那么每一个这样的方法的执行时刻都可以被视为一个连接点。但在这个切面中,只有被切点匹配到的连接点(即被指定注解修饰的方法的执行点)才会触发通知(around
方法)的执行。
总结来说,切面是包含了通知逻辑的类,通知是具体的增强处理逻辑(在这个例子中就是 `around` 方法里的处理),切点是定义了在哪些具体位置应用通知的点。
在这个示例中,Spring AOP 会创建一个代理对象来代理被 `@HasPermissions` 注解修饰的方法所属的目标对象。
具体来说,当调用被注解修饰的方法(比如 `performAdminOperation` 方法)时,实际上是由 Spring 创建的代理对象来处理这个调用。代理对象会在方法执行前后插入切面中定义的权限检查等增强逻辑。 所以可以说是代理对象代理了包含被注解修饰方法的目标对象。
我们下期再见!