公司项目中用的是shiro做安全认证框架,从代码中看到了判断验证码的,也看到了判断用户名是否存在的,就是没有发现判断密码是否正确的,后从网上文章以及查看源码才大概了解shiro对于密码验证的流程。
自定义的shiroRealm
public class ShiroRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 第一步从token中取出用户名
String userName = (String) token.getPrincipal();
// 第二步:根据用户输入的userName从数据库查询
User user = userService.findByUsername("userName");
if(user==null){
return null;//用户不存在
}
//第三步 从数据库取该用户的passw
String password = user.getPassword();
// 第四步 加盐
String salt = userCode;
.......其他判断逻辑......
// 第五步 创建SimpleAuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,password,ByteSource.Util.bytes(salt), this.getName());
//第六步 返回
return simpleAuthenticationInfo;// return的过程完成 password的验证
}
}
注意:最后的return simpleAuthenticationInfo 的时候就会触发password验证。
我们要知道一个继承关系
shiroRealm----->AuthorizingRealm---->AuthenticatingRealm
当执行"return simpleAuthenticationInfo"之后,会调用AuthenticatingRealm的getAuthenticationInfo()方法
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = getCachedAuthenticationInfo(token);
if (info == null) {
//otherwise not cached, perform the lookup:
info = doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
if (info != null) {
//验证token,info的数据
assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
上面代码中又调用了assertCredentialsMatch(token, info);
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
//判断验证是否通过,如果不通过则抛出异常(这个异常将在LoginController中捕获并处理)
if (!cm.doCredentialsMatch(token, info)) {
//not successful - throw an exception to indicate this:
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}
继续看doCredentialsMatch()的源码
调用的是类HashedCredentialsMatcher的方法
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenHashedCredentials = hashProvidedCredentials(token, info);//这里将得到页面传递来的password通过加密后的结果
Object accountCredentials = getCredentials(info);//这里得到是数据库的passwrod通过加密后的结果
return equals(tokenHashedCredentials, accountCredentials);
}
到这里就可看到password验证的大致流程,
如果返回true,那么验证就通过了。
如何返回false,那么上面的AuthenticatingRealm.assertCredentialsMatch()方法会抛出 IncorrectCredentialsException异常
在我们的LoginController中可以看到捕获shiro中异常的代码
@Controller
public class LoginController{
@RequestMapping(value="login")
public String login(HttpServletRequest request)throws Exception{
//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
//根据shiro返回的异常类路径判断,抛出指定异常信息
if(exceptionClassName!=null){
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
//最终会抛给异常处理器
isSucCode = 1;
errInfo = "账号不存在或已作废";
/*throw new CustomException("账号不存在");*/
} else if (AuthenticationException.class.getName().equals(exceptionClassName)){
//最终会抛给异常处理器
isSucCode = 1;
errInfo = "账号不存在或已作废";
}else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
isSucCode = 2;
errInfo = "密码错误";
/*throw new CustomException("密码错误");*/
} else if("randomCodeError".equals(exceptionClassName)){
isSucCode = 3;
errInfo = "验证码错误";
/*throw new CustomException("验证码错误");*/
}else {
isSucCode = 4;
errInfo = "未知错误,请联系管理员";
/*throw new CustomException("未知错误,请联系管理员");*/
}
}
}
}
可以看到获取IncorrectCredentialsException异常后,提示密码错误给前段页面.