權限控制是企業級 OA 系統的核心基礎設施。本文展示一套經典的**「登錄時加載權限 → Session 存儲 → 頁面標籤判斷 → 動態渲染功能模塊」**的輕量級權限實現方案,無需重型框架(Shiro/Spring Security),適合中小型項目快速落地。
session.setAttribute("permissions", permissionSet)<c:if test="${permissions.contains('sys:user:add')}">public Set<String> loadUserPermissions(Long userId) {
// 1. 獲取用戶角色
List<Role> roles = roleMapper.selectByUserId(userId);
if (roles.isEmpty()) return Collections.emptySet();
// 2. 獲取角色關聯權限 (去重)
Set<String> permissions = new HashSet<>();
for (Role role : roles) {
List<Permission> perms = permissionMapper.selectByRoleId(role.getId());
perms.forEach(p -> permissions.add(p.getCode())); // 如 "sys:user:add"
}
return permissions;
}
@PostMapping("/login")
public Result login(@RequestBody LoginDTO dto, HttpSession session) {
User user = userService.authenticate(dto.getUsername(), dto.getPassword());
if (user == null) return Result.fail("用戶名或密碼錯誤");
// 核心:加載權限並存 Session
Set<String> permissions = permissionService.loadUserPermissions(user.getId());
session.setAttribute("user", user);
session.setAttribute("permissions", permissions); // 權限標識集合
session.setAttribute("menus", menuService.buildUserMenus(user.getId())); // 可選:菜單樹
return Result.ok("登錄成功");
}
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<c:if test="${permissions.contains('sys:user:add')}">
<a href="/user/add" class="btn btn-primary">新增用戶</a>
</c:if>
<c:if test="${permissions.contains('sys:user:delete')}">
<button class="btn btn-danger" onclick="batchDelete()">批量刪除</button>
</c:if>
<a th:if="${#sets.contains(session.permissions, 'sys:user:add')}"
href="/user/add" class="btn btn-primary">新增用戶</a>
<button th:if="${#sets.contains(session.permissions, 'sys:user:delete')}"
class="btn btn-danger" onclick="batchDelete()">批量刪除</button>
後端:登錄成功返回 permissions: ["sys:user:add", "sys:user:delete", ...],前端存入 Vuex/Pinia/Redux/Context。
前端全局指令:
// Vue 3 自定義指令 v-permission
app.directive('permission', {
mounted(el, binding) {
const permissions = store.state.user.permissions;
if (!permissions.includes(binding.value)) {
el.parentNode?.removeChild(el); // 或 el.style.display = 'none'
}
}
});
頁面使用:
<a v-permission="'sys:user:add'" href="/user/add">新增用戶</a> <button v-permission="'sys:user:delete'" @click="batchDelete">批量刪除</button>
beforeEach 攔截跳轉,檢查目標路由 meta.permission 是否在用戶權限集合中,無權重定向 403 頁。| 優點 | 缺點 | 優化方案 |
|---|---|---|
| 實現簡單、無額外依賴、讀取極快 | 權限變更需重新登錄生效 | 提供「刷新權限」接口,管理員操作後調用;或定時同步 |
| 天然支持集群 | Session 複製/Redis 存儲增加開銷 | 權限集合體積小(幾 KB),影響可忽略;大權限集可壓縮存儲 |
| 無法精細到數據行級權限 | 行級權限需在 Service/SQL 層動態拼接條件,不適合前端判斷 |
「登錄加載 → Session 存儲 → 前端判斷渲染」是權限控制的**最小可行性方案(MVP)**。它以極低複雜度解決 80% 的菜單/按鈕顯隱需求。隨著業務增長,可平滑演進:引入 **Spring Security** 統一認證授權、**RBAC/ABAC 模型** 精細建模、**OAuth2/JWT** 支持微服務無狀態認證、**動態權限刷新** 解決即時生效。但無論架構如何升級,**核心邏輯始終是:認證主體 → 獲取權限集 → 決策點判斷 → 允許/拒絕**。