Springboot + Shiro——MD5 盐值加密(配置)

其实这里所说的盐,简单的说,就是一组安全随机数。它会在特定的时候,加入到密码中(一般来说是加密后的密码)。从而使密码变得更有味道(从单一简单化到复杂化),更安全。

如何做到?

1). 在 doGetAuthenticationInfo 方法返回值创建 SimpleAuthenticationInfo 对象的时候, 需要使用
SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName) 构造器
2). 使用 ByteSource.Util.bytes() 来计算盐值.
3). 盐值需要唯一: 一般使用随机字符串或 user id
4). 使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 来计算盐值加密后的密码的值.

注:下面的步骤我重点讲如何配置实现MD5 盐值加密
步骤:

  1. 当我们从前台页面进行登录时,点击提交登录后,在后台通过调用Subject的login方法,此时shiro会调用SecurityManage安全管理器,尝试对比密码并登录,而我们要做的就是自定义Realm,因为SecurityManage的认证需要从Realm中获取用户输入的信息。因此需要自定义一个Realm
public class UserRealm extends AuthorizingRealm {
    @Autowired
    private UserService service;
    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection collection) {
        return null;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //通过token获取从前端输入的用户名
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String username = userToken.getUsername();

        //调用service方法判断该用户名是否存在
        User user = service.queryUser(username);

        if (user==null){
            //抛出用户名不存在的错误
            throw new UnknownAccountException();
        }
        //根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
        //通常需要以下四个参数
        //1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
        Object principal = user;
        //2). credentials: 密码.即从数据库中获取的密码
        Object credentials = user.getPassword();
        //3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
        String realmName = getName();
        //4). credentialsSalt: 盐值,这里我使用的是用户名
        ByteSource credentialsSalt = ByteSource.Util.bytes(user.getName());
        return new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
    }
}

说明:上面这个类中的第二个方法返回的SimpleAuthenticationInfo, 可以理解为里面传入的参数就是用于Shiro对用户输入的密码进行认证的相关信息。Shiro会将用户输入的密码先进行MD5 盐值加密再进行比较。
注:这里要注意,你存在数据库中的密码是要加密的,如果没有加密的话,Shiro会认证失败。会报如下错误

[org.apache.shiro.authc.AbstractAuthenticator] - Authentication failed
for token submission [org.apache.shiro.authc.UsernamePasswordToken -
admin01, rememberMe=false]. Possible unexpected error? (Typical or
expected login exceptions should extend from AuthenticationException)

  1. 接下来我们就需要配置Shiro,并在里面配置上使用MD5盐值加密
@Configuration
public class ShiroConfig {
    /**
     * ShiroFilterBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultSecurityManager);
        return bean;
    }

    @Bean("securityManager")
    public DefaultWebSecurityManager getDefaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 创建 realm 对象 , 需要自定义
     * @return
     */
    @Bean("userRealm")
    public UserRealm userRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher){
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(matcher);
        return userRealm;
    }

    /**
     * 替换当前 Realm 的 credentialsMatcher 属性.
     * 直接使用 HashedCredentialsMatcher 对象, 并设置加密算法即可.
     * 密码校验规则HashedCredentialsMatcher
     * 这个类是为了对密码进行编码的
     * 防止密码在数据库中明码表示,当然在登录认证的时候,
     * 这个类也负责对form里输入的密码进行编码
     * 处理认证匹配处理器
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //指定加密方式为MD5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //加密次数
        credentialsMatcher.setHashIterations(1024);
        return credentialsMatcher;
    }
}
  1. 在Controller层只需获取当前的 Subject,即调用 SecurityUtils.getSubject()。然后把用户名和密码封装成封装为 UsernamePasswordToken 对象,最后调用Subject 的 login(AuthenticationToken) 方法.进行认证操作即可
@RequestMapping("/login")
    public String login(String username,String password,Model model){
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        try {
            //执行登录方法,如果没有异常就Ok
             subject.login(token);
            return "index";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg","用户名错误");
            return "login";
        } catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }

至此关于使用MD5 盐值加密的配置就大功告成了。

总结一下:
Q:如何将获取的密码进行MD5 盐值加密:
A:替换当前 Realm 的 credentialsMatcher 属性. 直接使用 HashedCredentialsMatcher 对象, 并设置加密算法即可.

Q:如何比较用户输入的密码是否正确
A:通过 AuthenticatingRealm 的 credentialsMatcher 属性来进行的密码的比对!

  1. 获取当前的 Subject. 调用 SecurityUtils.getSubject();
  2. 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
  3. 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象
    1). 创建一个表单页面
    2). 把请求提交到 SpringMVC 的 Handler
    3). 获取用户名和密码.
  4. 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
  5. 自定义 Realm 的方法, 从数据库中获取对应的记录, 返回给 Shiro.
    1). 实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类
    2). 实现 doGetAuthenticationInfo(AuthenticationToken) 方法.
  6. 由 shiro 完成对密码的比对.
发布了85 篇原创文章 · 获赞 9 · 访问量 6700

猜你喜欢

转载自blog.csdn.net/weixin_43517302/article/details/105418593
今日推荐