Shiro入门 -- shiro登录认证之密码比对和源码探究

1、自定义的ShiroRealm,继承

AuthenticatingRealm
//使用认证功能 AuthenticatingRealm
public class ShiroRealm extends AuthenticatingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.把AuthenticationToken转换为UsernamePasswordToken
        //authenticationToken保存了页面表单输入的用户名和密码信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //2.从UsernamePasswordToken中获取username
        String username = token.getUsername();
        //3.从数据库查询对应的用户记录
        System.out.println("从数据库获取:" + username + "的信息");
        //4.若用户不存在
        if("unkonwn".equals(username)) {
            throw new UnknownAccountException("用户不存在");
        }

        //5.根据用户信息情况,构建AuthenticationInfo,通常使用的实现类为:SimpleAuthenticationInfo
        //以下信息是从数据库获取的
        //1). principal:认证的实体信息,可以是username,也可以是数据表对应的实体类对象
        Object principal = username;
        //2). credentials:密码
        Object credentials = "1234";
        //3). realmName:当前realm对象的name,调用父类的getName()
        String realmName = getName();
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);

        return info;
    }

2、配置applicationContext.xml

<!--
            7.配置哪些页面需要受保护 以及 访问这些页面需要的权限
            1) anon 代表可以被匿名访问
            2) authc 代表认证(登录)后才可以被访问的页面
            3) logout 代表登出
        -->
        <property name="filterChainDefinitions">
            <value>
                /shiro/login = anon
                /login.jsp = anon
                /shiro/logout = logout

                /list.jsp = authc
                /** = authc
            </value>
        </property>

3、根据自定义的Realm,我们知道

UsernamePasswordToken保存了页面表单输入的用户名和密码信息,
SimpleAuthenticationInfo保存着数据库中用户的密码,那么密码的比对shiro如何完成的呢?什么时候进行比对呢?

见源码Debug:

(1)UsernamePasswordToken

(2)密码比对

我们可以发现,密码的比对是明文进行,显然是不可取、不安全。数据表应该保存加密之后的密码

那么,我们应该如何对用户输入的密码进行加密呢?

还是查看源码,我们通过

CredentialsMatcher来进行加密,从数据库获取的密码应该是加密之后的字符串。

(3)做法:替换当前Realm的credentialsMatcher属性,直接使用HashedCredentialsMatcher对象,并设置加密算法

 

4、配置凭证匹配器 

CredentialsMatcher以及密码的MD5加密

(1)applicationContext.xml中进行配置

<!--
        3.配置Realm
        3.1直接配置一个实现了org.apache.shiro.realm.Realm接口的bean
    -->
    <bean id="jdbcRealm" class="com.dhu.shiro.realms.ShiroRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5"></property>
            </bean>
        </property>
    </bean>

(2)CredentialsMatcher源码查看

(3)加密的源码查看

(4)源码简单分析:SimpleHash中包含了salt称为盐值、hashIterations加密次数,hashIterations可以在配置文件进行设置

   protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
        String hashAlgorithmName = assertHashAlgorithmName();
        return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
    }

(5)根据源码HashedCredentialsMatcher.java 中 hashProvidedCredentials 方法,进行配置

<!--
        3.配置Realm
        3.1直接配置一个实现了org.apache.shiro.realm.Realm接口的bean
    -->
    <bean id="jdbcRealm" class="com.dhu.shiro.realms.ShiroRealm">
        <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>

 (6)修改自定义的Realm

        //5.根据用户信息情况,构建AuthenticationInfo,通常使用的实现类为:SimpleAuthenticationInfo
        //以下信息是从数据库获取的
        //1). principal:认证的实体信息,可以是username,也可以是数据表对应的实体类对象
        Object principal = username;
        //2). credentials:密码
        Object credentials = "a20b8e682a72eeac0049847855cecb86";
        //3). realmName:当前realm对象的name,调用父类的getName()
        String realmName = getName();
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
        return info;
    }

    // 测试加密后的密码
    public static void main(String[] args) {
        String hashAlgorithmName = "MD5";
        Object credentials = "1234";
        Object salt = null;
        int hashIterations = 1024;
        SimpleHash res = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        System.out.println(res); //a20b8e682a72eeac0049847855cecb86
    }
}

(7)再次Debug,发现加密后密码一样,同时登录成功~

5、 MD5盐值加密

这样设置之后,我们还是发现有一个问题。若明文输入的一样的,经过MD5加密后的密码也是一样的,依然不安全。我们希望即使两个原始密码一样,经过加密后的结果也不一样。

需要加点佐料,加盐salt

(1)加密后的结果需要把salt加进去

SimpleHash res = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);

(2)返回值

SimpleAuthenticationInfo需要把盐salt带上
SimpleAuthenticationInfo需要把salt带上

6、修改自定义的Realm以及测试加密后的密码

盐值需要保证唯一性:

//4). 盐值
ByteSource salt = ByteSource.Util.bytes(username); //唯一性
//使用认证功能 AuthenticatingRealm
public class ShiroRealm extends AuthenticatingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.把AuthenticationToken转换为UsernamePasswordToken
        //authenticationToken保存了页面表单输入的用户名和密码信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //2.从UsernamePasswordToken中获取username
        String username = token.getUsername();
        //3.从数据库查询对应的用户记录
        System.out.println("从数据库获取:" + username + "的信息");
        //4.若用户不存在
        if("unkonwn".equals(username)) {
            throw new UnknownAccountException("用户不存在");
        }

        //5.根据用户信息情况,构建AuthenticationInfo,通常使用的实现类为:SimpleAuthenticationInfo
        //以下信息是从数据库获取的
        //1). principal:认证的实体信息,可以是username,也可以是数据表对应的实体类对象
        Object principal = username;
        //2). credentials:密码
        Object credentials = "null";
        if("admin".equals(username)) {
            credentials = "c34af346c89b8b03438e27a32863c9b5";
        }
        if("user".equals(username)) {
            credentials = "3e042e1e3801c502c05e13c3ebb495c9";
        }
        //3). realmName:当前realm对象的name,调用父类的getName()
        String realmName = getName();
        //4). 盐值
        ByteSource salt = ByteSource.Util.bytes(username); //唯一性

        SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
        info = new SimpleAuthenticationInfo(principal, credentials, salt, realmName);
        return info;
    }

    // 测试加密后的密码
    public static void main(String[] args) {
        String hashAlgorithmName = "MD5";
        Object credentials = "1234";
        Object salt = ByteSource.Util.bytes("user");
        int hashIterations = 1024;
        SimpleHash res = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        System.out.println(res);
    }
}

7、笔记总结

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值