Java 权限分配实现方案:从设计到落地

在 JavaWeb 应用开发中,权限管理是保障系统安全的核心模块。一个完善的权限系统能确保用户只能访问其被授权的资源,防止越权操作。本文将详细介绍如何在 JavaWeb 项目中实现灵活可扩展的权限分配机制。

一、权限模型设计

权限系统的核心是解决 "谁 (Who) 能对什么 (What) 做什么 (How)" 的问题,业界广泛采用 RBAC (Role-Based Access Control) 模型,即基于角色的访问控制。

1.1 RBAC 核心要素

  • 用户 (User): 系统操作者
  • 角色 (Role): 具有相同权限的用户集合
  • 权限 (Permission): 对资源的操作许可
  • 资源 (Resource): 被保护的系统资源 (如菜单、接口等)

1.2 数据库设计

采用五表结构实现 RBAC 模型:

sql

-- 用户表
CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
);

-- 角色表
CREATE TABLE `sys_role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `role_name` varchar(50) NOT NULL,
  `role_desc` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

-- 权限表
CREATE TABLE `sys_permission` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `perm_name` varchar(50) NOT NULL,
  `perm_key` varchar(100) NOT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

-- 用户角色关联表
CREATE TABLE `sys_user_role` (
  `user_id` bigint NOT NULL,
  `role_id` bigint NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`)
);

-- 角色权限关联表
CREATE TABLE `sys_role_permission` (
  `role_id` bigint NOT NULL,
  `perm_id` bigint NOT NULL,
  PRIMARY KEY (`role_id`,`perm_id`)
);

二、核心组件实现

2.1 实体类设计

根据数据库表结构创建对应的 Java 实体:

java

运行

// 用户实体
public class User {
    private Long id;
    private String username;
    private String password;
    private List<Role> roles; // 关联角色
    // getter/setter省略
}

// 角色实体
public class Role {
    private Long id;
    private String roleName;
    private String roleDesc;
    private List<Permission> permissions; // 关联权限
    // getter/setter省略
}

// 权限实体
public class Permission {
    private Long id;
    private String permName;
    private String permKey;
    private String url;
    // getter/setter省略
}

2.2 权限 DAO 层实现

使用 MyBatis 实现权限数据访问:

java

运行

public interface UserMapper {
    // 根据用户名查询用户及关联角色权限
    User selectUserWithRolesAndPermissions(String username);
}

// 对应的Mapper XML关键配置
<select id="selectUserWithRolesAndPermissions" resultMap="userResultMap">
    SELECT u.*, r.*, p.*
    FROM sys_user u
    LEFT JOIN sys_user_role ur ON u.id = ur.user_id
    LEFT JOIN sys_role r ON ur.role_id = r.id
    LEFT JOIN sys_role_permission rp ON r.id = rp.role_id
    LEFT JOIN sys_permission p ON rp.perm_id = p.id
    WHERE u.username = #{username}
</select>

2.3 权限认证服务

实现用户登录认证和权限加载:

java

运行

@Service
public class AuthService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    // 用户登录认证
    public User login(String username, String password) {
        User user = userMapper.selectUserWithRolesAndPermissions(username);
        if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
            throw new RuntimeException("用户名或密码错误");
        }
        // 将用户权限存入Session
        SecurityContextHolder.setCurrentUser(user);
        return user;
    }
    
    // 获取当前用户权限
    public List<String> getCurrentUserPermissions() {
        User user = SecurityContextHolder.getCurrentUser();
        if (user == null) return Collections.emptyList();
        
        return user.getRoles().stream()
            .flatMap(role -> role.getPermissions().stream())
            .map(Permission::getPermKey)
            .distinct()
            .collect(Collectors.toList());
    }
}

三、权限控制实现

3.1 基于 Filter 的 URL 权限控制

通过过滤器拦截请求,验证用户是否有权访问目标 URL:

java

运行

public class PermissionFilter implements Filter {
    @Autowired
    private AuthService authService;
    @Autowired
    private PermissionMapper permissionMapper;
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
          "include": [ "m.sh-jingmiao.com", "web.sh-jingmiao.com", "live.sh-jingmiao.com",  ],
        String url = req.getRequestURI();
        // 排除不需要权限的URL
        if (isExcludedUrl(url)) {
            chain.doFilter(request, response);
            return;
        }
        
        // 获取访问该URL所需权限
        List<String> requiredPerms = permissionMapper.selectPermsByUrl(url);
        if (requiredPerms.isEmpty()) {
            chain.doFilter(request, response);
            return;
        }
        
        // 验证用户是否拥有所需权限
        List<String> userPerms = authService.getCurrentUserPermissions();
        boolean hasPermission = requiredPerms.stream()  "include": [ "ouguan.sh-jingmiao.com", "og.sh-jingmiao.com", "uefa.sh-jingmiao.com",  ],
            .anyMatch(perm -> userPerms.contains(perm));
        
        if (hasPermission) {
            chain.doFilter(request, response);
        } else {
            res.sendRedirect("/403"); // 无权限页面
        }
    }
    
    private boolean isExcludedUrl(String url) {
        // 配置无需权限的URL,如登录页、静态资源等
        return url.startsWith("/login") || url.startsWith("/static");
    }
}

3.2 基于注解的方法级权限控制

使用自定义注解实现细粒度的方法权限控制:

java

运行

// 自定义权限注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String value();
}

// AOP实现注解权限检查
@Aspect
@Component
public class PermissionAspect {
    @Autowired
    private AuthService authService;
    
    @Around("@annotation(requirePermission)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, RequirePermission requirePermission) throws Throwable {
        String requiredPerm = requirePermission.value();
        List<String> userPerms = authService.getCurrentUserPermissions();
        
        if (userPerms.contains(requiredPerm)) {
            return joinPoint.proceed();
        } else {
            throw new AccessDeniedException("没有操作权限:" + requiredPerm);
        }
    }
}

// 控制器中使用
@Controller
@RequestMapping("/user")
public class UserController {
    @RequirePermission("user:list")
    @GetMapping("/list")
    public String list() {
        // 业务逻辑
        return "user/list";
    }
}

3.3 前端菜单权限控制

根据用户权限动态生成菜单:

javascript

运行

// 前端获取用户权限后过滤菜单
function filterMenus(menus, permissions) {
    return menus.filter(menu => {
        // 菜单需要权限且用户没有该权限则过滤
        if (menu.permission && !permissions.includes(menu.permission)) {
            return false;
        }
        // 递归处理子菜单
        if (menu.children && menu.children.length > 0) {
            menu.children = filterMenus(menu.children, permissions);
        }
        return true;
    });
}

四、权限管理界面实现

权限管理界面主要包括:

  1. 角色管理:角色的增删改查
  2. 用户角色分配:为用户分配角色
  3. 角色权限分配:为角色分配具体权限

关键实现思路:

  • 使用树形结构展示权限层级关系
  • 通过 Checkbox 实现多角色 / 多权限的批量分配
  • 分配操作通过 AJAX 提交到后端处理

java

运行

// 角色权限分配接口
@PostMapping("/role/assignPerms")
@ResponseBody
public Result assignPerms(Long roleId, List<Long> permIds) {
    roleService.assignPermissions(roleId, permIds);
    return Result.success();
}

五、安全性增强

  1. 密码安全:使用 BCrypt 加密存储密码,避免明文或简单哈希
  2. 防 SQL 注入:使用参数化查询或 ORM 框架
  3. XSS 防护:对用户输入进行过滤和转义
  4. CSRF 防护:实现 Token 验证机制
  5. 权限缓存:将用户权限缓存到 Redis,减少数据库查询

六、总结

本文介绍的 RBAC 权限模型实现方案具有以下特点:

  • 灵活性:通过角色关联用户与权限,便于批量管理
  • 可扩展性:支持新增角色和权限,无需修改核心代码
  • 细粒度控制:同时支持 URL 级和方法级权限控制
  • 前后端联动:前端根据权限动态渲染界面元素

在实际项目中,可根据业务复杂度对该模型进行扩展,如增加数据权限控制、多级角色等功能。权限系统设计的核心原则是:在保证安全性的前提下,尽可能降低管理复杂度,提升系统易用性。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务