权限管理的实现方法有很多种,也有很多集成不错的框架。现在用AOP和自定义注解实现一个简单易理解的权限管理~
默认已经有做好了RBAC(role based access control),基于角色的访问控制。就是数据库中已经有了用户表,角色表,权限表,用户角色表,角色权限表。我们判断权限是取一个用户所有角色拥有权限的并集。
目录
权限判断注解
AOP 实现
获取用户权限服务层
使用权限注解
解释
总结
优化
权限判断注解
PermissionCheck.java
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 实现
java">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 {@Autowiredprivate 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
获取用户权限并集。- 检查用户是否具有所需权限,若不具有则抛出异常。
获取用户权限服务层
java">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 {@Autowiredprivate UserRepository userRepository;@Autowiredprivate RoleRepository roleRepository;@Autowiredprivate 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
中,自动去重,得到权限并集。
使用权限注解
java">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
方法:
java">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 来增强安全性和用户信息的传递,提高系统的安全性和可扩展性。