Shiro中的MD5和盐

Shiro的MD5和Salt

1. 原理

用户注册时

  • 生成一段随机字符串,一般使用UUID,生成的字符串即所谓的salt
  • 将用户输入的密码+salt,整体进行加密,
  • 将加密后的数据存储到数据库,
  • salt也应该存储到数据库中。

用户登录时

  • 将用户输入的密码+salt,整体进行加密
  • 与数据库中存储的数据进行比对
  • 相同则为密码正确,不同则为密码错误

2. md5哈希散列

/**
 * @Author: Hjx
 * @Date: 2021/8/9 17:24
 */
public class TestShiroMd5 {
    public static void main(String[] args) {
        // 参数1:字符串的值,即需要加密的字符串
        Md5Hash md5Hash = new Md5Hash("123");
        System.out.println(md5Hash.toHex());
        // 参数2:salt
        Md5Hash md5Hash1 = new Md5Hash("123", "hjx");
        System.out.println(md5Hash1);
        // 参数3:散列值,即散列多少次
        Md5Hash md5Hash2 = new Md5Hash("123", "hjx", 1024);
        System.out.println(md5Hash2);
    }
}

3. shiro添加md5

shiro中凭证信息的查验是在AuthenticatingRealm类中的assertCredentialsMatch函数实现的

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) {
        // 在这里进行凭证信息的验证
        assertCredentialsMatch(token, info);
    } else {
        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
    }

    return info;
}
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    // 获取凭证适配器
    CredentialsMatcher cm = getCredentialsMatcher();
    if (cm != null) {
        // 默认的进行 字符串的匹配 equals 比较
        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.");
    }
}

所以,如果我们要使用md5加密,可以修改 凭证适配器的类型

在这里插入图片描述

Test测试类

public class TestMd5Authenticator {
    public static void main(String[] args) {
        // 创建SecurityManager安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        // 给SecurityManager设置自定义Realm
        CustomerMd5Realm customerMd5Realm = new CustomerMd5Realm();

        // 设置md5 凭证适配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        customerMd5Realm.setCredentialsMatcher(hashedCredentialsMatcher);

        defaultSecurityManager.setRealm(customerMd5Realm);
        // 给安全工具类SecurityUtils 设置 SecurityManager
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        // 通过安全工具类SecurityUtils获取 主体subject
        Subject subject = SecurityUtils.getSubject();
        // 创建Token,模拟接收前端用户输入的用户名和密码
        UsernamePasswordToken token = new UsernamePasswordToken("hjx", "123");
        try {
            System.out.println("认证状态 : "+subject.isAuthenticated());
            subject.login(token);
            System.out.println("认证状态 : "+subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

自定义的Realm

/**
 * @Author: Hjx
 * @Date: 2021/8/9 18:03
 */
public class CustomerMd5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 从 token 中获取用户输入的身份凭证
        String userName = (String) token.getPrincipal();
        // 模拟从数据库查询出的用户名和密码
        String name = "hjx";
        String pass = "3edd36faca731a93a8a00be8490d2276";

        if (name.equals(userName)){
            /*
                SimpleAuthenticationInfo 是 AuthenticationInfo 的实现类
                参数1:用户名
                参数2:md5加密后的密码
                参数3:自定义realm的名称
             */
            return new SimpleAuthenticationInfo(name, pass, this.getName());
        }
        return null;
    }
}

4. 加盐

修改自定义Realm

/**
 * @Author: Hjx
 * @Date: 2021/8/9 18:03
 */
public class CustomerMd5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 从 token 中获取用户输入的身份凭证
        String userName = (String) token.getPrincipal();
        // 模拟从数据库查询出的用户名和密码
        String name = "hjx";
        String pass = "3edd36faca731a93a8a00be8490d2276";
        String credentialsSalt = "hjx";

        if (name.equals(userName)){
            /*
                SimpleAuthenticationInfo 是 AuthenticationInfo 的实现类
                参数1:用户名
                参数2:md5加密后的密码
                参数3:盐值,类型为ByteSource,需要进行格式转换
                参数4:自定义realm的名称
             */
            return new SimpleAuthenticationInfo(name, pass, ByteSource.Util.bytes(credentialsSalt), this.getName());
        }
        return null;
    }
}

5. 添加散列次数

修改Test测试类

/**
 * @Author: Hjx
 * @Date: 2021/8/9 17:57
 */
public class TestMd5Authenticator {
    public static void main(String[] args) {
        // 创建SecurityManager安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        // 给SecurityManager设置自定义Realm
        CustomerMd5Realm customerMd5Realm = new CustomerMd5Realm();

        // 设置md5 凭证适配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 设置加密方式
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置散列次数
        hashedCredentialsMatcher.setHashIterations(1024);
        customerMd5Realm.setCredentialsMatcher(hashedCredentialsMatcher);

        defaultSecurityManager.setRealm(customerMd5Realm);
        // 给安全工具类SecurityUtils 设置 SecurityManager
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        // 通过安全工具类SecurityUtils获取 主体subject
        Subject subject = SecurityUtils.getSubject();
        // 创建Token,模拟接收前端用户输入的用户名和密码
        UsernamePasswordToken token = new UsernamePasswordToken("hjx", "123");
        try {
            System.out.println("认证状态 : "+subject.isAuthenticated());
            subject.login(token);
            System.out.println("认证状态 : "+subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

代码及资料地址:hjx: 知道的越多,越发觉自己的无知 (gitee.com)
视频学习参考:【编程不良人】2020最新版Shiro教程,整合SpringBoot项目实战教程_哔哩哔哩_bilibili

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值