一、shiro默认密码的比对
通过 AuthenticatingRealm 的 credentialsMatcher 属性来进行的密码的比对
/**源码org.apache.shiro.realm.AuthenticatingRealm * Asserts that the submitted {@code AuthenticationToken}'s credentials match the stored account * {@code AuthenticationInfo}'s credentials, and if not, throws an {@link AuthenticationException}. * * @param token the submitted authentication token * @param info the AuthenticationInfo corresponding to the given {@code token} * @throws AuthenticationException if the token's credentials do not match the stored account credentials. */ protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info)
throws AuthenticationException { CredentialsMatcher cm = getCredentialsMatcher(); if (cm != null) { if (!cm.doCredentialsMatch(token, info)) { //not successful - throw an exception to indicate this: String msg = ""; throw new IncorrectCredentialsException(msg); } } else { throw new AuthenticationException(""); } }
调试技巧:在org.apache.shiro.authc.UsernamePasswordToken的getPassword()方法中添加断点
①、接口CredentialsMatcher
源码 package org.apache.shiro.authc.credential; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; public interface CredentialsMatcher { boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info); }
②、接口CredentialsMatcher的继承关系
shiro默认是用org.apache.shiro.authc.credential.SimpleCredentialsMatcher进行密码比较
//SimpleCredentialsMatcher.doCredentialsMatch()
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { Object tokenCredentials = getCredentials(token); Object accountCredentials = getCredentials(info); return equals(tokenCredentials, accountCredentials); }
二、MD5加密
使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations) 来计算盐值加密后的密码的值
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
public static void main(String[] args){ //加密方式 String hashAlgorithmName = "MD5"; //明文密码 Object credentials = "1234"; //盐值 Object salt = ByteSource.Util.bytes("nchu"); int hashIterations = 1024; Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
//加密后的密码 System.out.println(result); }
三、Shiro密码加密
①、在spring核心配置文件中配置自定义Realm
<!-- 自定义Realm --> <bean id="MD5Realm" class="com.nchu.shiro.MD5Realm"> <!-- 更改自定义Realm的默认credentialsMatcher属性--> <property name="credentialsMatcher" > <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!--指定加密算法--> <property name="hashAlgorithmName" value="MD5"></property> <!--设置加密次数--> <property name="hashIterations" value="1024"></property> </bean> </property> </bean> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realms"> <list> <ref bean="MD5Realm"/> </list> </property> </bean>
替换当前 Realm 的 credentialsMatcher 属性. 直接使用 HashedCredentialsMatcher 对象, 并设置加密算法即可
②、自定义Realm
使用shiro MD5加密前提数据库存储的密码是经过MD5加密的
import com.nchu.mvc.dao.ShiroRealmMapper; import org.apache.shiro.authc.*; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.realm.AuthenticatingRealm; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; /** * Created by yangshijing on 2018/1/16 0016. */ public class MD5Realm extends AuthenticatingRealm { @Autowired ShiroRealmMapper shiroRealmMapper; @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("----->"+token.hashCode()); //1. 把 AuthenticationToken 转换为 UsernamePasswordToken UsernamePasswordToken upToken = (UsernamePasswordToken) token; //2. 从 UsernamePasswordToken 中来获取 username String username = upToken.getUsername(); //3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录 System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息."); String password = shiroRealmMapper.login(username); //4. 若用户不存在, 则可以抛出 UnknownAccountException 异常 if(password==null){ throw new UnknownAccountException("用户不存在!"); } //5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常. /* if("monster".equals(username)){ throw new LockedAccountException("用户被锁定"); }*/ //6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回.
//通常使用的实现类为: SimpleAuthenticationInfo //以下信息是从数据库中获取的. //1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象. Object principal = username; //2). hashedCredentials: 加密后的密码. Object hashedCredentials = password; //3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可 String realmName = getName(); //4). 加密盐值 ByteSource credentialsSalt = ByteSource.Util.bytes("nchu"); SimpleAuthenticationInfo info = //new SimpleAuthenticationInfo(principal, credentials, realmName); new SimpleAuthenticationInfo(principal,hashedCredentials,credentialsSalt,realmName); return info; }
注意:在 doGetAuthenticationInfo 方法返回值创建 SimpleAuthenticationInfo 对象的时候, 需要使用SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName) 构造器