Apache Shiro 常用工具与 API 详解

Apache Shiro 2.2.1 常用工具与 API 详解:说明 Shiro 无独立命令行工具、需经 Java API 调用,逐类给出 SecurityManager、Subject、AuthenticationToken、Realm、Permission、Session 的接口签名(javap 实取)与已实测代码示例及运行回显,附认证异常清单与会话超时要点,全程实测。

Apache ShiroApache Shiro2026/10/12

Apache Shiro 常用工具与 API 详解

目标:详解 Apache Shiro 2.2.1 的核心 API 与工具使用。Shiro 以 jar 库形态提供,没有独立命令行程序,认证、授权、会话管理全部通过 Java API 完成。本文按类速查 SecurityManager、Subject、AuthenticationToken、Realm、Permission、Session 等关键接口,签名取自 shiro-core 2.2.1 的 javap 实测输出,代码示例均已在 Ubuntu 22.04.5 + OpenJDK 17 环境实测运行。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS
  • Java:OpenJDK 17.0.20.1(javap 为 JDK 自带工具)
  • 构建工具:Apache Maven 3.6.3
  • Shiro 版本:2.2.1(本地仓库 jar:~/.m2/repository/org/apache/shiro/shiro-core/2.2.1/shiro-core-2.2.1.jar)
  • 示例工程目录:/root/shiro-demo/
  • 演示账号:root、admin、alice(密码统一 q1w2e3r4)

二、工具概览

Shiro 没有 shiro 命令行可执行程序,全部能力通过 Java API 在应用代码中调用。实际使用方式是把 shiro-core 及其依赖放进 classpath(Maven 依赖或 java -cp),在自己的 Java 类中调用核心接口。

API 类别代表接口/类作用
安全管理org.apache.shiro.mgt.SecurityManager认证、授权、会话管理的总入口(框架侧)
主体操作org.apache.shiro.subject.Subject当前用户的登录、角色、权限、会话操作(应用侧)
认证凭据org.apache.shiro.authc.AuthenticationToken 及 UsernamePasswordToken封装登录提交的主体与凭据
账号数据源org.apache.shiro.realm.Realm 及 text.IniRealm对接账号/角色/权限数据并执行认证
权限模型org.apache.shiro.authz.Permission权限判定(implies 语义)
会话org.apache.shiro.session.Session会话读写、超时、touch/stop

用 JDK 自带的 javap 可直接查看接口签名(实测命令格式,jar 路径为本地仓库实取):

javap -cp ~/.m2/repository/org/apache/shiro/shiro-core/2.2.1/shiro-core-2.2.1.jar org.apache.shiro.subject.Subject

三、前置条件

  • 已安装 JDK 17 与 Maven,示例工程可编译运行(见《Apache Shiro 构建并运行示例应用指南》):

    cd /root/shiro-demo
    mvn -q compile
    
  • shiro-core 2.2.1 已随依赖下载到本地仓库(~/.m2/repository/org/apache/shiro/shiro-core/2.2.1/),javap 直接读取该 jar。

四、核心 API 详解

4.1 SecurityManager(安全管理总入口)

org.apache.shiro.mgt.SecurityManager 继承 Authenticator、Authorizer、SessionManager 三个接口(javap 实测输出):

public interface org.apache.shiro.mgt.SecurityManager extends org.apache.shiro.authc.Authenticator,org.apache.shiro.authz.Authorizer,org.apache.shiro.session.mgt.SessionManager {
  public abstract org.apache.shiro.subject.Subject login(org.apache.shiro.subject.Subject, org.apache.shiro.authc.AuthenticationToken) throws org.apache.shiro.authc.AuthenticationException;
  public abstract void logout(org.apache.shiro.subject.Subject);
  public abstract org.apache.shiro.subject.Subject createSubject(org.apache.shiro.subject.SubjectContext);
}
  • 属框架侧接口:应用一般不直接调用 SecurityManager,而是经 SecurityUtils/Subject 间接使用。
  • 实测:IniSecurityManagerFactory 加载 shiro.ini 后构建出的实现为 org.apache.shiro.mgt.DefaultSecurityManager,realm 为 iniRealm (org.apache.shiro.realm.text.IniRealm)(运行 ConfigDemo 的实测回显见《Apache Shiro 配置参数详解》第六章)。
  • 应用启动时的一次性装配(示例代码节选,实测可用):
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);

装配入口 org.apache.shiro.ini.IniSecurityManagerFactory 的签名(javap 实测节选):

public class org.apache.shiro.ini.IniSecurityManagerFactory extends org.apache.shiro.ini.IniFactorySupport<org.apache.shiro.mgt.SecurityManager> {
  public static final java.lang.String MAIN_SECTION_NAME;
  public static final java.lang.String SECURITY_MANAGER_NAME;
  public static final java.lang.String INI_REALM_NAME;
  public org.apache.shiro.ini.IniSecurityManagerFactory();
  public org.apache.shiro.ini.IniSecurityManagerFactory(org.apache.shiro.config.Ini);
  public org.apache.shiro.ini.IniSecurityManagerFactory(java.lang.String);
  public java.util.Map<java.lang.String, ?> getBeans();
  public void destroy();
}
  • 三个常量 MAIN_SECTION_NAME / SECURITY_MANAGER_NAME / INI_REALM_NAME 对应 [main] 段装配与默认 iniRealm 命名;构造器支持传 Ini 对象或 classpath: 路径字符串。

4.2 Subject(当前主体,应用侧最常用)

org.apache.shiro.subject.Subject 是应用代码操作认证/授权/会话的核心接口(javap 实测输出):

public interface org.apache.shiro.subject.Subject {
  public abstract java.lang.Object getPrincipal();
  public abstract org.apache.shiro.subject.PrincipalCollection getPrincipals();
  public abstract boolean isPermitted(java.lang.String);
  public abstract boolean isPermitted(org.apache.shiro.authz.Permission);
  public abstract boolean[] isPermitted(java.lang.String...);
  public abstract boolean[] isPermitted(java.util.List<org.apache.shiro.authz.Permission>);
  public abstract boolean isPermittedAll(java.lang.String...);
  public abstract boolean isPermittedAll(java.util.Collection<org.apache.shiro.authz.Permission>);
  public abstract void checkPermission(java.lang.String) throws org.apache.shiro.authz.AuthorizationException;
  public abstract void checkPermission(org.apache.shiro.authz.Permission) throws org.apache.shiro.authz.AuthorizationException;
  public abstract void checkPermissions(java.lang.String...) throws org.apache.shiro.authz.AuthorizationException;
  public abstract void checkPermissions(java.util.Collection<org.apache.shiro.authz.Permission>) throws org.apache.shiro.authz.AuthorizationException;
  public abstract boolean hasRole(java.lang.String);
  public abstract boolean[] hasRoles(java.util.List<java.lang.String>);
  public abstract boolean hasAllRoles(java.util.Collection<java.lang.String>);
  public abstract void checkRole(java.lang.String) throws org.apache.shiro.authz.AuthorizationException;
  public abstract void checkRoles(java.util.Collection<java.lang.String>) throws org.apache.shiro.authz.AuthorizationException;
  public abstract void checkRoles(java.lang.String...) throws org.apache.shiro.authz.AuthorizationException;
  public abstract void login(org.apache.shiro.authc.AuthenticationToken) throws org.apache.shiro.authc.AuthenticationException;
  public abstract boolean isAuthenticated();
  public abstract boolean isRemembered();
  public abstract org.apache.shiro.session.Session getSession();
  public abstract org.apache.shiro.session.Session getSession(boolean);
  public abstract void logout();
  public abstract <V> V execute(java.util.concurrent.Callable<V>) throws org.apache.shiro.subject.ExecutionException;
  public abstract void execute(java.lang.Runnable);
  public abstract <V> java.util.concurrent.Callable<V> associateWith(java.util.concurrent.Callable<V>);
  public abstract java.lang.Runnable associateWith(java.lang.Runnable);
  public abstract void runAs(org.apache.shiro.subject.PrincipalCollection) throws java.lang.NullPointerException, java.lang.IllegalStateException;
  public abstract boolean isRunAs();
  public abstract org.apache.shiro.subject.PrincipalCollection getPreviousPrincipals();
  public abstract org.apache.shiro.subject.PrincipalCollection releaseRunAs();
}

按用途分组:

分组方法说明
认证login、isAuthenticated、isRemembered、logout登录、状态查询与登出
身份getPrincipal、getPrincipals取当前主体(账号名)与主体集合
角色判定hasRole、hasRoles、hasAllRoles、checkRole、checkRolesis/has 系列返回布尔,check 系列失败抛异常
权限判定isPermitted、isPermittedAll、checkPermission、checkPermissions支持权限串与 Permission 对象两种入参
会话getSession、getSession(boolean)取会话;带 boolean 表示无会话时是否创建
线程执行execute、associateWith、runAs 等把 Subject 绑定到其他线程执行任务

实测用法与回显(示例工程 AuthzDemo 节选):

Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken(username, "q1w2e3r4"));
boolean admin = subject.hasRole("admin");
boolean read = subject.isPermitted("app:read");
subject.checkPermission("app:delete");   // 无权限时抛 UnauthorizedException

root 登录后 hasRole(admin) = true;alice 登录后 isPermitted(app:read) = true、checkPermission(app:delete) 抛 UnauthorizedException: Subject does not have permission [app:delete](完整回显见《Apache Shiro 构建并运行示例应用指南》第八章)。

4.3 AuthenticationToken 与 UsernamePasswordToken

org.apache.shiro.authc.AuthenticationToken 只有两个取值方法(javap 实测输出):

public interface org.apache.shiro.authc.AuthenticationToken extends java.io.Serializable {
  public abstract java.lang.Object getPrincipal();
  public abstract java.lang.Object getCredentials();
}

最常用实现 org.apache.shiro.authc.UsernamePasswordToken(javap 实测节选):

public class org.apache.shiro.authc.UsernamePasswordToken implements org.apache.shiro.authc.HostAuthenticationToken,org.apache.shiro.authc.RememberMeAuthenticationToken {
  public org.apache.shiro.authc.UsernamePasswordToken();
  public org.apache.shiro.authc.UsernamePasswordToken(java.lang.String, char[]);
  public org.apache.shiro.authc.UsernamePasswordToken(java.lang.String, java.lang.String);
  public org.apache.shiro.authc.UsernamePasswordToken(java.lang.String, java.lang.String, boolean);
  public org.apache.shiro.authc.UsernamePasswordToken(java.lang.String, java.lang.String, boolean, java.lang.String);
  public java.lang.String getUsername();
  public char[] getPassword();
  public java.lang.Object getPrincipal();
  public java.lang.Object getCredentials();
  public java.lang.String getHost();
  public boolean isRememberMe();
  public void setRememberMe(boolean);
  public void clear();
}
  • getPrincipal() 返回用户名,getCredentials() 返回密码(char[] 或 String 形态)。
  • 构造器第三参 boolean 为 rememberMe;登录失败的消息里能看到该标记(实测回显:Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - root, rememberMe=false] did not match the expected credentials.)。
  • 实测用法(LoginDemo 节选):currentUser.login(new UsernamePasswordToken("root", "q1w2e3r4")); 成功后 isAuthenticated 为 true。

4.4 Realm 与 IniRealm

org.apache.shiro.realm.Realm 是账号数据源接口(javap 实测输出):

public interface org.apache.shiro.realm.Realm {
  public abstract java.lang.String getName();
  public abstract boolean supports(org.apache.shiro.authc.AuthenticationToken);
  public abstract org.apache.shiro.authc.AuthenticationInfo getAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) throws org.apache.shiro.authc.AuthenticationException;
}

Ini 配置场景的现成实现 org.apache.shiro.realm.text.IniRealm(javap 实测节选):

public class org.apache.shiro.realm.text.IniRealm extends org.apache.shiro.realm.text.TextConfigurationRealm {
  public static final java.lang.String USERS_SECTION_NAME;
  public static final java.lang.String ROLES_SECTION_NAME;
  public org.apache.shiro.realm.text.IniRealm();
  public org.apache.shiro.realm.text.IniRealm(org.apache.shiro.config.Ini);
  public org.apache.shiro.realm.text.IniRealm(java.lang.String);
  public java.lang.String getResourcePath();
  public void setResourcePath(java.lang.String);
  public org.apache.shiro.config.Ini getIni();
  public void setIni(org.apache.shiro.config.Ini);
}
  • Realm 三方法的语义:getName() 返回 realm 名;supports() 判定是否处理该类 token;getAuthenticationInfo() 执行账号查找与凭据比对,失败抛 AuthenticationException 子类。
  • 实测:IniSecurityManagerFactory 依据 shiro.ini 的 [users]/[roles] 段隐式构建 IniRealm,realm 名称为 iniRealm;账号不存在时异常消息为 Realm [org.apache.shiro.realm.text.IniRealm@19091ea8] was unable to find account data for the submitted AuthenticationToken [...](hashcode 每次运行随机)。
  • IniRealm 也支持编程式单独实例化:new IniRealm("classpath:shiro.ini"),配合 SecurityManager 手工装配使用。

4.5 Permission 与权限串

org.apache.shiro.authz.Permission 是权限判定接口,只有一个方法(javap 实测输出):

public interface org.apache.shiro.authz.Permission {
  public abstract boolean implies(org.apache.shiro.authz.Permission);
}
  • implies 语义:当前 Permission 是否「覆盖」传入的 Permission;admin = *(通配符)之所以让 root 对 app:delete 也返回 true,就是通配 Permission implies 任意权限串的结果(实测回显见《Apache Shiro 构建并运行示例应用指南》第八章)。
  • 应用侧通常直接用权限串(资源:操作 分层,如 app:read),由 Subject.isPermitted/checkPermission 接收字符串并交给 Realm 转换判定;只有自定义权限逻辑时才需要实现 Permission 接口。
  • 实测判定结果:alice(user 角色)app:read/app:write 为 true、app:deploy/app:delete 为 false;root(admin 角色)四项全 true。

4.6 Session(会话)

org.apache.shiro.session.Session 接口(javap 实测输出):

public interface org.apache.shiro.session.Session {
  public abstract java.io.Serializable getId();
  public abstract java.util.Date getStartTimestamp();
  public abstract java.util.Date getLastAccessTime();
  public abstract long getTimeout() throws org.apache.shiro.session.InvalidSessionException;
  public abstract void setTimeout(long) throws org.apache.shiro.session.InvalidSessionException;
  public abstract java.lang.String getHost();
  public abstract void touch() throws org.apache.shiro.session.InvalidSessionException;
  public abstract void stop() throws org.apache.shiro.session.InvalidSessionException;
  public abstract java.util.Collection<java.lang.Object> getAttributeKeys() throws org.apache.shiro.session.InvalidSessionException;
  public abstract java.lang.Object getAttribute(java.lang.Object) throws org.apache.shiro.session.InvalidSessionException;
  public abstract void setAttribute(java.lang.Object, java.lang.Object) throws org.apache.shiro.session.InvalidSessionException;
  public abstract java.lang.Object removeAttribute(java.lang.Object) throws org.apache.shiro.session.InvalidSessionException;
}
方法说明
getId会话 ID(实测输出为随机 UUID,如 901cd5c2-2d05-48f2-904d-21e0dcce612e)
getStartTimestamp / getLastAccessTime会话创建与最近访问时间
getTimeout / setTimeout会话级超时(毫秒),不设置时沿用 SessionManager 的 globalSessionTimeout(实测默认 1800000 ms)
touch主动续期(刷新最近访问时间)
stop立即失效会话
setAttribute / getAttribute / removeAttribute属性读写删(LoginDemo 实测:setAttribute("loginUser", principal) 后可读回 root)

五、认证异常体系

shiro-core 2.2.1 jar 中的认证异常清单(unzip -l shiro-core-2.2.1.jar 实取):

org/apache/shiro/authc/AccountException.class
org/apache/shiro/authc/AuthenticationException.class
org/apache/shiro/authc/ConcurrentAccessException.class
org/apache/shiro/authc/CredentialsException.class
org/apache/shiro/authc/DisabledAccountException.class
org/apache/shiro/authc/ExcessiveAttemptsException.class
org/apache/shiro/authc/ExpiredCredentialsException.class
org/apache/shiro/authc/IncorrectCredentialsException.class
org/apache/shiro/authc/LockedAccountException.class
org/apache/shiro/authc/UnknownAccountException.class
org/apache/shiro/authc/pam/ShortCircuitIterationException.class
org/apache/shiro/authc/pam/UnsupportedTokenException.class

层级与用途:AuthenticationException 为基类;CredentialsException(密码类,含 IncorrectCredentials、ExpiredCredentials)与 AccountException(账号类,含 UnknownAccount、DisabledAccount、LockedAccount、ExcessiveAttempts)是两大分支;ConcurrentAccessException 表示并发访问受限。

⚠️ 注意:jar 中不存在 FailedAuthenticationException(对 1.13.0 与 2.2.1 两个版本的 shiro-core 均 unzip -l 核查计数为 0),捕获时不要写这个类名;最常见的两个已实测异常是 IncorrectCredentialsException(密码错误)与 UnknownAccountException(账号不存在),均有真实回显(见《Apache Shiro 构建并运行示例应用指南》第七章)。

六、完整最小可运行示例

把上述 API 串成一次「装配 → 登录 → 判权 → 会话 → 登出」的完整流程(LoginDemo 实测源码节选):

Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityUtils.setSecurityManager(factory.getInstance());

Subject currentUser = SecurityUtils.getSubject();
currentUser.login(new UsernamePasswordToken("root", "q1w2e3r4"));
boolean admin = currentUser.hasRole("admin");

Session session = currentUser.getSession();
session.setAttribute("loginUser", currentUser.getPrincipal());
Object loginUser = session.getAttribute("loginUser");

currentUser.logout();

实测运行输出(应用段节选,mvn exec:java -Dexec.mainClass=demo.LoginDemo):

[demo.LoginDemo.main()] INFO demo.LoginDemo - before login, isAuthenticated = false
[demo.LoginDemo.main()] INFO demo.LoginDemo - after login, isAuthenticated = true
[demo.LoginDemo.main()] INFO demo.LoginDemo - principal = root
[demo.LoginDemo.main()] INFO demo.LoginDemo - hasRole(admin) = true
[demo.LoginDemo.main()] INFO demo.LoginDemo - session attribute loginUser = root
[demo.LoginDemo.main()] INFO demo.LoginDemo - after logout, isAuthenticated = false

七、注意事项

⚠️ 注意:

  1. SecurityManager 是框架侧接口,应用代码以 SecurityUtils.getSubject() 取 Subject 操作为主;SecurityUtils.setSecurityManager 全进程只需装配一次。
  2. is/has 系列(isPermitted/hasRole)返回布尔便于分支;check 系列(checkPermission/checkRole)失败抛 AuthorizationException 子类(如 UnauthorizedException),二者按业务语义选用。
  3. Subject 接口中 getSession(boolean) 的 boolean 表示「无会话时是否新建」;无参 getSession 等价 getSession(true)。
  4. Session 默认实现为内存 MemorySessionDAO,globalSessionTimeout 默认 1800000 ms(实测值);调参方式与覆盖验证见《Apache Shiro 配置参数详解》第七章。

八、常见问题

Q1:怎么快速查某个接口有哪些方法?

用 JDK 自带的 javap 直接反查 jar 中的类签名(命令格式见「二、工具概览」),无需 IDE;把类全名换成目标类即可。

Q2:login 抛出的异常该按什么层次捕获?

基类 AuthenticationException 兜底,按需先捕获具体子类:IncorrectCredentialsException(密码错)、UnknownAccountException(账号不存在)等(实测异常消息见 4.3 与 4.4);不要捕获 jar 中不存在的 FailedAuthenticationException。

Q3:为什么 root 能通过 app:delete 的权限校验?

[roles] 段中 admin = *,通配符 Permission 的 implies 覆盖任意权限串;非通配角色(如 user = app:read, app:write)则逐串匹配(实测结果见 4.5)。

Q4:getSession() 拿到的会话什么时候失效?

受 globalSessionTimeout 控制(实测默认 1800000 ms),会话校验线程按 sessionValidationInterval(实测默认 3600000 ms)周期清理;也可对单个 Session 用 setTimeout 覆盖或 stop() 立即失效。

九、参考链接

  • Apache Shiro 官网:https://shiro.apache.org/
  • Java 认证指南:https://shiro.apache.org/java-authentication-guide.html
  • Java 授权指南:https://shiro.apache.org/java-authorization-guide.html
  • Session Management 文档:https://shiro.apache.org/session-management.html
  • 2.2.1 API 文档:https://shiro.apache.org/static/2.2.1/apidocs/