Spring Boot 整合 shiro 之盐值加密认证详解(六)

概述

上一篇博客我们主要讲了自定义 Realm,里面的用户认证所使用的密码都是明文,这种方式是不可取的,往往我们在实战开发中用户的密码都是以密文形势进行存储,并且要求加密算法是不可逆的,著名的加密算法有MD5、SHA1等。所以这篇博客我们主要介绍 Shiro 安全框架学习它的加密方案。这里我们采用md5的方式加密,然后呢。md5加密又分为加盐,和不加盐

不加盐认证

    @Test
    public void testMatcher() {
        CustomRealm customRealm = new CustomRealm();
        //1,构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //设置Realm
        defaultSecurityManager.setRealm(customRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取主体
        Subject subject = SecurityUtils.getSubject();
        //用户名和密码的token
        UsernamePasswordToken token = new UsernamePasswordToken("dev", "123456");
        try {
            //2,主体提交认证请求
            subject.login(token);
            System.out.println("是否权限认证:isAuthenticated=" + subject.isAuthenticated());
            //检查是否有角色
            subject.checkRoles("admin");
            System.out.println("有admin角色");
            //检查是否有权限
            subject.checkPermissions("user:delete", "user:edit", "user:list", "user:add");
            System.out.println("有 user:add、user:list、user:edit、user:delete权限");

        } catch (IncorrectCredentialsException exception) {
            System.out.println("用户名或密码错误");
        } catch (LockedAccountException exception) {
            System.out.println("账号已被锁定");
        } catch (DisabledAccountException exception) {
            System.out.println("账号已被禁用");
        } catch (UnknownAccountException exception) {
            System.out.println("用户不存在");
        } catch (UnauthorizedException ae) {
            System.out.println("用户没有权限");
        }
    }

CustomRealm 请参考Spring Boot 使用自定义 Realm 进行认证授权(五)

加入密码认证核心代码

        //进行加密
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //采用md5加密
        matcher.setHashAlgorithmName("md5");
        //设置加密次数
        matcher.setHashIterations(2);
        customRealm.setCredentialsMatcher(matcher);

修改 CustomRealm 新增获取密文的方法

     /**
     * 获得密文密码
     * @Author:     djy
     * @UpdateUser:
     * @Version:     0.0.1
     * @param currentPassword
     * @return       java.lang.String
     * @throws
     */
    private String getPasswordMatcher(String currentPassword){
        return new Md5Hash(currentPassword, null,2).toString();
   }

修改 doGetAuthenticationInfo

    /**
     * 用户认证
     *
     * @param authenticationToken
     * @return org.apache.shiro.authc.AuthenticationInfo
     * @throws
     * @Author: djy
     * @UpdateUser:
     * @Version: 0.0.1
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("开始执行*******doGetAuthenticationInfo方法啦****");
        //获取登录用户名
        String userName = (String) authenticationToken.getPrincipal();
        //通过用户名到数据库获取用户信息
        String password = getPasswordByUsername(userName);
        if (null == password) {
            return null;
        }
        System.out.println("密码为:" + password);
        String matcherPwd = getPasswordMatcher(password);
        System.out.println(matcherPwd);
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, getName());
        return authenticationInfo;
    }

当用户 admin 登录进来的时候 程序输出

UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
  • 程序输出
是否权限认证:isAuthenticated=true
开始执行*******doGetAuthorizationInfo方法啦****
admin
有admin角色
开始执行*******doGetAuthorizationInfo方法啦****
admin
开始执行*******doGetAuthorizationInfo方法啦****
admin
开始执行*******doGetAuthorizationInfo方法啦****
admin
开始执行*******doGetAuthorizationInfo方法啦****
admin
有 user:add、user:list、user:edit、user:delete权限
  • 假如改成还是用明文检验
//       String matcherPwd=getPasswordMatcher(password);
//       System.out.println(matcherPwd);
//       SimpleAuthenticationInfo authenticationInfo = new 
//SimpleAuthenticationInfo(username,matcherPwd,getName());
        SimpleAuthenticationInfo authenticationInfo = new
SimpleAuthenticationInfo(username,password,getName());
  • 程序输出
开始执行*******doGetAuthenticationInfo方法啦****
密码为:123456
4280d89a5a03f812751f504cc10ee8a5
用户名或密码错误

加盐认证

当两个用户的密码相同时,单纯使用不加盐的MD5加密方式,会发现数据库中存在相同结构的密码,这样也是不安全的。我们希望即便是两个人的原始密码一样,加密后的结果也不一样。如何做到呢?其实就好像炒菜一样,两道一样的鱼香肉丝,加的盐不一样,炒出来的味道就不一样。MD5加密也是一样,需要进行盐值加密。

创建 testSaltMatcher 方法

    @Test
    public void testSaltMatcher() {
        CustomRealm customRealm = new CustomRealm();
        //进行加密
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //采用mdf加密
        matcher.setHashAlgorithmName("md5");
        //设置加密次数
        matcher.setHashIterations(1);
        customRealm.setCredentialsMatcher(matcher);
        //1,构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //设置Realm
        defaultSecurityManager.setRealm(customRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取主体
        Subject subject = SecurityUtils.getSubject();
        //用户名和密码的token
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        try {
            //2,主体提交认证请求
            subject.login(token);
            System.out.println("是否权限认证:isAuthenticated=" + subject.isAuthenticated());
            //检查是否有角色
            subject.checkRoles("admin");
            System.out.println("有admin角色");
            //检查是否有权限
            subject.checkPermissions("user:delete", "user:edit", "user:list", "user:add");
            System.out.println("有 user:add、user:list、user:edit、user:delete权限");
            //if no exception, that's it, we're done!
        } catch (IncorrectCredentialsException exception) {
            System.out.println("用户名或密码错误");
        } catch (LockedAccountException exception) {
            System.out.println("账号已被锁定");
        } catch (DisabledAccountException exception) {
            System.out.println("账号已被禁用");
        } catch (UnknownAccountException exception) {
            System.out.println("用户不存在");
        } catch (UnauthorizedException ae) {
            System.out.println("用户没有权限");
        }
    }

加盐验证核心代码

//设置盐的值
 authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(username));

修改 CustomRealm 新增获取加盐密文的方法

    /**
     * 获得密文密码
     *
     * @param currentPassword
     * @return java.lang.String
     * @throws
     * @Author: djy
     * @UpdateUser:
     * @Version: 0.0.1
     */
    private String getPasswordMatcher(String currentPassword, String username) {
        return new Md5Hash(currentPassword, username).toString();
    }

修改 doGetAuthenticationInfo

    /**
     * 用户认证
     *
     * @param authenticationToken
     * @return org.apache.shiro.authc.AuthenticationInfo
     * @throws
     * @Author: djy
     * @UpdateUser:
     * @Version: 0.0.1
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("开始执行*******doGetAuthenticationInfo方法啦****");
        //获取登录用户名
        String userName = (String) authenticationToken.getPrincipal();
        //通过用户名到数据库获取用户信息
        String password = getPasswordByUsername(userName);
        if (null == password) {
            return null;
        }
//        System.out.println("密码为:" + password);
//        String matcherPwd = getPasswordMatcher(password);
        String matcherPwd = getPasswordMatcher(password,userName);
        System.out.println(matcherPwd);
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, matcherPwd, getName());
        //设置盐的值
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userName));
        return authenticationInfo;
    }

测试

  • 当用户 admin 登录进来 程序输出
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
开始执行*******doGetAuthenticationInfo方法啦****
a66abb5684c45962d887564f08346e8d
是否权限认证:isAuthenticated=true
开始执行*******doGetAuthorizationInfo方法啦****
admin
有admin角色
开始执行*******doGetAuthorizationInfo方法啦****
admin
开始执行*******doGetAuthorizationInfo方法啦****
admin
开始执行*******doGetAuthorizationInfo方法啦****
admin
开始执行*******doGetAuthorizationInfo方法啦****
admin
有 user:add、user:list、user:edit、user:delete权限
  • 当用户 admin 登录但是密码却输成1234567 程序输出
UsernamePasswordToken token = new UsernamePasswordToken("admin", "1234567");
开始执行*******doGetAuthenticationInfo方法啦****
a66abb5684c45962d887564f08346e8d
用户名或密码错误
  • 当用户 admin 登录进来密码也输入正确了但是 验证的时候改用明文 程序输出
//把下面两个方法注释掉
//String matcherPwd = getPasswordMatcher(password,userName);
//System.out.println(matcherPwd);
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,getName());
开始执行*******doGetAuthenticationInfo方法啦****
用户名或密码错误

shiro 加密就讲到这里,其实有的小伙伴会有疑问,假如我们公司用sessionID 作为登录状态的凭证那么我们该怎么办呢,怎么才能让用户通过认证呢?这里我们可以自定义密码匹配类继承 HashedCredentialsMatcher类 重写它的 doCredentialsMatch (密码匹配的方法)即可。后面我们讲解实战开发的时候会详细的讲解。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一名技术极客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值