角色权限控制(RBAC)与动态菜单
实验目的
1.理解 RBAC(基于角色的访问控制)模型。
2.掌握用户-角色-权限的关系设计。
3.掌握基于 JWT 的权限控制实现方式。
4.实现接口权限校验(不同角色访问不同接口)。
5.实现动态菜单(根据权限返回菜单数据)。
实验环境
开发工具:IntelliJ IDEA
构建工具:Maven
测试工具:Postman或浏览器
AI工具:通义灵码
实验要求
在校园二手交易系统中实现权限控制功能:
1.实现用户、角色、权限三张核心表
2.登录后返回用户角色信息
3.不同角色访问不同接口
4.实现动态菜单接口(按权限返回)
5.未授权访问返回错误信息
实验内容
第一部分:RBAC模型设计
RBAC 核心关系:
用户(User) → 角色(Role) → 权限(Permission)
关系说明:
一个用户可以有多个角色
一个角色可以有多个权限
权限决定可以访问哪些接口
第二部分:数据库设计
1.用户表
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
password VARCHAR(100)
);
2.角色表(role)
CREATE TABLE role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50)
);
3.权限表(permission)
CREATE TABLE permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
path VARCHAR(100)
);
4.用户角色表(user_role)
CREATE TABLE user_role (
user_id BIGINT,
role_id BIGINT
);
5.角色权限表(role_permission)
CREATE TABLE role_permission (
role_id BIGINT,
permission_id BIGINT
);
6.示例数据
-- 用户
INSERT INTO user VALUES (1,'jack','123456');
INSERT INTO user VALUES (2,'lily','123456');
-- 角色
INSERT INTO role VALUES (1,'admin'),(2,'user');
-- 权限
INSERT INTO permission VALUES
(1,'查询商品','/goods/list'),
(2,'添加商品','/goods/add');
-- 用户角色
INSERT INTO user_role VALUES (1,1);
INSERT INTO user_role VALUES (2,2);
-- 角色权限
INSERT INTO role_permission VALUES (1,1),(1,2);
INSERT INTO role_permission VALUES (2,1);
第三部分:创建Mapper接口
- 创建UserMapper:
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
2.创建RoleMapper
@Mapper
public interface RoleMapper extends BaseMapper<Role> {
}
3.创建PermissionMapper
@Mapper
public interface PermissionMapper extends BaseMapper<Permission> {
}
4.创建UserRoleMapper
@Mapper
public interface PermissionMapper extends BaseMapper<Permission> {
}
5.创建RolePermissionMapper
@Mapper
public interface PermissionMapper extends BaseMapper<Permission> {
}
第四部分:创建DTO
- 创建MenuDTO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MenuDTO {
private String name;
private String path;
private String permission;
}
- 创建UserInfoDTO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfoDTO {
private Long userId;
private String username;
private List<String> roles;
private List<MenuDTO> menus;
}
第五部分:创建Service层
1.创建PermissionService
@Service
public class PermissionService {
@Autowired
private UserRoleMapper userRoleMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private RolePermissionMapper rolePermissionMapper;
@Autowired
private PermissionMapper permissionMapper;
public List<String> getUserRoles(Long userId) {
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId, userId);
List<UserRole> userRoles = userRoleMapper.selectList(wrapper);
List<Long> roleIds = userRoles.stream()
.map(UserRole::getRoleId)
.collect(Collectors.toList());
if (roleIds.isEmpty()) {
return new ArrayList<>();
}
List<Role> roles = roleMapper.selectBatchIds(roleIds);
return roles.stream()
.map(Role::getRoleName)
.collect(Collectors.toList());
}
public List<MenuDTO> getUserMenus(Long userId) {
List<String> roles = getUserRoles(userId);
if (roles.isEmpty()) {
return new ArrayList<>();
}
LambdaQueryWrapper<Role> roleWrapper = new LambdaQueryWrapper<>();
roleWrapper.in(Role::getRoleName, roles);
List<Role> roleList = roleMapper.selectList(roleWrapper);
List<Long> roleIds = roleList.stream()
.map(Role::getId)
.collect(Collectors.toList());
LambdaQueryWrapper<RolePermission> rpWrapper = new LambdaQueryWrapper<>();
rpWrapper.in(RolePermission::getRoleId, roleIds);
List<RolePermission> rolePermissions = rolePermissionMapper.selectList(rpWrapper);
List<Long> permissionIds = rolePermissions.stream()
.map(RolePermission::getPermissionId)
.distinct()
.collect(Collectors.toList());
if (permissionIds.isEmpty()) {
return new ArrayList<>();
}
List<Permission> permissions = permissionMapper.selectBatchIds(permissionIds);
return permissions.stream()
.map(p -> MenuDTO.builder()
.name(p.getName())
.path(p.getPath())
.permission(p.getName())
.build())
.collect(Collectors.toList());
}
public boolean hasPermission(Long userId, String permissionName) {
List<MenuDTO> menus = getUserMenus(userId);
return menus.stream()
.anyMatch(menu -> menu.getPermission().equals(permissionName));
}
}
2.创建RoleService
@Service
public class RoleService {
@Autowired
private RoleMapper roleMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Autowired
private RolePermissionMapper rolePermissionMapper;
public Long getRoleIdByName(String roleName) {
LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Role::getRoleName, roleName);
Role role = roleMapper.selectOne(wrapper);
return role != null ? role.getId() : null;
}
@Transactional
public void assignRoleToUser(Long userId, Long roleId) {
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId, userId);
userRoleMapper.delete(wrapper);
UserRole userRole = new UserRole();
userRole.setUserId(userId);
userRole.setRoleId(roleId);
userRoleMapper.insert(userRole);
}
@Transactional
public void assignPermissionToRole(Long roleId, Long permissionId) {
LambdaQueryWrapper<RolePermission> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RolePermission::getRoleId, roleId)
.eq(RolePermission::getPermissionId, permissionId);
if (rolePermissionMapper.selectCount(wrapper) == 0) {
RolePermission rolePermission = new RolePermission();
rolePermission.setRoleId(roleId);
rolePermission.setPermissionId(permissionId);
rolePermissionMapper.insert(rolePermission);
}
}
}
- 创建权限注解
第六部分:创建权限拦截器
- PermissionInterceptor
@Component
public class PermissionInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private PermissionService permissionService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
RequirePermission requirePermission = handlerMethod.getMethodAnnotation(RequirePermission.class);
if (requirePermission == null) {
requirePermission = handlerMethod.getBeanType().getAnnotation(RequirePermission.class);
}
if (requirePermission != null) {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
}
Long userId = jwtUtil.getUserIdFromToken(token);
String permission = requirePermission.value();
if (!permissionService.hasPermission(userId, permission)) {
throw new BusinessException(403, "没有权限访问此资源");
}
}
return true;
}
}
- 注册该拦截器
registry.addInterceptor(permissionInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/auth/login", "/auth/register");
第七部分:创建权限管理 PermissionController
RestController
@RequestMapping("/permission")
public class PermissionController {
@Autowired
private PermissionService permissionService;
@Autowired
private RoleService roleService;
@Autowired
private JwtUtil jwtUtil;
@GetMapping("/menus")
public Result<List<MenuDTO>> getUserMenus(HttpServletRequest request) {
String token = (String) request.getAttribute("token");
Long userId = jwtUtil.getUserIdFromToken(token);
List<MenuDTO> menus = permissionService.getUserMenus(userId);
return Result.success(menus);
}
@GetMapping("/userinfo")
public Result<UserInfoDTO> getUserInfo(HttpServletRequest request) {
String token = (String) request.getAttribute("token");
Long userId = jwtUtil.getUserIdFromToken(token);
String username = jwtUtil.getUsernameFromToken(token);
List<String> roles = permissionService.getUserRoles(userId);
List<MenuDTO> menus = permissionService.getUserMenus(userId);
UserInfoDTO userInfo = UserInfoDTO.builder()
.userId(userId)
.username(username)
.roles(roles)
.menus(menus)
.build();
return Result.success(userInfo);
}
@PostMapping("/assign-role")
public Result<String> assignRole(@RequestParam Long userId, @RequestParam Long roleId) {
roleService.assignRoleToUser(userId, roleId);
return Result.success("角色分配成功");
}
@PostMapping("/assign-permission")
public Result<String> assignPermission(@RequestParam Long roleId, @RequestParam Long permissionId) {
roleService.assignPermissionToRole(roleId, permissionId);
return Result.success("权限分配成功");
}
}
第八部分:测试功能
1. 登录获取 token(使用你数据库中的用户)
POST http://localhost:8080/auth/login
{
"username": "你的用户名",
"password": "你的密码"
}
2. 获取动态菜单
GET http://localhost:8080/permission/menus
Authorization: Bearer {token}
3. 获取用户信息(含角色和权限)
GET http://localhost:8080/permission/userinfo
Authorization: Bearer {token}
4. 测试权限控制
GET http://localhost:8080/goods/list # 需要"查看商品"权限
POST http://localhost:8080/goods/add # 需要"商品管理"权限