zoukankan      html  css  js  c++  java
  • shiro中JdbcRealm使用salt的问题

    JdbcRealm中创建用户的一般写法:

    public String register(User user) {
            RandomNumberGenerator gen = new SecureRandomNumberGenerator();
            ByteSource salt = gen.nextBytes();
    
            String hashedPasswordBase64 = new Sha256Hash(user.getPassword(), salt, 1024).toBase64();
    
            user.setPassword(hashedPasswordBase64);
            user.setSalt(salt.getBytes());
    
            try {
                userDAO.create(user);
            } catch (SQLException e) {
                e.printStackTrace();
                //TOOD:log error
            }
            return "redirect:login";
        }

    这个里面一个比较麻烦的问题在于salt该如何存放。nextBytes()方法返回的实际值是SimpleByteSource,有些地方直接使用salt.toString()存作字符串,SimpleByteSource重写了toString方法,实际上返回的是base64编码。问题在于Jdbc并不能使用这种方式存放的数据。

    看下JdbcRealm的doGetAuthenticationInfo方法:

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            UsernamePasswordToken upToken = (UsernamePasswordToken) token;
            String username = upToken.getUsername();
    
            // Null username is invalid
            if (username == null) {
                throw new AccountException("Null usernames are not allowed by this realm.");
            }
    
            Connection conn = null;
            SimpleAuthenticationInfo info = null;
            try {
                conn = dataSource.getConnection();
    
                String password = null;
                String salt = null;
                switch (saltStyle) {
                case NO_SALT:
                    password = getPasswordForUser(conn, username)[0];
                    break;
                case CRYPT:
                    // TODO: separate password and hash from getPasswordForUser[0]
                    throw new ConfigurationException("Not implemented yet");
                    //break;
                case COLUMN:
                    String[] queryResults = getPasswordForUser(conn, username);
                    password = queryResults[0];
                    salt = queryResults[1];
                    break;
                case EXTERNAL:
                    password = getPasswordForUser(conn, username)[0];
                    salt = getSaltForUser(username);
                }
    
                if (password == null) {
                    throw new UnknownAccountException("No account found for user [" + username + "]");
                }
    
                info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
                
                if (salt != null) {
                    info.setCredentialsSalt(ByteSource.Util.bytes(salt));
                }
    
            } catch (SQLException e) {
                final String message = "There was a SQL error while authenticating user [" + username + "]";
                if (log.isErrorEnabled()) {
                    log.error(message, e);
                }
    
                // Rethrow any SQL errors as an authentication exception
                throw new AuthenticationException(message, e);
            } finally {
                JdbcUtils.closeConnection(conn);
            }
    
            return info;
        }

    可以看出JdbcRealm是吧密码和盐都当作字符串读取,而ByteSource本质上存放的是byte数组。深究下去,实际上是使用UTF-8解码字符串得到了byte[]。但问题在于,String并不是什么byte都可以存放的,不论是JVM内部使用的UTF-16,还是这里编解码使用的UTF-8,都是有一定格式要求的。不是什么byte[]都可以放进去然后原封不动的拿出来,更何况中间还经过了一次MySQL。

    更重要的是,根本没有必要使用字符串存放,ByteSource.Util.bytes本身就有byte[]的重载,而base64也谈不上什么加密。直接存放二进制是不存在问题的。实现起来也很简单。重写doGetAuthenticationInfo,使用byte[]创建SimpleByteSource即可。

  • 相关阅读:
    LVDS 数据通道详解 单8 单6
    MFC中CTime获取日期时间的方法
    Sqlite 修改字段的名称。
    SQLite 字段数据类型
    把View转化成Image
    iOS 键盘类型UIKeyboardType
    验证银行卡号格式是否正确
    获取手机wifi下的网络地址
    图形处理专题(文章收集)
    收藏的博客
  • 原文地址:https://www.cnblogs.com/narcissu5/p/3954526.html
Copyright © 2011-2022 走看看