当前位置: 首页 > article >正文

Spring Boot中使用AOP实现权限管理

       权限管理的实现方法有很多种,也有很多集成不错的框架。现在用AOP和自定义注解实现一个简单易理解的权限管理~

       默认已经有做好了RBAC(role  based  access  control),基于角色的访问控制。就是数据库中已经有了用户表,角色表,权限表,用户角色表,角色权限表。我们判断权限是取一个用户所有角色拥有权限的并集。

目录

权限判断注解

 AOP 实现

获取用户权限服务层

使用权限注解

解释

总结

优化


权限判断注解

PermissionCheck.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 PermissionCheck {
    String value();
}

 AOP 实现

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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 java.lang.reflect.Method;


@Aspect
@Component
public class PermissionAspect {


    @Autowired
    private PermissionService permissionService;


    @Pointcut("execution(* *(..)) && @annotation(PermissionCheck)")
    public void permissionCheckPointcut() {}


    @Before("permissionCheckPointcut()")
    public void checkPermission() throws Throwable {
        MethodSignature signature = (MethodSignature) ProxyMethodInvocation.currentInvocation().getMethod().getSignature();
        Method method = signature.getMethod();
        PermissionCheck permissionCheck = method.getAnnotation(PermissionCheck.class);
        String requiredPermission = permissionCheck.value();


        String username = getUsernameFromRequest();
        Set<String> userPermissions = permissionService.getUserPermissions(username);


        if (!userPermissions.contains(requiredPermission)) {
            throw new RuntimeException("没有权限");
        }
    }


    private String getUsernameFromRequest() {
        // 从请求中获取用户名,例如从 token 或 session 中获取
        return "username";
    }
}

解释:

  • @Pointcut("execution(* *(..)) && @annotation(PermissionCheck)"):定义切点为带有 PermissionCheck 注解的所有方法。
  • @Before("permissionCheckPointcut()"):在这些方法执行前执行 checkPermission 方法。
  • MethodSignature signature = (MethodSignature) ProxyMethodInvocation.currentInvocation().getMethod().getSignature();:获取方法签名。
  • Method method = signature.getMethod();:获取被调用的方法。
  • PermissionCheck permissionCheck = method.getAnnotation(PermissionCheck.class);:获取方法上的 PermissionCheck 注解。
  • String requiredPermission = permissionCheck.value();:获取注解中的权限信息。
  • Set<String> userPermissions = permissionService.getUserPermissions(username);:调用 PermissionService 获取用户权限并集。
  • 检查用户是否具有所需权限,若不具有则抛出异常。

获取用户权限服务层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


@Service
public class PermissionService {


    @Autowired
    private UserRepository userRepository;


    @Autowired
    private RoleRepository roleRepository;


    @Autowired
    private PermissionRepository permissionRepository;


    public Set<String> getUserPermissions(String username) {
        User user = userRepository.findByUsername(username);
        List<Role> roles = roleRepository.findByUserId(user.getId());
        Set<String> permissions = new HashSet<>();
        for (Role role : roles) {
            List<Permission> rolePermissions = permissionRepository.findByRoleId(role.getId());
            for (Permission permission : rolePermissions) {
                permissions.add(permission.getName());
            }
        }
        return permissions;
    }
}

解释:

  • getUserPermissions 方法:
    • 根据用户名查找用户。
    • 通过用户查找其所有角色。
    • 遍历角色,找到每个角色对应的权限列表。
    • 将权限存储在 HashSet 中,自动去重,得到权限并集。

使用权限注解

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/api")
public class SampleController {


    @GetMapping("/createUser")
    @PermissionCheck("user::add")
    public String createUser() {
        // 执行创建用户的逻辑
        return "用户创建成功";
    }


    @GetMapping("/editUser")
    @PermissionCheck("user::edit")
    public String editUser() {
        // 执行编辑用户的逻辑
        return "用户编辑成功";
    }
}

解释

1. PermissionService

  • 使用 HashSet 存储用户权限,自动去重,确保权限列表中的元素唯一性。
  • 遍历用户的所有角色,将每个角色的权限添加到 HashSet 中,实现权限并集的计算。

2. PermissionAspect

  • 从方法注解中获取所需的权限信息。
  • 通过 PermissionService 获取用户权限并集。
  • 检查用户是否具有所需权限,若不具有则抛出异常。

总结

  • 服务层

    • PermissionService 负责根据用户查找其所有角色的权限,并将这些权限存储在 HashSet 中,实现权限并集的计算。
  • AOP 切面

    • 使用 PermissionAspect 在方法执行前检查用户是否具有所需权限。
  • 自定义注解

    • PermissionCheck 注解用于标记需要权限检查的方法,并传递权限信息。

优化

       在实际应用中,还可以进一步优化,例如使用缓存来提高权限查询的性能,避免频繁的数据库查询,以及使用更安全的认证和授权机制,如 JWT 等。

以下是使用 JWT 提取用户名的示例,用于 getUsernameFromRequest 方法:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;


private String getUsernameFromRequest() {
    String token = getTokenFromRequest();
    Claims claims = Jwts.parser()
 .setSigningKey("your_secret_key")
 .parseClaimsJws(token)
 .getBody();
    return claims.getSubject();
}


private String getTokenFromRequest() {
    // 从请求头中获取 JWT 令牌
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    String bearerToken = request.getHeader("Authorization");
    if (bearerToken.startsWith("Bearer ")) {
        return bearerToken.substring(7);
    }
    return null;
}

解释:

  • getUsernameFromRequest 方法:
    • 从请求中获取 JWT 令牌。
    • 解析 JWT 令牌获取 claims
    • 从 claims 中获取用户名(通常存储在 subject 字段)。

       这样可以根据用户的多个角色进行权限并集的计算和检查,同时使用 JWT 来增强安全性和用户信息的传递,提高系统的安全性和可扩展性。


http://www.kler.cn/a/506995.html

相关文章:

  • VD:生成a2l文件
  • 2013年IMO几何预选题第4题
  • C#上位机通过CAN总线发送bin文件
  • excel仅复制可见单元格,仅复制筛选后内容
  • qml LevelAdjust详解
  • React 中hooks之useLayoutEffect 用法总结以及与useEffect的区别
  • Python 的时间处理模块 datetime 详解
  • 图论1-问题 B: 算法7-4,7-5:图的遍历——深度优先搜索
  • 博图 linucx vmware
  • css 实现自定义虚线
  • QT 通过QAxObject与本地应用程序读取Excel内容
  • 汽车故障码U100187 LIN1Communication time out 解析和处理方法
  • 【50个服务器常见端口】
  • 【Linux】sed编辑器二
  • 基于华为云车牌识别服务设计的停车场计费系统【华为开发者空间-鸿蒙】
  • ArcGIS模拟风场(流场)
  • 《AI与鸿蒙Next:建筑设计可视化的革新力量》
  • 使用AKTools本地部署AKShare财经数据接口库
  • 《零基础Go语言算法实战》【题目 4-12】找到给定集合的所有子集
  • 【CSS】 ---- CSS 实现图片随鼠标移动局部放大特效
  • VSCode代理配置导致的SSL证书验证错误及解决方案
  • Conda的一些常用命令
  • 运行fastGPT 第二步 安装宝塔面板 用于管理安装docker和其文件
  • 1.15寒假作业
  • Hooks扩展
  • 【刷题笔记】滑动窗口单调队列题目