shiro笔记之

编程入门 行业动态 更新时间:2024-10-22 18:43:56

shiro<a href=https://www.elefans.com/category/jswz/34/1770047.html style=笔记之"/>

shiro笔记之

公司项目中用的是shiro做安全认证框架,从代码中看到了判断验证码的,也看到了判断用户名是否存在的,就是没有发现判断密码是否正确的,后从网上文章以及查看源码才大概了解shiro对于密码验证的流程。

自定义的shiroRealm

public class ShiroRealm extends AuthorizingRealm {@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// 第一步从token中取出用户名String userName = (String) token.getPrincipal();// 第二步:根据用户输入的userName从数据库查询User user = userService.findByUsername("userName");		if(user==null){return null;//用户不存在}//第三步  从数据库取该用户的passwString password = user.getPassword();// 第四步  加盐String salt = userCode;.......其他判断逻辑......// 第五步  创建SimpleAuthenticationInfoSimpleAuthenticationInfo 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异常后,提示密码错误给前段页面.

 

补充:

1.上面进行密码校验用到了盐,并且自己定义了密码校验的配置文件,如下:

public class BigScreenMatcher  extends HashedCredentialsMatcher {public BigScreenMatcher() {}/*** 密码校验* @param token  页面传过来的密码* @param info  查询出的数据库密码* @return*/@Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {PasswordHelper passwordHelper = new PasswordHelper();BigScreenToken bigScreenToken = (BigScreenToken)token;return info.getCredentials().toString().equals(passwordHelper.encryptPassword(bigScreenToken.getPassword(), bigScreenToken.getSalt()));}
}

2.如果不加盐的话,可以直接使用下面的配置进行密码校验,不用自己再定义上面的类来校验:

/*** 凭证匹配器* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了* 所以我们需要修改下doGetAuthenticationInfo中的代码;* )* 可以扩展凭证匹配器,实现 输入密码错误次数后锁定等功能,下一次*/@Bean(name = "credentialsMatcher")public HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();//散列算法:这里使用MD5算法;hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列的次数,比如散列两次,相当于 md5(md5(""));hashedCredentialsMatcher.setHashIterations(2);//storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);return hashedCredentialsMatcher;}

 

更多推荐

shiro笔记之

本文发布于:2024-02-06 05:16:24,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1746715.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:笔记   shiro

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!