全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-18 8 分钟 ✍️ juanwangdev

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值应大于登录拦截器
  • 权限变更后需刷新缓存

📝 发现内容有误?点击此处直接编辑

← 上一篇 Spring MVC 拦截器实现日志记录
下一篇 → Spring MVC 拦截器实现登录验证
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库