目录
Apache Shiro核心类、接口基本解析
Subject(主题)
Subject就像呈现的视图。所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
Subject currentUser = SecurityUtils.getSubject(); //获取当前执行(登录)的用户
Session session = currentUser.getSession(); //获取session会话
session.setAttribute( "someKey", "aValue" );
通过SecurityUtils.getSubject()获取Subject实例
独立应用程序中的调用可能会Subject在特定于应用程序的位置中返回基于用户数据的,而在服务器环境(例如Web应用程序)中,它会获取Subject与当前线程或传入请求相关联的基于用户数据的
Subject常用方法
(1)登录
方法 | 描述 |
---|---|
Object getPrincipal() | 返回应用程序范围内的唯一标识主体。 如果主题是匿名的,尚无任何关联的帐户数据(尚未登录),则返回null |
PrincipalCollection getPrincipals() | 返回此主题(Subject)的主体(principals)(标识属性)。 如果主题是匿名的,因为它还没有任何关联的帐户数据(尚未登录),返回null |
Session getSession() | 返回与此主题关联的Session |
Session getSession(boolean create) | 返回与此主题关联的Session。 boolean参数确定是否应创建新会话(如果没有现有会话) |
boolean isRemembered() | Subject是否具有身份(不是匿名),并且principals在上一个会话期间通过成功的身份验证记住该身份 |
void login(AuthenticationToken token) | 为此主题(Subject)/用户执行登录尝试 |
void logout() | 注销此主题,并使所有关联实体(Session和授权数据)无效和/或将其删除 |
boolean isAuthenticated() | 是否登录成功。成功true |
(2)角色
方法 | 描述 |
---|---|
boolean hasAllRoles(Collection<String>) | 此主题(Subject)是否具有指定的所有角色;全有返回true,一个不满足返回false |
boolean hasRole(String roleIdentifier) | 此Subject是否具有指定角色 |
boolean[] hasRoles(List<String>) | 检查此Subject是否具有指定的角色,并返回一个布尔数组,指示拥有哪些角色 |
(3)权限
方法 | 描述 |
---|---|
boolean[] isPermitted(List<Permission>) | 检查此主体(Subject)是否暗含给定的权限,并返回一个布尔数组,指示暗含了哪些权限 |
boolean isPermitted(Permission) | 是否拥有指定权限 |
boolean[] isPermitted(String…) | 检查此Subject是否暗含给定的权限字符串,并返回一个布尔数组,指示暗含了哪些权限 |
boolean isPermitted(String permission) | 是否允许此Subject执行操作或访问,指定的权限字符串汇总的资源 |
boolean isPermittedAll(Collection<Permission>) | 如果此Subject暗含所有指定的权限,返回true |
boolean isPermittedAll(String…) | 如果此Subject隐含所有指定的权限字符串,返回true |
常见异常(登录)
AuthenticationException异常是Shiro在登录认证过程中、认证失败需要抛出的异常。包含以下子类
(1)账号异常(AccountException)
由于执行身份验证尝试的帐户存在问题而引发的异常
异常 | 描述 |
---|---|
ConcurrentAccessException | 并发访问异常(多个用户同时登录时抛出) |
UnknownAccountException | 未知的账号 |
ExcessiveAttemptsException | 认证次数超过限制 |
DisabledAccountException | 禁用的账号 |
LockedAccountException | 账号被锁定 |
pportedTokenException | 使用了不支持的Token |
(2)凭证异常(CredentialsException)
由于身份验证过程中为帐户提交的凭据有问题而引发异常
异常 | 描述 |
---|---|
IncorrectCredentialsException | 不正确的凭证 |
ExpiredCredentialsException | 凭证过期 |
(3)授权异常(AuthorizationException)
授权(访问控制检查)过程中出现问题,则抛出异常
异常 | 描述 |
---|---|
UnauthorizedException | 抛出以指示请求的操作或对请求的资源的访问是不允许的 |
UnanthenticatedException | 当尚未完成认证时,尝试执行授权操作时引发异常 |
Session(会话)
通过Subject.getSession获取实例,提供了常规HttpSession所使用的大部分功能,但具有一些额外的优点和一个很大的不同:不需要HTTP环境
①部署在Web应用程序内部,则默认情况下Session将HttpSession基于该应用程序
②部署在非Web环境中,Shiro将默认自动使用其企业会话管理
意味着无论部署环境如何,都可以在任何层的应用程序中使用相同的API。这就打开了一个全新的应用程序世界,因为不需要强制要求使用会话的任何应用程序使用HttpSession或EJB状态会话Bean。而且,任何客户端技术现在都可以共享会话数据
ShiroFilterFactoryBean过滤器配置
注意:在使用Shiro框架时,ShiroFilterFactoryBean属性filterChainDefinitions配置过滤器遇到了数量限制问题。当过滤器规则超过12个之后,出现Shiro无法拦截非权限请求的现象
目前解决方法:控制过滤规则数量在12个之内
配置 | 描述 |
---|---|
securityManager | 必须。引入安全管理器 |
loginUrl | 没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的“/login.jsp”页面 |
successUrl | 登录成功默认跳转页面,可以不配置,不配置则跳转至“/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。 |
unauthorizedUrl | 账户权限验证失败跳转的地址 |
filterChainDefinitions | 账户权限不够跳转的地址 |
上面和配置的.ini文件非常相似,其中主要就是配置资源的的拦截。Shiro提供了很多默认的过滤器,比如什么验证,授权等
(1)认证过滤器
注意:定义规则是有顺序的,从上到下,拦截范围必须是从小到大的。调用是上至下
认证过滤器 | 描述 |
---|---|
anon | 匿名过滤器,没有参数。 允许未登录访问,一般用于配置登录页面和静态CSS等资源。/css/** = anon |
authc | 基于表单的过滤器,没有参数。表示需要认证(登录)才能使用 比如:若用户没有登录就会跳转到loginUrl的地址,其拦截的请求必须是通过登录验证的,即Subject.isAuthenticated() == true的账户才能访问。/admins/user/** = authc |
logout | 退出过滤器。Shiro提供了一个退出的功能,配置了/logout = logout,Shiro就会生成一个虚拟的映射路径,当用户访问了这个路径,Shiro会自动清空缓存并跳转到loginUrl页面 |
user | 用户过滤器。表示必须存在用户,当登入操作时不做检查 和authc过滤器很类似,都是账户为登录的进行拦截并跳转到loginUrl地址;不同之处在于authc允许账户必须是通过Subject.siAuthenticated() ==true的;而user不仅允许登录账户访问,通过rememberMe登录的用户也能访问 |
authcBasic | /admins/user/**=authcBasic 没有参数,表示httpBasic认证 |
(2)授权过滤器
授权过滤器 | 描述 | ||||||
---|---|---|---|---|---|---|---|
roles[角色] | 参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割 例如:/admins/user/**=roles[“admin,guest”],登录用户同时具有后面的所有角色才能通过认证,相当于hasAllRoles()。 | ||||||
perms[权限] | 参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割 例如:/admins/user/**=perms[“user:add:,user:modify:”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll() | ||||||
rest | 风格过滤器,自动根据请求方法构建权限字符串。 例如 :/admins/user/=rest[user] 根据请求的方法,相当于/admins/user/=perms[user:method] 其中method为post,get,delete等 | ||||||
port | /admins/user/**=port[8081] 当请求的url的端口不是8081是跳转到 schemal://serverName:8081?queryString 其中:
| ||||||
ssl | /admins/user/**=ssl 没有参数,表示安全的url请求,协议为https |
控制器收集用户信息(UsernamePasswordToken)
控制器:UsernamePasswordToken
领域:AuthenticationToken(顶层接口)
AuthenticationToken用于收集用户提交的身份(用户名)及凭据(密码)。
Shiro会调用CredentialsMatcher对象的doCredentialsMatch(),对AuthenticationInfo对象(登录校验)和AuthenticationToken(权限校验)进行匹配。匹配成功则表示主体(Subject)认证成功,否则表示认证失败
Shiro仅提供了一个可以直接使用的UsernamePasswordToken,用于实现基于用户名/密码主体(Subject)身份认证
UsernamePasswordToken实现了RememberMeAuthenticationToken和HostAuthenticationToken,可以实现“记住我”及“主机验证”的支持
自定义控制器
一般情况下UsernamePasswordToken已经可以满足需求。当遇到需要声明自己的Token类时,可以根据需求来实现下面接口
控制器 | 描述 |
---|---|
①AuthenticationToken | 收集用户提交的身份(用户名)及凭据(密码) |
②HostAuthenticationToken | 保留身份验证尝试源自的主机信息 |
③RememberMeAuthenticationToken | AuthenticationToken指示用户身份跨会话记住。 |
(1)如果不需要“记住我”,也不需要“主机验证”,则可以实现AuthenticationToken
(2)如果需要“记住我”,则可以实现RememberMeAuthenticationToken
(3)如果需要“主机验证”功能,则可以实现HostAuthenticationToken
(4)如果需要“记住我”,且需要“主机验证”,则可以像UsernamePasswordToken一样,同时实现RememberMeAuthenticationToken和HostAuthenticationToken
(5)如果需要其他自定义功能,则需要自己实现
注意:当为相应用户创建新会话时,该用户的身份将被记住,但不会被视为已通过身份验证
领域中身份验证(SimpleAuthenticationInfo类)
public class SimpleAuthenticationInfo
implements MergableAuthenticationInfo, SaltedAuthenticationInfo {}
MergableAuthenticationInfo、SaltedAuthenticationInfo接口的简单实现。用于保存主题(Subject)和凭证
顶层接口AuthenticationInfo。SimpleAuthenticationInfo提供了多个构造方法,但是一般密码是经过加密的;Shiro会自动根据token中的用户名和密码与从数据库中查询到的数据进行匹配,如果匹配成功就登录成功,否则就抛出异常
(1)使用方式一
SimpleAuthenticationInfo(PrincipalCollection principals,Object credentials)
SimpleAuthenticationInfo(PrincipalCollection principals
,Object hashedCredentials
,ByteSource credentialsSalt)
用于接收帐户的标识主体,验证主体的哈希凭证以及哈希凭证时使用的盐
构造参数
参数 | 描述 |
---|---|
principals | 帐户的识别主题,身份,主体的唯一标识(用户名、自定义Realm名) 比如:用户名、邮箱等,如果将用户名和密码传给了Token对象,那么在Token对象中就能getPrincipal获取这个标识 |
credentials | 验证主题的证明、凭据(密码、数字证书、指纹)。 在Shiro等安全框架中,类似于密码这种数据一般都是经过加密处理 |
credentialsSalt | 对hashedCredentials进行哈希处理时使用的盐(Salt) |
PrincipalCollection
通过PrincipalCollection的实现类SimplePrincipalCollection(Object principal, String realmName)创建
参数 | 描述 |
---|---|
principal | 标识。用户名 |
realmName | 凭证。密码 |
PrincipalCollection principalCollection = new SimplePrincipalCollection(username, realm);
注意:在创建SimplePrincipalCollection实例的时候传入的第一个参数,判断是否是一个对象,是或者不是对象,最终都会加入到SimplePrincipalCollection实例中,可以发现SimpleAuthenticationInfo的第一个参数既可以是user对象也可以是username
(2)使用方式二
SimpleAuthenticationInfo(Object principal,Object credentials,String realmName)
SimpleAuthenticationInfo(Object principal,Object hashedCredentials,ByteSource credentialsSalt,String realmName)
构造参数
参数 | 描述 |
---|---|
principal | 指定领域(Ream)关联的“主要”主题。传入User类的user对象 注意:此参数可以通过subject.getPrincipal()方法获取(获取当前记录的用户),从这个用户对象进而再获取一系列的所需要的属性 |
credentials | 验证主体的凭据。 传入的是从数据库中获取到的password,然后再与token中的password进行对比,匹配上了就通过,匹配不上就报异常 |
credentialsSalt | 哈希hashedCredentials时使用的盐(Salt)。 用于加密密码对比。 若不需要,则可以设置为空“”,防止密码被解密破解 |
realmName | 获取主题(Subject)和凭证()的领域。当前realm的名字 |
采用帐户的单个“主要”主体及其对应的凭据(与指定的领域相关联)
构造函数采用帐户的单个“主要”委托人,其对应的哈希凭证,用于哈希凭证的盐以及与委托人相关联的领域的名称
这是一个方便的构造函数,使用PrincipalCollection基于principal和realmName参数构造一个
领域中用户授权(SimpleAuthorizationInfo类)
AuthorizationInfo接口的简单实现。将角色和权限存储为内部属性
public class SimpleAuthorizationInfo
implements AuthorizationInfo {}
顶层接口:AuthorizationInfo
构造方法
构造方法 | 描述 |
---|---|
SimpleAuthorizationInfo() | 默认无参构造 |
SimpleAuthorizationInfo(Set<String> roles) | 创建具有指定角色且没有权限的新实例 |
成员方法
成员方法 | 描述 |
---|---|
void setObjectPermissions(Set<Permission>) | 设置直接分配给帐户的基于对象的权限 |
void setRoles(Set<String> roles) | 设置分配给该帐户的角色 |
void setStringPermissions(Set<String>) | 设置直接分配给该帐户的基于字符串的权限 |
Set<Permission> getObjectPermissions() | 返回Permission分配给相应Subject的所有类型安全的 |
Set<String> getRoles() | 返回分配给相应主题的所有角色的名称 |
Set<String> getStringPermissions() | 返回分配给相应主题的所有基于字符串的权限 |
下面add方法返回值都为void
成员方法 | 描述 |
---|---|
addObjectPermission(Permission) | 向与该帐户直接关联的用户添加(分配)权限 |
addObjectPermissions(Collection<Permission>) | 向与该帐户直接关联的权限添加(分配)多个权限 |
addRole(String role) | 向与该帐户关联的角色添加(分配)角色 |
addRoles(Collection<String> roles) | 向与该帐户关联的角色添加(分配)多个角色 |
addStringPermission(String permission) | 向与该帐户直接关联的用户添加(分配)权限 |
addStringPermissions(Collection<String>) | 向与该帐户直接关联的权限添加(分配)多个权限 |
Permission(了解)
权限表示执行操作或访问资源的能力。权限是系统安全策略中最精细或最基本的单元,也是构建细粒度安全模型的基石
public abstract class Permission
implements Guard, java.io.Serializable {}
Permission只有一个implies()方法
方法 | 描述 |
---|---|
boolean implies(Permission p) | 实现此方法,返回值为true,意味着可访问所有资源(超管) |
Permission有三个实现类AllPermission、DomainPermission、WildcardPermission |
(1)AllPermission实现了Permission
public class AllPermission
implements Permission, Serializable {}
AllPermission实例总是意味着任何其他许可;也就是说,其implies()始终返回true。此类的实例通常仅分配给“root”或“administrator”用户或角色
构造方法:AllPermission(Permission p)
构造方法 | 描述 |
---|---|
boolean implies(Permission p) | 始终返回true。表示任何被授予此权限的Subject都可以执行任何操作 |
(2)DomainPermission继承了WildcardPermission
public class DomainPermission
extends WildcardPermission {}
(3)WildcardPermission实现了Permission
public class WildcardPermission
implements Permission, Serializable {}
WildcardPermission是一种非常灵活的权限构造,支持多级权限匹配