Spring MVC 拦截器实现权限控制
权限控制是系统安全的核心功能,拦截器结合注解可实现灵活细粒度的权限管理。
权限注解定义
Java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String value(); // 权限标识
String message() default "无操作权限";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireRole {
String[] value(); // 角色列表
String message() default "无访问权限";
}
权限拦截器实现
Java
@Component
public class PermissionInterceptor implements HandlerInterceptor {
@Autowired
private PermissionService permissionService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 检查权限注解
RequirePermission permAnnotation = method.getAnnotation(RequirePermission.class);
if (permAnnotation != null) {
return checkPermission(permAnnotation, request, response);
}
// 检查角色注解
RequireRole roleAnnotation = method.getAnnotation(RequireRole.class);
if (roleAnnotation != null) {
return checkRole(roleAnnotation, request, response);
}
return true;
}
private boolean checkPermission(RequirePermission annotation,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
User user = UserContext.getUser();
if (user == null) {
sendError(response, 401, "未登录");
return false;
}
String permission = annotation.value();
if (!permissionService.hasPermission(user.getId(), permission)) {
sendError(response, 403, annotation.message());
return false;
}
return true;
}
private boolean checkRole(RequireRole annotation,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
User user = UserContext.getUser();
if (user == null) {
sendError(response, 401, "未登录");
return false;
}
String[] roles = annotation.value();
if (!permissionService.hasAnyRole(user.getId(), roles)) {
sendError(response, 403, annotation.message());
return false;
}
return true;
}
private void sendError(HttpServletResponse response, int code, String message) throws Exception {
response.setStatus(code);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(String.format("{\"code\":%d,\"message\":\"%s\"}", code, message));
}
}
权限服务实现
Java
@Service
public class PermissionService {
@Autowired
private UserRepository userRepository;
@Autowired
private PermissionRepository permissionRepository;
@Autowired
private RoleRepository roleRepository;
public boolean hasPermission(Long userId, String permissionCode) {
User user = userRepository.findById(userId);
if (user == null) {
return false;
}
// 获取用户所有角色的权限
Set<String> permissions = permissionRepository.findPermissionsByUserId(userId);
return permissions.contains(permissionCode);
}
public boolean hasAnyRole(Long userId, String... roleCodes) {
Set<String> userRoles = roleRepository.findRolesByUserId(userId);
for (String role : roleCodes) {
if (userRoles.contains(role)) {
return true;
}
}
return false;
}
public boolean hasAllRoles(Long userId, String... roleCodes) {
Set<String> userRoles = roleRepository.findRolesByUserId(userId);
for (String role : roleCodes) {
if (!userRoles.contains(role)) {
return false;
}
}
return true;
}
}
Controller使用示例
Java
@RestController
@RequestMapping("/api/user")
public class UserController {
@RequirePermission("user:view")
@GetMapping("/list")
public List<User> listUsers() {
return userService.findAll();
}
@RequirePermission("user:create")
@PostMapping
public User createUser(@RequestBody UserDTO dto) {
return userService.create(dto);
}
@RequirePermission("user:update")
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody UserDTO dto) {
return userService.update(id, dto);
}
@RequirePermission("user:delete")
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
}
@RestController
@RequestMapping("/api/admin")
@RequireRole("ADMIN")
public class AdminController {
@GetMapping("/dashboard")
public Map<String, Object> dashboard() {
return adminService.getDashboard();
}
@RequireRole("SUPER_ADMIN")
@PostMapping("/system/config")
public void updateConfig(@RequestBody ConfigDTO dto) {
adminService.updateConfig(dto);
}
}
权限数据库设计
SQL
-- 用户表
CREATE TABLE user (
id BIGINT PRIMARY KEY,
username VARCHAR(50),
password VARCHAR(100),
status INT DEFAULT 1
);
-- 角色表
CREATE TABLE role (
id BIGINT PRIMARY KEY,
role_code VARCHAR(50),
role_name VARCHAR(100)
);
-- 权限表
CREATE TABLE permission (
id BIGINT PRIMARY KEY,
permission_code VARCHAR(100),
permission_name VARCHAR(100)
);
-- 用户角色关联表
CREATE TABLE user_role (
user_id BIGINT,
role_id BIGINT,
PRIMARY KEY (user_id, role_id)
);
-- 角色权限关联表
CREATE TABLE role_permission (
role_id BIGINT,
permission_id BIGINT,
PRIMARY KEY (role_id, permission_id)
);
权限缓存优化
Java
@Service
public class PermissionService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String PERMISSION_CACHE_KEY = "user:permission:";
private static final String ROLE_CACHE_KEY = "user:role:";
public boolean hasPermission(Long userId, String permissionCode) {
Set<String> permissions = getPermissionsFromCache(userId);
return permissions.contains(permissionCode);
}
private Set<String> getPermissionsFromCache(Long userId) {
String key = PERMISSION_CACHE_KEY + userId;
Set<String> permissions = (Set<String>) redisTemplate.opsForValue().get(key);
if (permissions != null) {
return permissions;
}
// 从数据库查询
permissions = permissionRepository.findPermissionsByUserId(userId);
// 存入缓存,30分钟过期
redisTemplate.opsForValue().set(key, permissions, 30, TimeUnit.MINUTES);
return permissions;
}
public void refreshUserPermission(Long userId) {
String permKey = PERMISSION_CACHE_KEY + userId;
String roleKey = ROLE_CACHE_KEY + userId;
redisTemplate.delete(permKey);
redisTemplate.delete(roleKey);
}
}
URL权限控制
Java
@Component
public class UrlPermissionInterceptor implements HandlerInterceptor {
@Autowired
private UrlPermissionService urlPermissionService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String uri = request.getRequestURI();
String method = request.getMethod();
User user = UserContext.getUser();
if (user == null) {
response.sendRedirect("/login");
return false;
}
// 检查URL权限
if (!urlPermissionService.hasUrlPermission(user.getId(), uri, method)) {
response.setStatus(403);
response.getWriter().write("{\"code\":403,\"message\":\"无访问权限\"}");
return false;
}
return true;
}
}
动态权限管理
Java
@RestController
@RequestMapping("/api/permission")
public class PermissionController {
@Autowired
private PermissionService permissionService;
@RequireRole("ADMIN")
@PostMapping("/assign")
public void assignPermission(@RequestBody PermissionAssignDTO dto) {
permissionService.assignRolePermission(dto.getRoleId(), dto.getPermissionIds());
}
@RequireRole("ADMIN")
@PostMapping("/role/assign")
public void assignRole(@RequestBody RoleAssignDTO dto) {
permissionService.assignUserRole(dto.getUserId(), dto.getRoleIds());
// 刷新用户权限缓存
permissionService.refreshUserPermission(dto.getUserId());
}
@RequireRole("ADMIN")
@GetMapping("/user/{userId}")
public Set<String> getUserPermissions(@PathVariable Long userId) {
return permissionService.getUserPermissions(userId);
}
}
拦截器配置
Java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private PermissionInterceptor permissionInterceptor;
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 登录拦截器 - order=1
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**", "/api/public/**")
.order(1);
// 权限拦截器 - order=2(在登录验证之后执行)
registry.addInterceptor(permissionInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**", "/api/public/**")
.order(2);
}
}
权限拦截器应在登录拦截器之后执行,确保已有用户信息。
要点总结
- 使用注解标记方法所需权限,实现细粒度控制
- 拦截器通过HandlerMethod获取方法注解信息
- 权限数据建议使用RBAC模型(用户-角色-权限)
- 使用Redis缓存权限数据,避免频繁查询数据库
- 权限拦截器order值应大于登录拦截器
- 权限变更后需刷新缓存
📝 发现内容有误?点击此处直接编辑