AuthenticationStrategy:认证策略,当给认证器Authenticator设置多个Realm时,要用到认证策略;认证策略的大致意思是:将多个realm返回的结果,按照什么样的认证策略来形成最终的认证结果。
1:AuthenticationStrategy的继承和实现关系如下
AuthenticationStrategy接口有个抽象类的实现AbstractAuthenticationStrategy;AbstractAuthenticationStrategy对AuthenticationStrategy接口定义的功能进行了实现。
最常用的是AbstractAuthenticationStrategy的三个子类(通过重写方法对AbstractAuthenticationStrategy进行各自的改变):
FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略。
AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,将返回所有Realm身份验证成功的认证信息。
AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。
2:AuthenticationStrategy接口中的四个方法如下:
3:AuthenticationStrategy接口的实现类AbstractAuthenticationStrategy中多了一个merge方法
beforeAllAttempts:用于生成合并对象,合并对象用来合并各个realm生成的AuthenticationInfo对象。
beforeAttempt:仅仅将传入的合并对象返回,不做其他处理
afterAttempt:将realm返回的AuthenticationInfo对象和合并对象进行判断,并调用合并方法进行合并。
afterAllAttempts:将最终的合并对象返回
merge:合并realm返回的AuthenticationInfo对象和合并对象。
4:根据下面shiro的ModularRealmAuthenticator的doMultiRealmAuthentication方法中认证策略的使用,对认证策略进行总结:
- protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
- AuthenticationStrategy strategy = getAuthenticationStrategy();
- AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
- if (log.isTraceEnabled()) {
- log.trace("Iterating through {} realms for PAM authentication", realms.size());
- }
- for (Realm realm : realms) {
- aggregate = strategy.beforeAttempt(realm, token, aggregate);
- if (realm.supports(token)) {
- log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
- AuthenticationInfo info = null;
- Throwable t = null;
- try {
- info = realm.getAuthenticationInfo(token);
- } catch (Throwable throwable) {
- t = throwable;
- if (log.isDebugEnabled()) {
- String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
- log.debug(msg, t);
- }
- }
- aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);
- } else {
- log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
- }
- }
- aggregate = strategy.afterAllAttempts(token, aggregate);
- return aggregate;
- }
通过上面的代码对AbstractAuthenticationStrategy的认证流程总结如下:
(1)在循环遍历realms前,生成一个合并对象(beforeAllAttempts)
(2)在循环内,调用realm前,仅仅将合并对象返回(beforeAttempt)
(3)在循环内,调用realm后,将realm返回的对象和合并对象进行合并(afterAttempt,merge)
(4)在循环遍历realms后,将合并对象返回(afterAllAttempts)
理解:将多个realm认证的结果合并在一起,并最终返回。
5:FirstSuccessfulStrategy(只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略),所以我们只需要重写AbstractAuthenticationStrategy的merge方法(合并对象的过程),在该方法内,只需要将合并对象保持为第一个认证成功的realm返回的对象即可。(这里还需要验证,不明白最终如果所有realm验证失败了,会是什么结果?)
6:AtLeastOneSuccessfulStrategy(只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,将返回所有Realm身份验证成功的认证信息),所以我们只需要重写AbstractAuthenticationStrategy的afterAllAttempts方法(即所有realm认证以后),在该方法内,对合并对象进行判断,如果合并对象为空或者合并对象内没有任何的认证通过的主体信息,则说明没有任何一个Realm验证成功,所以最终结果是失败。
7:AllSuccessfulStrategy(所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了),所以我们需要重写AbstractAuthenticationStrategy的afterAttempt方法(即每个realm认证后),在方法内,对每次realm返回的对象都进行判断,只要有一个是失败,则整个认证过程就是失败的。(这里也需要验证一下,看如果抛出了运行时异常,catch能捕获到吗?)