zoukankan      html  css  js  c++  java
  • shiro入门学习--使用MD5和salt进行加密|练气后期

    写在前面

    在上一篇文章《Shiro入门学习---使用自定义Realm完成认证|练气中期》当中,我们学会了使用自定义Realm实现shiro数据源的切换,我们可以切换成从关系数据库如MySQL中读取用户认证信息进行认证,亦可从非关系型数据库例如mongodb中读取用户认证信息进行认证。这是一个伟大的进度,这使得我们可以使用shiro来提升我们应用程序的安全度了,

    那么,请大家思考一个问题,我们的应用程序真的安全了吗?

    我把咱么上一篇文章当中的认证方法代码摘抄在下面给大家看看

    /**认证
     * @author 赖柄沣 bingfengdev@aliyun.com
     * @date 2020-10-04 11:01:50
     * @param authenticationToken
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @throws AuthenticationException
     * @version 1.0
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 1. 从token中获取用户名
        String principal = (String) authenticationToken.getPrincipal();
    
        //2. 根据用户名查询数据库并封装成authenticationinfo对象返回(模拟)
        if (principal == "xiangbei") {
            AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei","123",this.getName());
            return authInfo;
        }
    
        return null;
    }
    

    在16行当中,我们模拟从数据库当中查询出了用户的注册信息,包括账户和密码,并且这里的密码是明文的。这意味着如果我们的用户密码被泄露了(这里用户原因导致的泄露除外),那么一些不友好的朋友将可以随意的进出我们的系统。这不但让我们的应用程序变得不安全,而且还会让我们面临法律风险。

    以下内容摘自《网络安全法》

    第三十四条 网络运营者应当建立健全用户信息保护制 度,加强对用户个人信息、隐私和商业秘密的保护

    第三十五条 网络运营者收集、使用公民个人信息,应当 遵循合法、正当、必要的原则,明示收集、使用信息的目 的、方式和范围,并经被收集者同意。 网络运营者不得收集与其提供的服务无关的公民个人 信息,不得违反法律、行政法规的规定和双方的约定收 集、使用公民个人信息,并应当依照法律、行政法规的规 定或者与用户的约定,处理其保存的公民个人信息。 网络运营者收集、使用公民个人信息,应当公开其收 集、使用规则。

    第三十六条 网络运营者对其收集的公民个人信息必须严 格保密,不得泄露、篡改、毁损,不得出售或者非法向他 人提供。 网络运营者应当采取技术措施和其他必要措施,确保 公民个人信息安全,防止其收集的公民个人信息泄露、毁 损、丢失。在发生或者可能发生信息泄露、毁损、丢失的 情况时,应当立即采取补救措施,告知可能受到影响的用 户,并按照规定向有关主管部门报告。

    所以,我们需要对用户信息进行加密保护。对于账户密码信息,我们应该采取不可逆的加密方式。也就是说,我们对密码进行加密存储后,哪怕其获取了我们的密文,他也不能得到我们的密码明文。这样就对我们的用户信息起到了一个很好的保护作用。

    MD5加密算法和salt盐值加密

    MD5加密算法

    什么是MD5加密

    MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

    特点

    1. 不可逆,也就是说其本身上不能由密文推出明文,

      但是,如果明文比较简单常见,还是存在泄露风险,例如先生成好简单明文的密文,然后使用穷举法进行破解;

    2. 对于同一个明文,无论加密多少次其密文都是一样的;

    3. 生成的结果始终是一个16进制的32位字符串。

    作用

    1. 数字签名(校验和)

      例如对于一份文件,为了保证网络传输当中不发生改变,我提前对其用md5加密算法进行加密,得到一段密文。我将这份文件和密文分别发给你。你在收到文件后也对其使用md5加密一次,得到一个密文。这时,你就可以比较两个密文是否一致,如果一致,则文件没有被篡改,反之,文件已经被篡改。

    2. 加密

    3. 垃圾邮件筛选

      原理和作用1一样

    salt盐值加密策略

    在上面的介绍md5加密算法时我们讲到,虽然MD5算法本身不可逆,但是如果用户采用简单的字符串作为密码的话,仍然有被暴力破解的风险。因此,为了解决这个问题,我们需要在对密码加密之前使其变得复杂化。

    而加盐就是其中的一种方式。所谓的加盐就是在原密码的基础上,加上一段随机字符串。然后再加密。

    当然,如果盐值随着密码一起被泄露出去,也是存在着密码被破解的风险的,我们只能做到相对安全。

    为了增加破解难度,可以在加盐时采取一定的策略,例如哈希加盐、加密后多次哈希。

    当然,这要在安全跟性能直接做个平衡。

    shiro使用MD5+salt加密

    分析

    在进行编码之前,我们需要理一下流程:

    1. 用户注册或系统分配账户时,服务层在接收到账号和凭证信息后,先对凭证信息采用md5+salt进行加密处理,然后将账号、加密后的密码还有盐值存入数据库;

    2. 用户登录请求接收后,先根据请求中的账号查询数据库:

      2.1 如果没有查到,直接返回“用户名或密码错误”的类似提示

      2.2 如果查到了账户信息,就执行步骤3;

    3. 将账号和加盐后凭证封装成AuthenticationInfo对象返回给shiro,shiro执行步骤4

    4. 对请求中的凭证进行加盐处理并执行步骤5

    5. 对加盐后的凭证进行md5加密,并将密文跟数据库当中的存储的密文进行比对:

      5.1 如果匹配成功,则认证通过

      5.2 如果匹配失败,则返回“用户名或密码错误”的类似提示

    实现

    编写自定义Realm并切换掉默认的凭证匹配器

    /**自定义Realm对象
     * @author 赖柄沣 bingfengdev@aliyun.com
     * @version 1.0
     * @date 2020/10/4 11:00
     */
    public class MySqlRealm extends AuthorizingRealm {
    
        public MySqlRealm() {
            //设置凭证匹配器,修改为hash凭证匹配器
            HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
            //设置算法
            myCredentialsMatcher.setHashAlgorithmName("md5");
            //散列次数
            myCredentialsMatcher.setHashIterations(1024);
            this.setCredentialsMatcher(myCredentialsMatcher);
        }
    
        /**授权
         * @author 赖柄沣 bingfengdev@aliyun.com
         * @date 2020-10-04 11:01:50
         * @param principalCollection
         * @return org.apache.shiro.authz.AuthorizationInfo
         * @throws AuthenticationException
         * @version 1.0
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
            return null;
        }
    
        /**认证
         * @author 赖柄沣 bingfengdev@aliyun.com
         * @date 2020-10-04 11:01:50
         * @param authenticationToken
         * @return org.apache.shiro.authz.AuthorizationInfo
         * @throws AuthenticationException
         * @version 1.0
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            // 1. 从token中获取用户名
            String principal = (String) authenticationToken.getPrincipal();
    
            //2. 根据用户名查询数据库并封装成authenticationinfo对象返回(模拟)
            if (principal == "xiangbei") {
                //四个参数分别是数据库中的账号、加密后的密码、盐值、realm名字
                AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei",
                        "ff595c47b51b4cf70fddce090f68879e",
                        ByteSource.Util.bytes("ee575f62-0dda-44f2-b75e-4efef795018f"),
                        this.getName());
                return authInfo;
            }
    
            return null;
        }
    }
    

    编写认证器

    /**认证管理器
     * @author 赖柄沣 bingfengdev@aliyun.com
     * @version 1.0
     * @date 2020/10/4 11:11
     */
    public class CurrentSystemAuthenticator {
        private DefaultSecurityManager securityManager;
        public CurrentSystemAuthenticator() {
            //创建安全管理器
            securityManager = new DefaultSecurityManager();
    
            //设置自定义realm
            this.securityManager.setRealm(new MySqlRealm());
    
            //将安全管理器设置到安全工具类中
            SecurityUtils.setSecurityManager(securityManager);
    
        }
    
        public void authenticate(String username,String password) {
            //获取当前登录主题
            Subject subject = SecurityUtils.getSubject();
    
            //生成toeken
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    
            //进行认证
            try {
                subject.login(token);
            }catch (UnknownAccountException | IncorrectCredentialsException e) {
                System.out.println("用户名或密码不正确");
            }
    
    
            //打印认证状态
            if (subject.isAuthenticated()){
                System.out.println(token.getPrincipal()+" 认证通过!");
            }else {
                System.out.println(token.getPrincipal()+" 认证未通过!");
            }
    
    
    
    
    
        }
    }
    

    测试

    生成加密后的密码
    /**
     * @author 赖柄沣 bingfengdev@aliyun.com
     * @version 1.0
     * @date 2020/10/4 21:37
     */
    public class Md5Test {
    
        @Test
        public void testMd5(){
            //三个参数分别对应密码明文、盐值、散列次数
            String salt = UUID.randomUUID().toString();
            Md5Hash md5Hash = new Md5Hash("123", salt,1024);
            System.out.println("密文:"+md5Hash.toHex());
            System.out.println("盐值:"+salt);
        }
    }
    

    输出

    密文:ff595c47b51b4cf70fddce090f68879e
    盐值:ee575f62-0dda-44f2-b75e-4efef795018f
    
    进行认证测试
    /**
     * @author 赖柄沣 bingfengdev@aliyun.com
     * @version 1.0
     * @date 2020/10/4 11:20
     */
    public class AuthcTest {
        private CurrentSystemAuthenticator authenticator;
        @Before
        public void init() {
            this.authenticator = new CurrentSystemAuthenticator();
        }
    
        @Test
        public void testAuthc(){
            this.authenticator.authenticate("xiangbei","123");
        }
    
    
    }
    

    输出

    xiangbei 认证通过!
    

    写在最后

    在这篇文章当中,我们主要是简单了解了shiro中的加密策略以及如何使用MD5+salt对密码进行加密。大家可以尝试着将MD5换成SHA-256加密算法再测一下。

    在下一篇文章当中,作者将介绍SpringBoot整合Shiro的相关内容,文章可能有点长,会考虑分两次写。请大家多多关注。

    欢迎大家点赞、转发、分享。转载注明出处时要带有原文链接。

  • 相关阅读:
    Easyui-datagrid显示时间的格式化代码
    JSP页面与JSP页面之间传输参数出现中文乱码的解决方案
    SpringMVC中在web.xml中添加中文过滤器的写法
    SpringMVC的实现过程
    BeanFactory 和 ApplicationContext的区别
    Spring中的IoC(控制反转)具体是什么东西
    Spring/AOP框架, 以及使用注解
    面向切面编程
    Spring的属性注入, byName和byType还有注入List属性
    反射, getClass(), 和something.class以及类型类(转)
  • 原文地址:https://www.cnblogs.com/bingfengdev/p/13779955.html
Copyright © 2011-2022 走看看