1.shiro所能做的事情
- 验证用户来核实他们的身份
- 对用户执行访问控制,
- 如:判断用户是否被分配了一个确定的安全角色
- 判断用户是否被允许做某事
- 如:判断用户是否被分配了一个确定的安全角色
- 在任何环境下使用 Session API,即使没有 Web 或 EJB 容器。
- 在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。
- 聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。
- 启用单点登录(SSO)功能。
- 为没有关联到登录的用户启用"Remember Me"服务
2.shiro 重要概念
(1)shiro架构图
![](https://i-blog.csdnimg.cn/blog_migrate/7404bde0ff0007dbc36b7b9ef718c22a.png)
(2)概念(来自中文文档)
- Subject(org.apache.shiro.subject.Subject) 当前与软件进行交互的实体(用户,第三方服务,cron job,等等)的安全特定“视图”。
- SecurityManager(org.apache.shiro.mgt.SecurityManager) 如上所述,SecurityManager 是 Shiro 架构的心脏。它基本上是一个“保护伞”对象,协调其管理的组件以确保 它们能够一起顺利的工作。它还管理每个应用程序用户的 Shiro 的视图,因此它知道如何执行每个用户的安全 操作。
- Authenticator(org.apache.shiro.authc.Authenticator) Authenticator 是一个对执行及对用户的身份验证(登录)尝试负责的组件。当一个用户尝试登录时,该逻辑 被 Authenticator 执行。Authenticator 知道如何与一个或多个 Realm 协调来存储相关的用户/帐户信息。从这些 Realm 中获得的数据被用来验证用户的身份来保证用户确实是他们所说的他们是谁。
- Authentication Strategy(org.apache.shiro.authc.pam.AuthenticationStrategy) 如果不止一个 Realm 被配置,则 AuthenticationStrategy 将会协调这些 Realm 来决定身份认证尝试成功或 失败下的条件(例如,如果一个 Realm 成功,而其他的均失败,是否该尝试成功? 是否所有的 Realm 必须成功?或只有第一个成功即可?)。
- Authorizer(org.apache.shiro.authz.Authorizer) Authorizer 是负责在应用程序中决定用户的访问控制的组件。它是一种最终判定用户是否被允许做某事的机制。 与 Authenticator 相似,Authorizer 也知道如何协调多个后台数据源来访问角色恶化权限信息。Authorizer 使用 该信息来准确地决定用户是否被允许执行给定的动作。
- SessionManager(org.apache.shiro.session.SessionManager) SessionManager 知道如何去创建及管理用户 Session 生命周期来为所有环境下的用户提供一个强健的 Session 体验。这在安全框架界是一个独有的特色——Shiro 拥有能够在任何环境下本地化管理用户 Session 的能力, 即使没有可用的 Web/Servlet 或 EJB 容器,它将会使用它内置的企业级会话管理来提供同样的编程体验。 SessionDAO 的存在允许任何数据源能够在持久会话中使用。
- SessionDAO(org.apache.shiro.session.mgt.eis.SessionDAO) SesssionDAO 代表 SessionManager 执行 Session 持久化(CRUD)操作。这允许任何数据存储被插入到会 话管理的基础之中。
- CacheManager(org.apahce.shiro.cache.CacheManager) CacheManager 创建并管理其他 Shiro 组件使用的 Cache 实例生命周期。因为 Shiro 能够访问许多后台数据源, 由于身份验证,授权和会话管理,缓存在框架中一直是一流的架构功能,用来在同时使用这些数据源时提高 性能。任何现代开源和/或企业的缓存产品能够被插入到 Shiro 来提供一个快速及高效的用户体验。
- Cryptography(org.apache.shiro.crypto.*) Cryptography 是对企业安全框架的一个很自然的补充。Shiro 的 crypto 包包含量易于使用和理解的cryptographic Ciphers,Hasher(又名 digests)以及不同的编码器实现的代表。所有在这个包中的类都被精心地设计以易于 使用和易于理解。任何使用 Java 的本地密码支持的人都知道它可以是一个难以驯服的具有挑战性的动物。Shiro 的 cryptoAPI 简化了复杂的 Java 机制,并使加密对于普通人也易于使用。
- Realms(org.apache.shiro.realm.Realm) 如上所述,Realms 在 Shiro 和你的应用程序的安全数据之间担当“桥梁”或“连接器”。当它实际上与安全 相关的数据如用来执行身份验证(登录)及授权(访问控制)的用户帐户交互时,Shiro 从一个或多个为应用 程序配置的 Realm 中寻找许多这样的东西。你可以按你的需要配置多个 Realm(通常一个数据源一个 Realm), 且 Shiro 将为身份验证和授权对它们进行必要的协调。
3.身份验证
(1)流程
![](https://i-blog.csdnimg.cn/blog_migrate/30e500b5473230a289d4e6dea465bc19.png)
(2)分析
1.获得subject
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject=SecurityUtils.getSubject();
subject.login(token);
这里的token是一个包含用户身份Principals(账号)与凭证Credentials(密码)的AuthenticationToken实例,一般我们用
UsernamePasswordToken token=new UsernamePasswordToken(account,password);,注:
Principals,
Credentials这两个单词会在shiro中频繁出现。
try { subject.login(token);//会抛出的异常
} catch ( UnknownAccountException uae ) { …
} catch ( IncorrectCredentialsException ice ) { …
} catch (LockedAccountException lae ) { …
} catch (ExcessiveAttemptsException eae ) { …
} … catch your own …
} catch ( AuthenticationException ae ) {
//unexpected error?
}
2.调用subject.login(token)后,委托应用程序的 SecurityManager,
SecurityManager通过调用 securityManager.login(token)开始真正的验证工作,SecurityManager 提供了一个安全的环境
3.SecurityManager 接收 token 以及简单地委托给内部的
Authenticator 实例通过调用 authenticator.authenticate(token),实际上SecurityManager 声明为
public interface SecurityManager extends Authenticator, Authorizer, SessionManager
4.如果应用程序中配置了一个以上的 Realm(realm概念见上),ModularRealmAuthenticator 实例将利用配置好的 AuthenticationStrategy 来启动 Multi-Realm 认证尝试。在 Realms 被身份验证调用之前,期间和以后, AuthenticationStrategy 调用使其能够对每个 Realm 的结果作出反应,另外AuthenticationStrategy 负责从每一个成功的 Realm 汇总结果并将它们“捆绑”到一个单一的 AuthenticationInfo 再现。这最后汇总的 AuthenticationInfo 实例就是从 Authenticator 实例返回的值。
AuthenticationStrategy 有3个具体实现
AtLeastOneSuccessfulStrategy | 如果一个(或更多)Realm 验证成功,则整体的尝试被认为是成功的。如果没有一个验证成功,则整体尝试失败。 |
FirstSuccessfulStrategy | 只有第一个成功地验证的 Realm 返回的信息将被使用。所有进一步的 Realm 将被忽略。如果没有一个验证成功,则整体尝试失败。 |
AllSucessfulStrategy | 为了整体的尝试成功,所有配置的 Realm 必须验证成功。如果没有一个验证成功,则整体尝试失败。 |
5.配置Realm来对token进行校验,看它是否支持提交的 AuthenticationToken。如果支持,那么支持 Realm 的getAuthenticationInfo 方法将会伴随着提交的 token 被调用。
通常自定义Realm通过继承AuthorizingRealm并重写其中的两个方法,如
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
//权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String account=(String)authenticationToken.getPrincipal();
User user=userMapper.selectUserByAccount(account);
//这里不需要抛出异常
if(user==null){
throw new UnknownAccountException("未知用户名");
}
//这里是从数据库查出的密码
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(account,user.getCpassword(),"userRealm");
info.setCredentialsSalt(ByteSource.Util.bytes(Const.ShiroConst.SALT));
return info;
}
}
4.授权
(待更新)