shiro认证

Shiro

代码地址gitee

1 认证

1.1 使用ini配置文件来实现认证

shiro实现简单的认证,用户名的账号和密码放在了根目录下的shiro.ini文件中

[users]
zhangsan=123123
lisi=123123

在设置securityManager的Realm的时候,选择new一个IniRealm,传入shiro.ini的路径

// 创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

// SecurityUtils 给全局安全管理器工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);

// 关键对象subject主体
Subject subject = SecurityUtils.getSubject();

// 创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123123");

// 用户认证
try {
  subject.login(token);
} catch (UnknownAccountException e) {
  e.printStackTrace();
  System.out.println("账号不存在");
} catch (IncorrectCredentialsException e) {
  e.printStackTrace();
  System.out.println("密码错误");
}

1.2 使用自己定义的Realm来实现认证

我们自定义的Realm需要继承AuthorizingRealm,重写doGetAuthorizationInfo()授权方法和doGetAuthenticationInfo()认证方法

通过追溯源码可以得到如下依赖图:

  • AuthenticatingRealm 认证的Realm抽象类
  • AuthorizingRealm 授权的Realm抽象类
  • 用户名比较是在SimpleAccountRealm中的doGetAuthenticationInfo 完成用户名校验
  • 密码是在AuthenticatingRealm中的assertCredentialsMatch完成校验
  • 通过图可以看出,继承AuthorizingRealm就可以重写认证AuthenticatingRealm和授权AuthorizingRealm两个抽象类的方法

shiro认证

自定义Realm类

public class CustomerRealm extends AuthorizingRealm {

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
  
    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // token中获取用户名
        String principal = (String) token.getPrincipal();
        System.out.println("principal = " + principal);

        // 根据用户名获取数据,例如 queryUserByUsername(principal);
      	// 这里模拟数据库中获取数据
        if ("zhangsan".equals(principal)) {
            /**
             * 参数1: 返回数据库中正确的用户名
             * 参数2: 密码
             * 参数3: 提供当前的realm的名字,直接调用父类的getName()方法
             */
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("zhangsan", "123123", this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

下述测试自定义Realm类的时候,只是将new IniRealm()改成new自己定义的Realm。后期和数据中的账号密码作比较是在自己定义的Realm中进行的。

// 创建securityManager
DefaultSecurityManager securityManager = new DefaultSecurityManager();

// 设置自定义Realm
securityManager.setRealm(new CustomerRealm());

// 将安全工具类设置为安全工具类
SecurityUtils.setSecurityManager(securityManager);

// 通过安全工具类获取Object
Subject subject = SecurityUtils.getSubject();

// 创建token
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123123");

// 登录
try {
  subject.login(token);
} catch (AuthenticationException e) {
  e.printStackTrace();
}

1.3 自定义Realm的md5加密加盐比较

md5加密 是不可逆的,同一数据在进行md5加密后得到的加密字符串相同。

自定义MD5Realm类

public class CustomerMD5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 获取身份信息
        String principal = (String) token.getPrincipal();

        // 根据用户名查询数据库
        if ("zhangsan".equals(principal)) {

            /**
             * 参数一: 数据库中的密码
             * 参数二:md5+salt之后的密码
             * 参数三:盐值
             * 参数四:当前realm的名称
             */
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                    "zhangsan",
                    "f997c74487a098c9477da3a0c10944a5",
                    ByteSource.Util.bytes("s0*5ps"),
                    getName());
            return info;
        }

        return null;
    }
}

md5加密测试类

相比之前不同的是,我们在new Realm之后,对Realm进行设置加密算法和迭代次数。迭代次数不同得到的MD5加密字符串也不同。

扩展:

​ 这里的盐值我们后期可以通过字符串生成工具来随机生成字符串,并将其存入数据库中,在进行登录的时候,用相同的加密原则来进行判断。例如,我们注册的时候是将盐值加载密码后面再进行加密,我们登录的时候就要将数据库中的盐值加在密码后面。

public class TestCustomerMD5RealmAuthenticator {
    public static void main(String[] args) {
        // 创建安全管理器
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        // 注入realm
        CustomerMD5Realm realm = new CustomerMD5Realm();
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 注册算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列次数
//        hashedCredentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(hashedCredentialsMatcher);

        // 为securityManager设置Realm
        securityManager.setRealm(realm);

        // 设置securityManager
        SecurityUtils.setSecurityManager(securityManager);

        // 获取subject对象
        Subject subject = SecurityUtils.getSubject();

        // 创建token
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");

        try {
            subject.login(token);
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误");
        }

    }
}
上一篇:源码中的设计模式-模板方法模式


下一篇:学习Python过程中 菜鸟进阶必备的九大技能!