You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Apache Shiro的Subject中获取权限列表并缓存

解决Shiro权限列表缓存问题

方案一:自定义JdbcRealm,重写授权方法存入会话

默认JdbcRealm的doGetAuthorizationInfo方法会查询权限但不会主动存入会话,你可以通过继承该类实现需求:

  1. 编写自定义Realm类
public class CustomJdbcRealm extends JdbcRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 调用父类获取包含权限的授权信息
        AuthorizationInfo authInfo = super.doGetAuthorizationInfo(principals);
        
        // 获取当前用户会话
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        
        // 提取权限集合并转为List存入会话
        Set<String> permissionSet = ((SimpleAuthorizationInfo) authInfo).getStringPermissions();
        List<String> permissionList = new ArrayList<>(permissionSet);
        session.setAttribute("permissonList", permissionList);
        
        return authInfo;
    }
}
  1. 修改Shiro配置替换Realm
    把配置里的默认JdbcRealm全类名替换为你自定义的Realm路径,其余配置保持不变:
# Configure JDBC realm datasource.
jdbcRealm = com.yourpackage.CustomJdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.authenticationQuery = select password FROM user where UPPER(email)=UPPER(?) and status = 'ACTIVE'
jdbcRealm.userRolesQuery = SELECT r.unique_name FROM permission_role_employee pe JOIN permission_role r ON pe.permission_role_fk  = r.permission_role_id JOIN employee e ON pe.employee_fk = e.employee_id JOIN user u ON e.user_fk = u.user_id WHERE UPPER(u.email)=UPPER(?) AND pe.delete_flag = false
jdbcRealm.permissionsQuery = SELECT p.unique_name FROM permission_role_object po JOIN permission p ON po.permission_fk  = p.permission_id JOIN permission_role r ON po.permission_role_fk = r.permission_role_id WHERE UPPER(r.unique_name)=UPPER(?) AND po.delete_flag = false
jdbcRealm.dataSource = $ds
jdbcRealm.credentialsMatcher = $sha512Matcher

# Token Login的Realm同理替换
tcRealm = com.yourpackage.CustomJdbcRealm
tcRealm.permissionsLookupEnabled = true
tcRealm.authenticationQuery = SELECT token FROM api_token WHERE token = ?
tcRealm.userRolesQuery = SELECT r.unique_name FROM permission_role_employee pe JOIN permission_role r ON pe.permission_role_fk  = r.permission_role_id JOIN employee e ON pe.employee_fk = e.employee_id JOIN api_token t ON t.employee_fk = e.employee_id WHERE UPPER(t.token)=UPPER(?) AND t.delete_flag = false
tcRealm.permissionsQuery = SELECT p.unique_name FROM permission_role_object po JOIN permission p ON po.permission_fk  = p.permission_id JOIN permission_role r ON po.permission_role_fk = r.permission_role_id WHERE UPPER(r.unique_name)=UPPER(?) AND po.delete_flag = false
tcRealm.dataSource = $ds

方案二:登录后主动触发授权并存入会话

如果不想修改Realm,可在用户登录成功后主动触发授权流程,提取权限存入会话:

// 登录成功后执行以下代码
Subject currentUser = SecurityUtils.getSubject();
// 触发授权(调用任意权限/角色检查方法,触发doGetAuthorizationInfo执行)
currentUser.hasRole("任意角色标识");

// 从Realm中获取授权信息
AuthorizationInfo authInfo = ((RealmSecurityManager) SecurityUtils.getSecurityManager())
        .getRealms().stream()
        .filter(realm -> realm instanceof JdbcRealm)
        .findFirst()
        .map(realm -> realm.getAuthorizationInfo(currentUser.getPrincipals()))
        .orElse(null);

if (authInfo != null) {
    Set<String> permissionSet = ((SimpleAuthorizationInfo) authInfo).getStringPermissions();
    List<String> permissionList = new ArrayList<>(permissionSet);
    currentUser.getSession().setAttribute("permissonList", permissionList);
}

缓存权限的使用

后续渲染左侧菜单时,直接从会话中取出permissonList,遍历检查菜单项权限是否存在于列表中即可,无需再调用Shiro的hasPermission方法,避免重复数据库查询。


内容的提问来源于stack exchange,提问作者vished2000

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 07:00:23