shiro讲解之 Cryptography
shiro讲解之 Cryptography
本章节将详细学习一下Shiro 的Cryptography 以及其在Application中的用处。
概念
官方文档
- Cryptography is a natural addition to an enterprise security framework. Shiro’s crypto package contains easy-to-use and understand representations of crytographic Ciphers, Hashes (aka digests) and different codec implementations. All of the classes in this package are carefully designed to be very easy to use and easy to understand. Anyone who has used Java’s native cryptography support knows it can be a challenging animal to tame. Shiro’s crypto APIs simplify the complicated Java mechanisms and make cryptography easy to use for normal mortal human beings.
通常理解
- 通常而言对Shiro 的 Cryptography 的理解应在于Shiro 提供了一个用于加密解密工具包,相较于JAVA本生机制里面的加密解密而言,Shiro 的 Cryptography更为简单高效同时也很安全。Shiro提供了base64和16进制字符串编码/解码的API支持,方便一些编码解码操作
Shiro Cryptography(Shiro Cryptography pakacge提供的机密算法)
对称加密
- AES
- BlowFish
可逆加密
- Hex
- base64
不可逆加密
- SHA-1
- MD5
实例
前言
我们都知道作为一个企业级的开发,对于安全的要求可以说是极致的,因而在一些重要或者敏感的数据都会通过加密保存。下面我们将梳理一下 Shiro Cryptography 在 认证过程中的具体使用(当然它还用于其他地方,这里会是一个典型的体现)。
图形案例
该图形的实际案例为:
- ①
在平台内我们通过一些加密算法保存了 A 用户的Principals 信息(一般只会对密码加密),该初始化加密算逻辑里面包含了MD5加密算法(不可逆)、加密次数、盐值(一般为用户名),产生的安全数据我们表示为 Security Data A - ②
通过Shiro Cryptography 模拟数据即当 A 用户登录平台式我们将把 A 用户提交的用户名和密码通过特定的加密算法(此处的特定算法指算法逻辑必须要和初始化加密算法逻辑一样即加密算法、加密次数、盐值必须一会)。产生的安全数据我们表示为 Security Data B - ③
将 Security Data A 与 Security Data B 对比。对比的过程有Shiro SimpleAuthenticationInfo 封装方法完成,我们只需调用即可,下面有代码提现。 - ④-⑤
最后将比较的结果分流至不同的逻辑块中
- ①
解析
1、初始化加密保存
在对数据初始化加密时我们要事先定好加密逻辑即使用什么加密算法合适,是否需要添加额外的盐值,加密次数为多少次合适等等。因为我们知道加密的算法有很多种比如 base64、SHA-1、MD5 等等。初始化产生的安全数据是否具有可逆性将直接影响我们 shiro 的 加密算法的实现。比如使用的是 SHA-1 产生了不可逆的数据,此时我们只能重新模拟数据然后作对比,如果base64等,我们直接使用 安全数据做逆向处理而不是在去模拟。总而言之,初始化加密的算法实现将直接决定 接下来 shiro 通过 Cryptography 产生对比数据的算法逻辑。
代码展示
// 加密算法类型
String hashAlgorithmName = “SHA-1”;
// 需要加密的密码
Object credentials = “123456”;
// 盐值
Object salt = ByteSource.Util.bytes(“Dustyone”);
// 加密次数
int hashIterations = 1024;
// 最后的加密结果
Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
以上代码表示在对用户的Principals 做初始化加密时使用了 SHA-1,盐值为 Dustyone 即为用户名,加密次数为1024次,最后得到一个安全数据: 580655386dc0bef1105a44f9dcbe4a1d3a7b0781
2、Shiro 加密算法实现
要使用 Shiro 的加密算法,首先我们通过配置Realm属性来完成我们对 Cryptography 的设计(此处我们已单Realm为例)。在ApplicationContext.xml的 Shiro 配置中 配置Realm的属性
以上代码表明我们自定义的Realm 可以通过 credentialsMatcher 来设定我们的加密算法逻辑。加密算法使用的是 SHA-1(不声明是表示不适用加密算法)。加密次数为 1024次,此处没有配置 Salt 使用为不同的用户其用户名可能会不同因而 盐值不能配置在xml文件中。
Realm 核心代码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("[FirstRealm] doGetAuthenticationInfo");
// 1. 把 AuthenticationToken 转换为 UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// 2. 从 UsernamePasswordToken 中来获取 username
String username = upToken.getUsername();
// 3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
SubjectEntity principals = new SubjectEntity("580655386dc0bef1105a44f9dcbe4a1d3a7b0781", "Dustyone");
// 4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
if ("unknown".equals(username)) {
throw new UnknownAccountException("用户不存在!");
}
// 5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.
if ("monster".equals(username)) {
throw new LockedAccountException("用户被锁定");
}
// 6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为:
// SimpleAuthenticationInfo
// 以下信息是从数据库中获取的.
// 1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
Object principal = principals.getUsername();
// 2). credentials: 密码.
Object credentials = principals.getPassword();
// 3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
String realmName = getName();
// 4). 盐值.
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
//Shiro 对比数据的核心方法
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt,realmName);
return info;
}
由以上代码可以看出对于 SubjectEntity 的Principals而言 它应该是从数据库中拿出来的数据,此处将通过一个简单的 entity 来代替,并且将 Principals 的 credentials 设置成了我们之前加密好了的数据。
那么问题来了,从以上代码中我们即没有看法Shiro的加密算法是怎样实现的,数据的对比又将是怎样完成的。这就涉及到Shiro 对 SimpleAuthenticationInfo 这个方法的封装问题了。因而要想知道Shiro 的机密算法是怎样实现的以及数据的对比又是怎样完成的,我们将从 SimpleAuthenticationInfo 入手。
SimpleAuthenticationInfo
SimpleAuthenticationInfo 是 Shiro 高度封装好的用产生加密数据和数据对比的方法,同时 SimpleAuthenticationInfo 也构造了实现不同程度的数据产生和对比的方法,下面我们将具体学习一下。- SimpleAuthenticationInfo();
不传入任何参数即不产生任何数据也不做任何对比 - SimpleAuthenticationInfo(PrincipalsCollection principals, Object credentials)
传入 一个 principal 集合 和一个唯一的密码。不做任何加密处理直接对比。这里我们可以模拟一个原型出来。比如某网站在用户注册是允许手机号/邮箱/QQ 号码作为 principal(此处特指用户名),而用户的 credentials 唯一即密码唯一。那么在不涉及加密的情况下我们可以直接将用户的 机号/邮箱/QQ 放入 Collection 中 匹配时只要有一个 Principal 满足配对要求即可视为匹配成功。 - SimpleAuthenticationInfo(Object principal, Object credentials,String realmName)
声明用指定的Realm 来做单个 principal 不加密的对比。 - SimpleAuthenticationInfo(PrincipalsCollections principals, Object hashedCredentials, ByteSource, credentialsSalt)
传入模拟好的安全数据(Shiro将其视为初始安全数据),加盐做加密处理并进行数据对比。 - SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName)
传入模拟好的安全数据(Shiro将其视为初始安全数据),加盐,并使用指定的Realm做加密处理并进行数据对比,我们一般使用该方法做数据加密和对比处理。
- SimpleAuthenticationInfo();
SimpleAuthenticationInfo 的处理过程
我们以 SimpleAuthenticationInfo(Object principals, Object hashedCredentials, ByteSource credentialsSalt, String realmName) 方法为例来说名一下 Shiro 的数据加密和数据对比是怎样实现的。
入参说明:
principals: 此处特指用户名
hashedCredentials:加密之后的密码,我们也称之为 Golden Data。
credentialsSalt:盐值,一般为用户名
realmName:处理的realm名- 保存hashedCredentials
在调用上述方法是Shiro 第一步会将加密之后的数据保存起来。 - 使用指定的加密算法(定义在配置文件中)、加密次数(定义在配置文件中)、盐值(ByteSource)加密并得到加密之后的数据
- 使用指定的Realm(realmName)将上面两个数据多对比。
- 返回对比结果给指定的Realm(realmName)。
- 保存hashedCredentials
- 3、数据对比
- 4、结果分流
根据返回的结果执行下一步代码 - 5、下一步
小结
- Shiro Cryptography 在 Realm Bean中的配置方法。
- Shiro 模拟加密的算法、加密次数、盐值必须和初始化加密的一致。
- Shiro 的模拟加密和数据对比同时使用 SimpleAuthenticationInfo 构造器来构造实现不同程度的实现方法。(若想具体了解以上两步骤是如何实现的请参阅相关源代码~)。
还没有评论,来说两句吧...