八月加油!
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");