赞
踩
之前在项目中通过自定义拦截器+自定义注解进行权限校验,拦截器代码过于臃肿!!!
于是想到了使用面向切面的方法!!
Aspect Oreinted Programming 面向切面编程,通过预编译方式或者运行时动态代理的方式,实现程序功能的统一管理和维护的一种技术(AOP是一种思想,并不依赖于某个框架或者编程语言实现)。
利用AOP可以对 业务逻辑的各部分进行隔离,使程序员更加专注于业务核心逻辑,从而降低代码的耦合度,提供程序可重用性,提高开发的效率(主要应用场景:权限控制,日志记录,性能统计,事务管理,异常处理)
1.目标对象
指需要被 增强的对象,Spring Aop通过代理增强实现 (target)
2.连接点(JoinPoint)
指的是被切面拦截到的点,在Spring当中指的是具体的方法.
3.切入点(pointcut)
表示一组连接点,通过正则表达式,通配符,aspectj切点表达式来进行定义和集中,定义了通知(advice)将要发生的地方. 简单的说:切入点就是我们对 哪些连接点 进行拦截的 定义。
4.通知:(advice)
通知指的是 拦截到连接点之后 要做的事情就是通知(功能增强) 按照分类:前置通知,后置通知,异常通知,环绕通知,最终通知。 前置通知:在目标对象的业务逻辑功能执行之前,发生. 通常用于:日志记录 权限控制
后置返回通知:发生目标功能对象的业务逻辑正常执行完之后,发生. 通常用于:对方法返回值进行处理.
后置异常通知:在目标对象的业务逻辑发生异常时发生 通常用于:项目的异常日志.
后置:不管目标对象的业务逻辑是否发异常,都会被执行. 通常用于:资源释放.
环绕通知:(使用最多),在目标对象的业务逻辑执行之前和之后发生。 通常用于:性能监控,事务管理.
顺序:环绕前置>普通前置>目标方法执行>环绕正常结束/出现异常>环绕后置>普通后置>普通返回或者异常。
5.切面: 切面指的是切入点(多个)和通知(多个)的结合
第一步:引入aop pom依赖
- <!--aop-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
第二步:编写一个自定义注解
- package com.tg.admin.common.annotation;
-
- import java.lang.annotation.*;
-
- /**
- * @Program: admin
- * @ClassName RequiresPermission
- * @Author: liutao
- * @Description:
- * @Create: 2023-03-20 08:11
- * @Version 1.0
- **/
-
- @Target({ElementType.TYPE, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface RequiresPermission {
- String roles() default " ";
- String permissions() default " ";
- }
第三步:编写一个切面
- package com.tg.admin.common.aop;
-
- import cn.hutool.core.util.StrUtil;
- import com.auth0.jwt.JWT;
- import com.auth0.jwt.exceptions.JWTDecodeException;
- import com.tg.admin.common.Constants;
- import com.tg.admin.common.annotation.RequiresPermission;
- import com.tg.admin.common.exception.ServiceException;
- import com.tg.admin.entity.User;
- import com.tg.admin.entity.vo.BtnVo;
- import com.tg.admin.service.UserService;
- import com.tg.admin.utils.MenuUtil;
- import com.tg.admin.utils.RedisUtil;
- import lombok.extern.slf4j.Slf4j;
- 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.annotation.Pointcut;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- import org.springframework.web.context.request.RequestContextHolder;
- import org.springframework.web.context.request.ServletRequestAttributes;
-
- import javax.servlet.http.HttpServletRequest;
- import java.lang.reflect.Method;
- import java.util.Arrays;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Objects;
- import java.util.stream.Collectors;
-
- /**
- * @Program: tg-admin
- * @ClassName PermissionChech
- * @Author: liutao
- * @Description: 角色、权限校验切面
- * @Create: 2023-06-20 18:18
- * @Version 1.0
- **/
- @Slf4j
- @Aspect
- @Component
- public class PermissionCheck {
- @Autowired
- private MenuUtil menuUtil;
- @Autowired
- private RedisUtil redisUtil;
- @Autowired
- private UserService userService;
-
-
- /***
- * @MethodName: permissionCheckPointCut
- * @description: 定义一个切点
- * @Author: LiuTao
- * @UpdateTime: 2023/6/20 19:34
- **/
- @Pointcut("@annotation(com.tg.admin.common.annotation.RequiresPermission)")
- public void permissionCheckPointCut() {
-
- }
-
- /***
- * @MethodName: check
- * @description: 环绕通知
- * @Author: LiuTao
- * @Param: [pjp]
- * @UpdateTime: 2023/6/20 19:34
- * @Return: java.lang.Object
- * @Throw: Throwable
- **/
- @Around("permissionCheckPointCut()")
- public Object check(ProceedingJoinPoint pjp) throws Throwable {
- // 获取请求对象
- HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
- // 记录日志
- log.info("===============系统操作日志===============");
- Signature signature = pjp.getSignature();
- // 请求的类
- String className = pjp.getTarget().getClass().getName();
- String methodName = signature.getName();
- log.info("请求方式:{}", request.getMethod());
- log.info("请求ip:{}", request.getRemoteAddr());
- log.info("请求类方法:{}", signature);
- log.info("请求参数:{}", Arrays.toString(pjp.getArgs()));
- // 权限注解校验
- MethodSignature handlerMethod = (MethodSignature) signature;
- Method method = handlerMethod.getMethod();
- if (method.isAnnotationPresent(RequiresPermission.class)) {
- RequiresPermission auth = method.getAnnotation(RequiresPermission.class);
- String roles = auth.roles();
- String permissions = auth.permissions();
-
- String token = request.getHeader("token");
- // 认证
- if (StrUtil.isBlank(token)) {
- throw new ServiceException(Constants.CODE_401, "请登录!!!");
- }
- String id;
- try {
- id = JWT.decode(token).getAudience().get(0);
- } catch (JWTDecodeException jwtDecodeException) {
- throw new ServiceException(Constants.CODE_401, "token验证失败,请重新登录");
- }
- User user = userService.getById(id);
- // 校验角色
- if (StrUtil.isNotBlank(roles)) {
- if (!Arrays.asList(roles.split(",")).contains(user.getRole())) {
- throw new ServiceException(Constants.CODE_403, "当前角色权限不足");
- }
- }
- // 校验权限
- if (StrUtil.isNotBlank(permissions)) {
- List<String> userPermissions = menuUtil
- .getPermissions(user.getRole())
- .stream()
- .map(BtnVo::getPermission)
- .collect(Collectors.toList());
- if (!new HashSet<>(userPermissions).containsAll(Arrays.asList(permissions.split(",")))) {
- throw new ServiceException(Constants.CODE_401, "无权限访问资源");
- }
- }
- }
- return pjp.proceed();
- }
-
- }
第四步:在web层使用自定义注解
- @RequiresPermission(roles = "ROLE_ADMIN")
- @ApiOperation(value = "查询所有用户", httpMethod = "GET")
- @GetMapping
- public Result<User> findAll() {
- List<User> list = userService.findAll();
- log.info("{}", list);
- return Result.success(list);
- }
-
- @RequiresPermission(permissions = "user:list:page")
- @ApiOperation(value = "分页查询所有用户信息", httpMethod = "GET")
- @GetMapping("/page")
- public Result<User> findPage(@RequestParam Integer pageNum,
- @RequestParam Integer pageSize,
- @RequestParam String username,
- @RequestParam String email,
- @RequestParam String address) {
- IPage<User> page = new Page<>(pageNum, pageSize);
- QueryWrapper<User> queryWrapper = new QueryWrapper<>();
- if (!"".equals(username)) {
- queryWrapper.like("username", username);
- }
- if (!"".equals(email)) {
- queryWrapper.like("email", email);
- }
- if (!"".equals(address)) {
- queryWrapper.like("address", address);
- }
- User currentUser = JwtUtil.getCurrentUser();
- System.out.println("当前用户------" + currentUser);
- return Result.success(userService.page(page, queryWrapper));
- }
使用ROLE_USER 用户访问
然后我们用ROLE_ADMIN 用户访问
最后完美撒花!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。