Authentication(身份验证)
身份验证是身份验证的过程-也就是说,证明用户实际上就是他们所说的真实身份。
为了使用户证明自己的身份,他们需要提供一些标识信息以及系统可以理解和信任的那种身份证明。
这是通过向Shiro提交用户的主体和凭据来完成的,以查看它们是否与应用程序期望的匹配。
Principals
Principals 是主题的“识别属性”。
Principals 可以是可以识别主题的任何东西,例如名字(姓氏),姓氏(姓氏或姓氏),用户名,社会保险号等。
当然,诸如姓氏之类的东西并不擅长唯一地标识主题,因此用于身份验证的最佳主体对于应用程序是唯一的-通常是用户名或电子邮件地址。
主要主体
尽管Shiro可以代表任意数量的主体,但Shiro希望应用程序具有一个“主要”主体-一个唯一标识应用程序中“主题”的值。
在大多数应用程序中,这通常是用户名,电子邮件地址或全局唯一的用户ID。
Credentials(凭证)
凭证通常是仅由主体知道的秘密值,用作其实际上“拥有”所主张身份的佐证。
凭据的一些常见示例是密码,生物特征数据(例如指纹和视网膜扫描)以及X.509证书。
主体/凭证配对的最常见示例是用户名和密码。
用户名是声明的身份,密码是与声明的身份匹配的证明。
如果提交的密码与应用程序期望的密码匹配,则该应用程序可以在很大程度上假定用户确实是他们所说的那个人,因为没有其他人应该知道相同的密码。
验证主题
验证主题的过程可以有效地分为三个不同的步骤:
-
收集主题提交的主体和证书
-
提交主体和凭据以进行身份验证。
-
如果提交成功,则允许访问,否则重试身份验证或阻止访问。
以下代码演示了Shiro的API如何反映这些步骤:
第一步:收集主题提交的主体和证书
//Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//"Remember Me" built-in:
token.setRememberMe(true);
在这种情况下,我们使用的是UsernamePasswordToken,它支持最常用的用户名/密码身份验证方法。这是Shiro的org.apache.shiro.authc.AuthenticationToken接口的实现,该接口是Shiro身份验证系统用来表示提交的主体和凭据的基本接口。
在这里需要注意的重要一点是Shiro不在乎您如何获取此信息:数据可能是由提交HTML表单的用户获取的,或者是从HTTP标头中检索的,或者是从Swing或Flex中读取的GUI密码形式,或通过命令行参数。从应用程序最终用户收集信息的过程与Shiro的AuthenticationToken概念完全脱钩。
您可以随意构造和表示AuthenticationToken实例-它与协议无关。
此示例还表明,我们已表明我们希望Shiro为身份验证尝试执行“记住我”服务。这样可以确保Shiro能够在以后返回应用程序时记住用户身份。我们将在下一章介绍“记住我”服务。
步骤2:提交主体和凭据
在收集了主体和凭据并将其表示为AuthenticationToken实例之后,我们需要将该令牌提交给Shiro来执行实际的身份验证尝试:
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
在获取当前正在执行的Subject之后,我们进行一次登录调用,传入我们之前创建的AuthenticationToken实例。
对登录方法的调用有效地表示身份验证尝试。
步骤3:处理成功或失败
如果登录方法悄悄返回,就可以了-我们完成了! 主题已通过身份验证。
应用程序线程可以不间断地继续运行,对 SecurityUtils.getSubject() 的所有其他调用将返回经过身份验证的Subject实例,以及对subject的任何调用。 isAuthenticated() 将返回true。
但是,如果登录尝试失败怎么办?
例如,如果最终用户提供了错误的密码,或者访问了系统太多次并且可能他们的帐户被锁定了怎么办?
Shiro具有丰富的运行时AuthenticationException层次结构,可以准确说明尝试失败的原因。
您可以将登录信息包装在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?
}
//No problems, continue on as expected...
如果现有的异常类之一不能满足您的需求,则可以创建自定义AuthenticationExceptions来表示特定的故障情况。
登录失败提示
尽管您的代码可以对特定的异常做出反应并在必要时执行逻辑,但是安全性最佳做法是仅在发生失败时向最终用户显示通用失败消息,例如“用户名或密码错误”。
这样可确保没有任何特定信息可供尝试攻击媒介的黑客使用。