八月加油!
shiro权限管理:
shiro架构:
securityManager:安全管理器,主体(subject)进行认证和授权。
authenticator:认证器,主体认证所需要的。
anthorizer:授权器,主体授权所需要的。
sessionManager:shiro提供了一套session的管理模式。常规的session都是web容器进行统一管理。
sessionDao:通过sessionDao来管理个性化session数据。
cacheManager:缓存管理器,session数据和授权数据进行缓存。 -- 和ehcache整合进行缓存数据管理。
realm:域,相当于数据源。通过realm存取认证/授权的相关数据。 -- realm中含有认证和授权逻辑。
cryptography:加密/解密组件管理。
用户认证流程:
1. 通过ini配置文件创建SecurityManager
2. 调用subject.login方法进行主体提交认证
3. SecurityManager最终是通过ModularRealmAuthenticator进行认证
4. ModularRealmAuthenticator调用IniRealm去ini配置文件中查询用户信息
5. IniRealm根据输入的token(UsernamePasswordToken)从ini中查询用户信息,根据账号查询用户信息
如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)
如果查询不到,就给ModularRealmAuthenticator返回null
6. ModularRealmAuthenticator接收IniRealm返回Authentication认证信息
如果返回的认证信息是null,ModularRealmAuthenticator抛出异常(org.apache.shiro.authc.UnknownAccountException)
如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码(在ini文件中存在)和token中的密码进行对比, 如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)
用户认证Demo:
1.ini文件构建:采用ini文件可以实现数据分组,存储方式上和properties一致,都是采用键值对方式。
shiro-simple.ini文件:
#用户信息分组
[users]
zhangsan=123
2.工程搭建,采用Junit测试,书写测试用例。
//用户登录和退出认证
@Test
public void testLoginAndLogout(){
//1. 通过ini文件构建SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-simple.ini");
//2. 创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//3. 将SecurityManager设置到当前的环境中
SecurityUtils.setSecurityManager(securityManager);
//4. 从SecurityManager中创建一个subject
Subject subject = SecurityUtils.getSubject();
//5. 在认证提交前准备token -- 令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
//6. 执行认证提交
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//7. 是否认证通过
System.out.println("shiro--登录是否认证通过:" + subject.isAuthenticated());
//8. 退出操作
subject.logout();
}
3.测试结果:
认证成功:
认证失败:
用户名未通过认证,返回Null,报org.apache.shiro.authc.UnknownAccountException异常
密码未通过验证,报org.apache.shiro.authc.IncorrectCredentialsException异常
用户授权流程:
1.对subject进行授权,调用方法isPermitted("permission串")。
2.SecurityManager执行授权,通过ModularRealmAuthorizer执行授权。
3.ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据。调用realm的授权方法:doGetAuthorizationInfo。
4.realm从数据库查询权限数据,返回ModularRealmAuthorizer。
5.ModularRealmAuthorizer调用PermissionResolver进行权限串比对。
6.如果比对后,isPermitted中"permission串"在realm查询到权限数据中,说明用户访问permission串有权限,否则 没有权限,抛出异常。
用户授权Demo:
1.ini文件构建:采用ini文件可以实现数据分组,存储方式上和properties一致,都是采用键值对方式。
shiro-permission.ini文件:
# 用户
[users]
# 用户zhangsan的密码是132,此用户具有role1和role2两个角色
zhangsan=123,role1,role2
# 权限
[roles]
# 角色role1对资源user具有create、update权限
role1=user:create,user:update
# 角色role2对资源user具有create、delete权限
role2=user:create,user:delete
2.工程搭建,采用Junit测试,书写测试用例。
//角色授权,资源授权测试
@Test
public void testAuthorization(){
//1. 通过ini文件构建SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");
//2. 创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//3. 将SecurityManager设置到当前的环境中
SecurityUtils.setSecurityManager(securityManager);
//4. 从SecurityManager中创建一个subject
Subject subject = SecurityUtils.getSubject();
//5. 在认证提交前准备token -- 令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
//6. 执行认证提交
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//7. 是否认证通过
System.out.println("shiro--登录是否认证通过:" + subject.isAuthenticated());
//基于角色的授权
//hasRole传入单个角色标识
boolean isHasRole = subject.hasRole("role1");
//hasRole传入多个角色标识
boolean isHasRoles = subject.hasAllRoles(Arrays.asList("role1", "role2"));
System.out.println("单个角色判断:" + isHasRole + "\n" + "多个角色判断:" + isHasRoles);
//使用check方法进行授权,授权不通过会抛出异常
// subject.checkRole("role3");
//基于资源的授权
//isPermitted传入单个权限标识符
boolean isPermitted = subject.isPermitted("user:create:1");
//isPermitted传入多个权限标识符
boolean isPermittedAll = subject.isPermittedAll("user:create", "user:delete");
System.out.println("单个权限判断:" + isPermitted + "\n" + "多个权限判断:" + isPermittedAll);
//使用check方法进行授权,授权不通过会抛出异常
// subject.checkPermission("items:add");
}
3.测试结果:
授权成功:
授权失败:
自定义Realm:
基于框架的权限管理实质上就是对数据库进行的安全管理,权限管理的操作对象均是通过Realm进行的,针对不同的需求和场景,可以自定义Realm进行权限管理。即通过Realm从数据库中获取权限数据。
CustomRealm 类:
/*
* 自定义realm -- 模拟数据库操作
* */
public class CustomRealm extends AuthorizingRealm {
//复写命名方法
@Override
public void setName(String name) {
super.setName("CustomRealm");
}
//用于认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 从token中取出身份信息
String userCode = (String) authenticationToken.getPrincipal();
// 根据用户输入的userCode从数据库中查询密码 -- 这里模拟数据库查询结果
String passWord = "123";
//查询不到返回Null
//查询到返回认证信息AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userCode, passWord, this.getName());
return simpleAuthenticationInfo;
}
//用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从principle中获取主体身份信息
String userCode = (String) principalCollection.getPrimaryPrincipal();
//根据身份信息,获取权限信息 -- 模拟数据库连接
List<String> permissions = new ArrayList<String>();
permissions.add("user:create"); //用户创建权限
permissions.add("items:add"); //商品添加权限
//查询到权限数据,返回授权信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//将授权信息添加到SimpleAuthorizationInfo中
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
}
ini配置文件:
[main]
# 自定义realm
customRealm= com.shiro.realm.CustomRealm
# 将realm设置到SecurityManager,类比spring中的注入
securityManager.realms=$customRealm
通过自定义的Reaml可以对数据库进行权限数据获取,返回认证信息AuthenticationInfo、授权信息SimpleAuthorizationInfo。
接着就是认证/授权了,测试用例只需要在ini文件加载处进行更改。测试用例的本质就是模拟用户的操作。
//1. 通过ini文件构建SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");