Shiro 安全框架(二)
Shiro 权限框架(二)
上一章节学了 Shiro 框架的简单结构和两个重要的方法的,接下来要学习如何使用框架的认证和授权。
自定义Realm
一般我们的用户名和密码数据都是来自数据库中的,所以自定义 Realm 读取数据库中的数据是非常有必要的。
按照我们流程分析,我们自定义的 Realm 要继承 AuthorizingRealm
这个类,并重写两个方法:
doGetAuthticationInfo()
认证doGetAuthorizationInfo()
授权
认证——doGetAuthticationInfo()
代码实现:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名
String principal = (String) authenticationToken.getPrincipal();
System.out.println(principal);
// 开始根据用户名查询数据库中用户名所对应的密码。
if ("xiaocheng".equals(principal)){
// 此处数据用于连接数据库得到的数据
// SimpleAuthenticationInfo 继承 AuthenticationInfo
// AuthenticationInfo 保存了正确用户的基本信息
// 参数1:用户名 参数2:密码 参数3:当前的 realm 名(this.getName())
return new SimpleAuthenticationInfo(principal, "123", this.getName());
}
return null;
}
MD5 + salt 加密
我们有时候不能明文的在数据库中保存用户的密码,我们需要对其进行加密处理。
同样也是要自定义 Realm 的。
MD5 加密
Realm 的 doGetAuthenticationInfo()
:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名
String principal = (String) authenticationToken.getPrincipal();
// 查询数据库比较
if (principal.equals("xiaocheng")){
// md5
return new SimpleAuthenticationInfo(principal,"202cb962ac59075b964b07152d234b70",this.getName());
}
return null;
}
test:
// 自定义 realm 使用 CustomMd5Realm md5realm
CustomMd5Realm realm = new CustomMd5Realm();
// 创建 hash 凭证认证器,密码认证是使用 --> HashedCredentialsMatcher 是 SimpleCredentialsMatcher 的子类,如果使用 hash,就是简单的 equals 比较。
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用 md5 算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 设置 realm 使用 hash 凭证匹配器
realm.setCredentialsMatcher(hashedCredentialsMatcher);
自此就在自定义的 realm 中添加了 md5 加密认证器,在匹配 subject 时,就会按照匹配器来匹配用户。
md5 + salt
Realm 的 doGetAuthenticationInfo()
:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名
String principal = (String) authenticationToken.getPrincipal();
// 查询数据库比较
if (principal.equals("xiaocheng")){
// md5 + salt
// 参数三 : salt,需要借助 ByteSource.Util.bytes() 完成加盐操作
return new SimpleAuthenticationInfo(principal,"cc1e39955047cba6c751ad1a6214af26",ByteSource.Util.bytes("salt"),this.getName());
}
return null;
}
test:
// 自定义 realm 使用 CustomMd5Realm md5realm
CustomMd5Realm realm = new CustomMd5Realm();
// 创建 hash 凭证认证器,密码认证是使用 --> HashedCredentialsMatcher 是 SimpleCredentialsMatcher 的子类,如果使用 hash,就是简单的 equals 比较。
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用 md5 算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 设置 hash 散列次数
hashedCredentialsMatcher.setHashIterations(1024);
// 设置 realm 使用 hash 凭证匹配器
realm.setCredentialsMatcher(hashedCredentialsMatcher);
加盐处理就在 doGetAuthenticationInfo()
中的返回值中添加参数,但是加盐处理使用的是一个工具类 ByteSource.Utile.bytes("salt")
。
md5 + salt + hash散列
Realm 的 doGetAuthenticationInfo()
:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名
String principal = (String) authenticationToken.getPrincipal();
// 查询数据库比较
if (principal.equals("xiaocheng")){
// md5 + salt + 散列
return new SimpleAuthenticationInfo(principal,"d10ce44fb96e787ef5a57c1aed5da166",ByteSource.Util.bytes("salt"),this.getName());
}
return null;
}
test:
// 自定义 realm 使用 CustomMd5Realm md5realm
CustomMd5Realm realm = new CustomMd5Realm();
// 创建 hash 凭证认证器,密码认证是使用 --> HashedCredentialsMatcher 是 SimpleCredentialsMatcher 的子类,如果使用 hash,就是简单的 equals 比较。
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用 md5 算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 设置 hash 散列次数
hashedCredentialsMatcher.setHashIterations(1024);
// 设置 realm 使用 hash 凭证匹配器
realm.setCredentialsMatcher(hashedCredentialsMatcher);
在设置认证凭证的时候设置 hash 散列次数.
小结
都是使用 hash 凭证认证器 ==> HashedCredentialsMatcher
- md5 加密:
hashedCredentialsMatcher.setHashAlgorithmName("md5")
发生在认证器上。 - md5 + salt 加密:
return SimpleAuthenticationInfo(principal,"password",ByteSource.Util.bytes("salt"),this.getName())
发生在自定义 Realm 上。 - md5 + salt + hash 加密:
hashedCredentialsMatcher.setHashIterations(1023)
发生在认证器上。
合理的使用加密可以让我们的认证更加安全。这一节需要了解到加密的操作都具体在那些地方,并不是都发生在认证器中,加盐操作就在 Realm 中完成。认证通过后就要进入下一个步骤——授权
授权——doGetAuthorizationInfo()
当用户通过认证为合法用户时,此时进入到授权过程。
其实授权就是:规定谁对什么资源进行怎么样的操作。
每一个合法用户都有自己在当前项目中的权限,权限是和资源绑定在一起的。
授权方式
主流授权方式分为两种RBAC:
-
基于角色进行访问控制
对每一个角色进行权限划分,也就是说会给用户分配角色,每一个角色都只能访问它的权限范围。 -
基于资源进行访问控制
以资源为核心进行访问控制,规定资源能被谁操作,如何操作。
权限字符串
权限字符串的规则是:资源标识符:操作:资源实例标识符
例子:
- 用户创建权限:
user:create
或user:create:*
- 用户修改实例test的权限:
user:update:test
- 用户操作test的所有权限:
user:*:test
可以使用通配符 *
授权实现
在自定义 Realm 中,我们上面实现了 doGetAuthenticationInfo()
方法来认证,还有一个 doGetAuthoriztionInfo()
就是用来授权的。
所以完成授权我们就要在这个方法中完成。
当用户请求授权时,需要查询数据库中用户对应的具体权限完成授权。
Realm 配置:
// 授权配置
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 打印用户的主角色
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
System.out.println("用户名:"+primaryPrincipal);
// 创建授权信息 —— AuthorizationInfo,可以使用 SimpleAuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 将数据库中查询的角色信息赋值给权限对象
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
// 将数据库中查询的权限信息赋值给权限对象
simpleAuthorizationInfo.addStringPermission("admin:*");
simpleAuthorizationInfo.addStringPermission("user:create");
return simpleAuthorizationInfo;
}
请求授权:
// 认证通过
if (subject.isAuthenticated()){
// 1. 基于角色权限控制
System.out.println(subject.hasRole("admin")); // 查询主体是否有 admin 这个角色
// 2. 多角色权限控制
boolean b = subject.hasAllRoles(Arrays.asList("admin","user")); // 查询主体是否有 admin、user 这两个角色,如果没有返回false
System.out.println(b);
// 3. 查看主体分别有那些角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));
for (boolean aBoolean : booleans) {
System.out.println(aBoolean);
}
System.out.println("================基于权限字符串的访问控制================");
// 4. 基于权限字符串的访问控制 资源标识符:操作:资源类型
// 查看主体是否具有 对 admin 下的 001 资源具有 create 操作的权限
System.out.println(subject.isPermitted("admin:create:001"));
// 查询主体分别对 "admin:create:*"、"user:*:001" 是否具有权限
boolean[] permitted = subject.isPermitted("admin:create:*", "user:*:001","user:create:002");
for (boolean b1 : permitted) {
System.out.println(b1);
}
// 查看主体是否同时具有权限
System.out.println(subject.isPermittedAll("admin:create:*", "user:update:001","user:create:002"));
}
小结
本章学习了自定义 Realm 继承 AuthorizingRealm
中的认证和授权方法的使用。还有认证过程中的数据加密。下面就要学习 Spring Boot 结合数据库整合 Shiro 去使用。