Implementing Dynamic Menu Based on User Authorities in a Web Application
1. Key Considerations for Implementing a Dynamic Menu
- User Authentication & Authorization
- Use Spring Security (for backend) to handle authentication and assign roles.
- Use JWT tokens, OAuth, or Session-based authentication for user verification.
- Role-Based or Permission-Based Access Control
- Role-based: Users are assigned predefined roles (e.g., ADMIN, USER, MODERATOR), and menus are controlled based on these roles.
- Permission-based: Each user has fine-grained permissions (e.g., CAN_VIEW_REPORTS, CAN_MANAGE_USERS) that determine menu visibility.
- Frontend Adaptation
- The frontend should dynamically render menu items based on the user’s roles or permissions.
- Menu items can be fetched from an API or stored locally in state management (e.g., React Context, Redux, Vuex).
2. Backend Implementation (Spring Boot Example)
Define User Roles and Authorities
Spring Security provides a way to define roles and authorities:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
@ElementCollection(fetch = FetchType.EAGER)
private Set<String> roles; // Example: ["ROLE_ADMIN", "ROLE_USER"]
}
@RestController
@RequestMapping("/api/menu")
public class MenuController {
@GetMapping
public ResponseEntity<MenuResponseDto> getUserMenu(@AuthenticationPrincipal UserDetails userDetails) {
String username = userDetails.getUsername();
List<MenuItem> menuItems = MenuService.getMenuForUser(userDetails.getAuthorities());
return ResponseEntity.ok(new MenuResponseDto(username, menuItems));
}
}
@Service
@RequiredArgsConstructor
public class MenuService {
private final MenuItemRepository menuItemRepository;
public List<MenuItem> getMenuForUser(Collection<? extends GrantedAuthority> authorities) {
Set<String> roleNames = authorities.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
return menuItemRepository.findByRoles(roleNames);
}
}
예시의 메뉴 DB 데이터
INSERT INTO menu_groups (name, order_index) VALUES ('Super Admin Menu', 1);
INSERT INTO menu_groups (name, order_index) VALUES ('Admin Menu', 2);
INSERT INTO menu_groups (name, order_index) VALUES ('User Menu', 3);
INSERT INTO menu_items (title, link, group_id, order_index) VALUES ('Dashboard', '/superadmin/dashboard', 1, 1);
INSERT INTO menu_items (title, link, group_id, order_index) VALUES ('User Management', '/superadmin/users', 1, 2);
INSERT INTO menu_items (title, link, group_id, order_index) VALUES ('Admin Panel', '/admin', 2, 1);
INSERT INTO menu_items (title, link, group_id, order_index) VALUES ('Manage Orders', '/admin/orders', 2, 2);
INSERT INTO menu_items (title, link, group_id, order_index) VALUES ('Home', '/home', 3, 1);
INSERT INTO menu_items (title, link, group_id, order_index) VALUES ('Profile', '/profile', 3, 2);
INSERT INTO menu_group_role (group_id, role) VALUES (1, 'ROLE_SUPER_ADMIN');
INSERT INTO menu_group_role (group_id, role) VALUES (2, 'ROLE_SUPER_ADMIN');
INSERT INTO menu_group_role (group_id, role) VALUES (3, 'ROLE_SUPER_ADMIN');
INSERT INTO menu_group_role (group_id, role) VALUES (2, 'ROLE_ADMIN');
INSERT INTO menu_group_role (group_id, role) VALUES (3, 'ROLE_ADMIN');
INSERT INTO menu_group_role (group_id, role) VALUES (3, 'ROLE_USER');