Shiro身份验证(三)

Shiro的认证过程包括收集Subject的身份和凭证、提交验证、处理结果。Remembered状态是非匿名且已知身份,而Authenticated状态是经过验证。注销功能允许解除与应用程序的绑定。身份验证涉及Authenticator、AuthenticationStrategy和Realm,ModularRealmAuthenticator协调多个Realm的认证策略,如AtLeastOneSuccessfulStrategy。Realm验证顺序可以是隐式或明确配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Authentication(身份验证)就是验证身份的过程,即在应用程序中验证他们到底存不存在。为了验证他们的身份,他们需要提供一些让应用程序信任的标识信息。

在Shiro中,用户必须提供principals(身份)和credentials(凭证)给Shiro,让应用程序验证用户身份。

  • Principals是一个主体的标识属性,它可以是任何东西,比如用户名、邮箱和手机等唯一标识。虽然Subject可以有任意数量principals,但是必须有一个主要principals。
  • Credentials是主体才知道秘密值。

它们最常见的例子就是用户名/密码的组合。

验证Subject

我们在Shiro的介绍(一)已经诉述过,验证Subject一般分为三步,我们来详解这三步。

        Step1:收集Subject的身份和凭证

// 收集用户名/密码
UsernamePasswordToken token = new UsernamePasswordToken(username, password);

// 设置”Remember Me”
token.setRememberMe(true);
如你所见,“UsernamePasswordToken”类支持最常见的用户名/密码身份验证方式,这个类实现了Shiro的“org.apache.shiro.authc.AuthenticationToken”接口,而这个接口用于Shiro验证系统验证提交的身份和凭证。

       Step2:提交身份和凭证

// 获取Subject
Subject currentUser = SecurityUtils.getSubject();

// 登录(提交身份和凭证)
currentUser.login(token);
如你所见,获取Subject当前运行实例,提交UsernamePasswordToken实例,执行登录操作。

        Step3:处理成功或失败

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

//No problems, continue on as expected...
如你所见,Shiro执行login方法,通过try/catch块捕捉异常来验证身份是否通过。你可以根据捕获的不同异常来显示不同的错误信息,例如用户名不存在、用户并被锁定等等。若登录成功,则在应用程序的任何地方都可以通过SubjectUtil.getSubject()获取返回被验证的Subject信息,并且subject.isAuthenticated()也将会返回true。若登录失败,则抛出不同的异常,也可以是自定义的AuthenticationException异常(在用户提交的身份和凭证发生错误时,建议只返回一个通用的信息给用户,例如:用户名/密码错误,这样也能加大黑客针对登录攻击的复杂度)。

Remembered 和 Authenticated

从上述的例子“token.setRememberMe(true)”看来,在正常的登录过程中,Shiro支持“remmber me”的功能。值得指出,Shiro使用一个标记来区分Remembered Subject和Authenticated Subject。

  • Remembered:一个被记住的Subject是非匿名并已知的身份(例如调用subject.getPrincipals()返回的结果非空)。并且这个身份是上一次验证通过后,被session记住的身份。若Subject被记住了,则调用subject.isRemembered()就会返回true。
  • Authenticated:一个被验证的Subject在Shiro的当前session中已经成功验证(即登录成功后未抛出异常)。若Subject被验证,则调用subject.isAuthenticated()就会返回true。

注:Remembered和Authenticated两者是互斥的,若一个为true,则另一个为false,反之亦然。

从上述的两个状态的介绍来看,由于Remembered Subject是上次身份验证后保留的状态,而Authenticated Subject是身份验证保留的状态,因此Remembered的身份没有Authenticated的身份更加“真实”(因为Remembered的身份是不用输入凭证的,所以当前身份有可能不是实际拥有者)。例如:在电子商城,你可以用Remembered身份得到自己的个人信息和添加商品到购物车,但是修改个人信息和提交订单就必须用Authenticated身份,这样才能保证当前身份的利益。

注销

当Subject在应用程序验证身份通过后,可以通过subject.logout()方法解除(注销)与应用程序的绑定关系。

currentUser.logout(); // 删除当前Subject所有的信息,并且使其的session失效
若在Web应用程序调用logout()方法,则Subject的RememberMe cookie也会被删除,由于删除cookie是在响应主体之后,因此建议将Subject重定向到新的视图或页面,这样才能保证删除任何与安全相关的cookie。

身份验证过程

到此为止,我们仅仅了解在应用程序中如何验证Subject身份,现在,我们来看Shiro在验证Subjec身份时执行的过程,从下图来看,Shiro在验证Subject身份基本划分为5步。


      Step1:应用程序代码通过构建AuthenticationToken实例去收集最终用户的身份和凭证,并调用Subject.login方法。

      Step2:这个Subject实例通常是DelegatingSubject(或其子类)的实例。在实际认证开始,会委托应用程序的SecurityManager实例去调用sercurityManager.login(token)方法。

      Step3:SecurityManager是Shiro基础核心组件,它负责接收token,并简单委托内部Authenticator实例去调用authenticator.authenticate(token)方法。该内部实例基本上使用的是继承Authenticator的ModularRealmAuthenticator实例,因为它支持在身份验证期间整合一个或多个Realm实例。它为Shiro提供可插拔的验证方式。

      Step4:若在应用程序配置多个Realm,则ModularRealmAuthenticator实例将会根据配置的AuthenticationStrategy(认证策略)进行多个Realm认证。若在应用程序配置单个Realm,则不需要配置AuthenticationStrategy进行单个Realm认证,直接调用Realm即可。

      Step5:每个Realm都会判断是否支持提交AuthenticationToken实例,如果是,则支持的Realm将会调用getAuthenticationInfo(token)方法。在这特殊的Realm中,getAuthenticationInfo方法有效的代表了一个身份验证的尝试。我们将覆盖Realm的doGetAuthenticationInfo方法来自定义认证处理。

Authenticator(认证)

正如前面提及的,Shiro securityManager默认实现使用的是ModularRealmAuthenticator实例,它在应用程序中支持单个或多个Realm。

在单个Realm的应用程序,ModularRealmAuthenticator将直接调用单个Realm。如果是多个Realm的应用程序,它将调用AuthenticationStrategy(认证策略)实例来协调Realm的执行。

自定义的Authenticator的shiro.ini文件配置示例如下:

[main]
...
authenticator = com.foo.bar.CustomAuthenticator
securityManager.authenticator = $authenticator
如你所见,com.foo.bar.CustomAuthenticator就是自定义Authenticator。

AuthenticationStrategy(认证策略)

当在应用程序配置多个Realm时,ModularRealmAuthenticator根据内部定义的AuthenticationStrategy组件的条件来判断认证是成功还是失败。

例如,仅仅只有一个Realm验证成功,而其他的Realm验证失败,那这次认证是成功了吗?或者是所有的Realm都验证成功,认证才算成功吗?或者如果一个Realm验证成功,则还需要验证其他的Realm吗?AuthenticationStrategy(认证策略)就是根据应用程序的这些需求做出判断。

AuthenticationStrategy(认证策略)是一个无状态的组件,在认证过程会经过四次调用:

  1. 在所有的Realm被调用之前
  2. 单个Realm在调用getAuthenticationInfo方法之前
  3. 单个Realm在调用getAuthenticationInfo方法之后
  4. 在所有的Realm被调用之后

AuthenticationStrategy(认证策略)负责聚合所有验证成功的Reaml结果,封装成一个AuthenticationInfo实例,最后返回该实例,并把它作为Subject最终身份信息。

如果你的应用程序通过多个Realm从多个数据获取账号信息,AuthenticationStrategy(认证策略)将聚合这些信息返回给Subject的身份。

Shiro有3种具体的认证策略

AuthenticationStrategy Class描述
AtLeastOneSuccessfulStrategy一个或多个Realm验证成功,就认为认证成功
FirstSuccessfulStrategy第一个Realm验证成功,就认为认证成功,其它的Realm将会被忽略
AllSuccessfulStrategy所有配置的Realm验证成功,才认为认证成功

ModularRealmAuthenticator默认的认证策略是AtLeastOneSuccessfulStrategy,这也是最常见的一种策略,当然你可以配置不同的策略,例如shiro.ini配置:

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

securityManager.authenticator.authenticationStrategy = $authcStrategy

...
自定义认证策略:如果你要想创建属于自己的认证策略,你需要继承org.apache.shiro.authc.pam.AbstractAuthenticationStrategy。AbstractAuthenticationStrategy类自动实现聚合功能,合并所有Realm返回的结果到一个AuthenticationInfo实例。

Realm验证顺序

ModularRealmAuthentication会存取SecurityMananger配置的Realm实例,当执行认证时,它将会迭代Realm集合,若该Realm支持验证提交的AuthenticationToken,则调用Realm的getAuthenticationInfo方法。

Realm验证顺序分为隐式顺序和明确顺序。若你在shiro.ini文件不配置“securityManager.realms”,则为隐式顺序,否则反之,看shiro.ini示例:

blahRealm = com.company.blah.Realm
fooRealm = com.company.foo.Realm
barRealm = com.company.another.Realm
上述的示例就是隐式顺序,它没有配置securityManager.realms,执行顺序:$blahRealm -> $fooRealm -> $barRealm,它等同于下一个shiro.ini示例:

blahRealm = com.company.blah.Realm
fooRealm = com.company.foo.Realm
...
<pre name="code" class="plain">shortRealm = com.company.short.Realm
barRealm = com.company.another.RealmsecurityManager.realms = $blahRealm, $fooRealm, $barRealm

 上述的示例就是明确顺序,它会按照securityManager.realms配置的顺序执行:$blahRealm -> $fooRealm -> $barRealm,而shortRealm则不会执行。


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值