如何从Apache Shiro的Subject中获取权限列表并缓存
解决Shiro权限列表缓存问题
方案一:自定义JdbcRealm,重写授权方法存入会话
默认JdbcRealm的doGetAuthorizationInfo方法会查询权限但不会主动存入会话,你可以通过继承该类实现需求:
- 编写自定义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; } }
- 修改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
相关产品推荐
相关产品推荐

