Apache Shiro认证(三)

总的参考文档:http://shiro.apache.org/reference.html
本文参考文档:http://shiro.apache.org/authentication.html

1、概念

Apache Shiro认证(三)_第1张图片

本文介绍Shiro功能特性中的身份认证部分,上图中绿色的一块。身份认证就是根据用户提供的身份以及凭证验证它的合法性。有两个概念:

  1. Principals:用户的身份,简单理解就是用户名。
  2. Credentials:简单理解就是用户的密码,或者其它能证明用户身份的东西。

2、Login

前文说过,在具体的应用中用Subject代表一个用户,认证的过程可以分成三步:

  1. 收集用户Principal与Credential。
  2. Subject将收集到的Principal与Credential提交给SecurityManager进行身份验证。
  3. 确认验证结果,是否通过身份验证。

2.1.收集用户Principal与Credential

示例代码:

//Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(username, password);

//"Remember Me" built-in: 
token.setRememberMe(true);

首先username与password怎么来的,代码中并没有写。实际上Shiro并不关心它们是怎么来的,常见的情况是用户通过浏览器提交了表单,应用从表单中拿到的数据。拿到数据后,将它们转换成UsernamePasswordToken实例。UsernamePasswordToken是 org.apache.shiro.authc.AuthenticationToken接口的具体实现。实际上这个UsernamePasswordToken实例最终会被传递给Realm,Realm要认识这个类才能知道怎么解析里边的用户名与密码。

假如这样一种情况,用户在提交数据时,除了用户名、密码外,还包括防止机器人的随机码,就像我们平时登录网站时会先刷出打了噪点的小图片,上边有一串数字、字母、汉字什么的。这个时候就需要开发者实现自己的org.apache.shiro.authc.AuthenticationToken类,将用户、密码、随机验证码一起打包。当然也要设置或者自定义Realm,让它知道如何解释打包进来的东西,如何验证这些东西。另外注意最后一行代码token.setRememberMe(true);,这个是记住我功能,目前搞不清楚它是怎么实现的,但我想应该与会话管理有关。这个东西等看完SessionManager后再说。

2.2.提交Principal与Crenential

示例代码:

Subject currentUser = SecurityUtils.getSubject();

currentUser.login(token);

前文中说过了,SecurityUtils代表了一个SecurityManager实例,并且它是单例,全应用只有一个实例。从它获取Subject实例,这样Subject实例就与SecurityUtils代表的SecurityManager实例关联起来了。

一旦从SecurityUtils获取了Subject实例,那么这个实例就会与当前线程绑定,它是线程级别的对象。比如你在某个方法中执行了上述代码,那么在另一个方法中调用SecurityUtils.getSubject()时,返回的仍然是示例代码中的Subject,是同一个,而不是新创建。如果上述代码通过了验证,那么在后续的方法中调用SecurityUtils.getSubject().isAuthenticated()返回的依然是true。

currentUser.login(token);这一行表示向SecurityManager发起身份认证。

2.3.确认验证结果

login方法没有返回值,如果验证通过不会生成异常,如果没有通过则会抛出各种异常,因此需要用try catch捕获异常并处理,示例代码如下:

try {
    currentUser.login(token);
} catch ( UnknownAccountException uae ) { ...
} catch ( IncorrectCredentialsException ice ) { ...
} catch ( LockedAccountException lae ) { ...
} catch ( ExcessiveAttemptsException eae ) { ...
} ... catch your own ...
} catch ( AuthenticationException ae ) {
    //unexpected error?
}

上述异常是由Realm抛出的,当然开发者可以自定义Realm并抛出自定义异常。

3、Logout

示例代码:

currentUser.logout(); //removes all identifying information and invalidates their session too.

执行logout操作后,Subject将会清空自己所持有的所有信息,并且将相关的session失效,效果等同于它是刚从SecurityUtils.getSubject();获取到一样。

4、认证流程图

Apache Shiro认证(三)_第2张图片

上图中一共有五个步骤,本质上是层层调用,最后将token传给Realm。

第一步:用户调用Subject.login(token)并将token传入。

第二步:因为Subject是与SecurityManager绑定的,Subject.login(token)实际会调用securityManager.login(token),这个时候token到了SecurityManager。

第三步:securityManager.login(token)又调用authenticator.login(token),数据token传给了Authenticator组件。Authenticator一般是ModularRealmAuthenticator类实例,它支持多Realm。

第四步:这一步只有在多个Realm时才会发生。如果有多个Realm,Authenticator会初始化一个AuthenticationStrategy实例,AuthenticationStrategy负责根据配置的策略协调多个Realm的认证过程,后边会详细介绍。

第五步:这时token终于到了具体的Realm,在这里最终进行身份验证,后边会详细介绍Realm。

上述五个步骤,每个涉及到的组件实例用户都可以自定义。

5、AuthenticationStrategy

认证策略,当有多个Realm时负责协调整个认证过程。比如当一个Realm通过认证,其它的Realm都没有通过认证,这个时候整个认证应该是成功还是不成功呢?是否需要所有Realm都认证成功才算整个认证成功?在认证的时候,如果第一个Reaml就已经认证通过,这个时候是不是应该整个认证过程已经成功了,后边的Realm不需要再认证了呢?AuthenticationStrategy就是做这个事情的。

AuthenticationStrategy的四次交互:

  1. 在token传递给任何Realm之前交互一次。
  2. 调用某个具体的Realm这前交互一次。
  3. 调用完某个具体的Realm之后交互一次。
  4. 所有的Realm调用完成以后,再交互一次。

在最后一步的时候,如果有不止一个Realm通过了验证,那么AuthenticationStrategy会将全部的结果都聚合到单一的AuthenticationInfo实例中,这个实例会通过Authenticator传递给SecurityManager,再由SecurityManager返回给具体的Subject,这样Subject就知道自己通过了那个Realm的认证。

AuthenticationStrategy本身是Shiro提供的接口,同时Shiro也提供了三种具体实现。当然如果Shiro提供的具体实现无法需求,开发者也可以自定义。

AuthenticationStrategy class Description
AtLeastOneSuccessfulStrategy 向所有Realm发起认证,只有要一个认证通过,整个认证过程就被认为成功。只有所有Realm认证全部失败,才认为整个认证过程失败。如果有多个Realm通过认证,相关的信息全部聚合并返回给Subject。
FirstSuccessfulStrategy 依次向Realm发起认证,只要有一个成功,整个认证被认为成功,其它Realm将不再认证。所有Realm都失败,则整个认证失败。只返回给Subject认证成功的Realm信息。
AllSuccessfulStrategy 这个很明确,所有Realm必需全部成功,一个失败都不行。所有Realm的认证成功信息聚合后返回给Subject。

默认Authenticator是ModularRealmAuthenticator类实例,它默认使用AtLeastOneSuccessfulStrategy谁策略。可通过如下方式配置:

[main]
...
authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy

securityManager.authenticator.authenticationStrategy = $authcStrategy

...

5、多Realm的排序

FirstSuccessfulStrategy认证策略中,多个Realm是依次进行的,因此Realm的顺序会影响到认证结果。在配置SecurityManager时有两种反序方式。

一各旧隐式排序,如:

blahRealm = com.company.blah.Realm
...
fooRealm = com.company.foo.Realm
...
barRealm = com.company.another.Realm

这种的话,SecurityManager就按它们在配置文件中定义的顺序发起认证。

另一种是显示排序,如:

blahRealm = com.company.blah.Realm
...
fooRealm = com.company.foo.Realm
...
barRealm = com.company.another.Realm

securityManager.realms = $fooRealm, $barRealm, $blahRealm

最后一行代码,明确定义了三个Realm的顺序,隐式排序就失效。

你可能感兴趣的:(Shiro,安全)