title: 談談Shiro的原理及在SSM和SpringBoot兩種環(huán)境下的使用姿勢(上篇)
categories:
- Spring
tags: - Shiro使用
本篇主要是記錄關于Shiro進行認證和授權的大致原理,然后是單獨在Shiro中實現(xiàn)認證和授權的方式。最后主要說明在傳統(tǒng)SSM的工程中使用Shiro和在SpringBoot的工程中使用Shiro進行整合科贬。關于認證和授權,我這里采用的是規(guī)范的RBAC權限模型,數(shù)據(jù)庫的建表語句已經(jīng)托管github的工程中泳梆。
在進行Shiro具體認證和授權的流程介紹之前,首先說一下Shiro中幾個比較重要的概念(其中的接口或者類)。
Subject:含義為主體榜掌。Subject作為用戶端(使用Shiro進行授權的一端)的抽象,在Shiro中是通過一個接口來體現(xiàn)的优妙。在使用Shiro的時候,我們也就是通過調用Subject的認證和授權的方法來實現(xiàn)具體的認證和授權的。
-
SecurityManager:含義為安全管理器憎账。SecurityManager是整個Shiro的核心所在,它負責對所有的Subject進行安全管理,我們在通過Subject進行授權和認證的時候,Subject其實是通過SecurityManager來實現(xiàn)具體業(yè)務邏輯的套硼。SecurityManager在Shiro中是通過一個接口來體現(xiàn)的,而且它繼承了Authenticator,Authorizer,SessionManager。如下圖:
Aaron Swartz
這樣SecurityManager的認證會交給Authenticator定義的業(yè)務邏輯完成,授權會交給Authorizer定義的業(yè)務邏輯完成,會話管理會交給SessionManager來完成胞皱。
Authenticator:含義為認證器,主要是完成對用戶身份的認證邪意。Authenticator在Shiro是一個接口,在Shiro中提供了一個ModularRealmAuthenticator的實現(xiàn)類用于完成認證。ModularRealmAuthenticator已經(jīng)可以完成大多數(shù)的認證需求,如果我們有新的業(yè)務,那么我們通過自定義認證器來完成特殊的業(yè)務反砌。
Authorizer:含義為授權器,主要是完成對用戶操作的授權雾鬼。Authorizer在Shiro中是一個接口,相比Authenticator,Shiro提供了更多的授權器的實現(xiàn)類,其中也包括類似的ModularRealmAuthorizer。這些默認的實現(xiàn)類可以完成大多數(shù)需求,如果我們有新的業(yè)務,那么我們通過自定義授權器來完成特殊的業(yè)務宴树。
realm:含義為領域策菜。Realm在Shiro中也是一個接口,SecurityManager進行認證和授權的時候,它所需要的數(shù)據(jù)信息都是從Realm中獲取的。Realm有多種實現(xiàn)類,代表了Realm可以從多種數(shù)據(jù)源中讀取已經(jīng)配置的數(shù)據(jù)信息用于認證和授權。Realm在Shiro中是一個相當關鍵的部分,因為我們的授權器和認證器最終都是通過Realm來實現(xiàn)各自的業(yè)務的做入。
SessionDAO:含義為Session會話.在Shiro中也是作為一個接口來體現(xiàn)的冒晰。sessiondao可以實現(xiàn)將session數(shù)據(jù)持久化。
CacheManager:含義為緩存管理器竟块。用戶的認證和授權的信息可以緩存到CacheManager中,從而提升數(shù)據(jù)的訪問性能壶运。
Cryptography:含義為密碼管理。shiro提供了Cryptography來作為我們信息的加密和界面的工具浪秘。
ok,在說完了Shiro中幾個比較關鍵的概念之后,我們開始看一下在Shiro中是如何進行的認證和授權的蒋情。
注:所有的sql都包含在了工程中
代碼地址: https://github.com/fuyunwang/ShiroDemo.git
認證:
下面這張圖說明了Shiro中進行認證的大致流程。
可以看到,Shiro最終其實通過Realm來完成最終的認證耸携。我們上面也已經(jīng)提到,Realm其實作為一種數(shù)據(jù)源的地位存在,其包含多個實現(xiàn)類代表著從不同的數(shù)據(jù)源中進行數(shù)據(jù)信息的獲取棵癣。我這里通過使用其中一個實現(xiàn)類IniRealm來實現(xiàn)最簡單的認證流程。(具體代碼在v0.1tag下夺衍。)
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:inirealm-shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken("beautifulsoup", "password");
try{
subject.login(token);
}catch(AuthenticationException e){
e.printStackTrace();
}
介紹完使用inirealm來完成從ini配置文件中獲取數(shù)據(jù)之后,我們做一個自定義Realm,來完成從數(shù)據(jù)庫這一數(shù)據(jù)源中獲取數(shù)據(jù)狈谊。
自定義Realm一般采用繼承自AuthorizingRealm的方式,然后重寫其中的認證和授權的方法,核心代碼如下,完整代碼在github。
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
String username=(String) token.getPrincipal();
ShiroDemoMapper mapper=getShiroMapper();
User user = mapper.findByUsername(username);
if(null!=user){
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(username, user.getPassword(),TAG);
return authenticationInfo;
}
return null;
}
然后進行配置:
[main]
#進行自定義realm的配置
customRealm=com.beautifulsoup.shiro.demo.realm.ShiroDemoRealm
securityManager.realms=$customRealm
這樣我們自定義的realm就會生效了,我們可以實現(xiàn)從數(shù)據(jù)庫中獲取數(shù)據(jù),然后校驗我們主體subject的信息,從而實現(xiàn)判斷是否認證成功的功能沟沙。
這里我們認證的時候,在數(shù)據(jù)庫中采用明文存取的密碼,這當然是不合理的,所以通常情況下,我們會采用加鹽(salt)的方式,使用散列算法如MD5對我們原有的密碼進行加密然后存入數(shù)據(jù)庫中河劝。(改進之后的代碼在v0.2標簽下)
首先修改配置文件,定義散列算法和散列次數(shù)等:
[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=3
#進行自定義realm的配置
customRealm=com.beautifulsoup.shiro.demo.realm.ShiroDemoRealm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
然后修改realm
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
String username=(String) token.getPrincipal();
ShiroDemoMapper mapper=getShiroMapper();
User user = mapper.findByUsername(username);
if(null!=user){
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(username, user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),TAG);
return authenticationInfo;
}
return null;
}
好,說完認證我們接下來說授權:
授權:
同樣,下面這張圖說明了在Shiro中進行授權的大致流程。
可以看到,SecurityManager最終交給Realm進行授權,實際上Realm是會返回一個ModularRealmAuthorizer類,該類得到所有的系統(tǒng)配置的權限然后調用PermissionResolver進行了權限的匹配矛紫。
接上所講,我們還是使用ini的配置文件來配置shiro實現(xiàn)授權,主要是配置文件更加方便我們的管理赎瞎。
這里我們的權限信息定義在配置文件中,畢竟我們的權限信息大多數(shù)是固定的,而且對于權限不多的情況下,這種方式更簡單。對于授權的操作主要包括針對角色的授權和針對資源的授權兩種方式,由于基于角色的權限控制不如基于資源的權限控制更加靈活,所以我們采用基于資源的權限控制為例來介紹颊咬。
配置文件進行配置的方式如下(代碼在v0.3標簽):
[users]
#用戶beautifulsoup具有role1和role3的角色
beautifulsoup=password,role1,role3
[roles]
#權限role1具有對01用戶訂單的創(chuàng)建權限和對02訂單資源的修改權限和對所有訂單的查詢操作务甥。
role1=item:create:01,item:update:02,item:query
role2=item:*:01,item:update:02
role3=item:create:02,item:delete:02
基本權限的驗證:
@Test
public void testIniAuthorization(){
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:permission-shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//首先認證,認證通過之后才能授權
UsernamePasswordToken token=new UsernamePasswordToken("beautifulsoup", "password");
try{
subject.login(token);
}catch(AuthenticationException e){
e.printStackTrace();
}
System.out.println("用戶的認證狀態(tài):"+subject.isAuthenticated());
boolean isPermitted=subject.isPermittedAll("item:create:01","item:query");
subject.checkPermissions("item:create:01","item:query");
System.out.println(isPermitted);
}
接下來使用自定義realm來實現(xiàn)用戶的授權。
在認證中已經(jīng)提到繼承自AuthorizingRealm,其提供了兩個方法,我們現(xiàn)在用第二個方法來實現(xiàn)授權的邏輯喳篇。(代碼在v0.4標簽)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//得到認證成功之后憑證的身份信息
String username=(String) principals.getPrimaryPrincipal();
//查詢數(shù)據(jù)庫得到所有的權限列表
List<String> permissionList=new ArrayList<String>();
UserCustomMapper mapper=getUserCustomMapper();
UserCustom userCustom = mapper.findUserCustomByUsername(username);
Set<RoleCustom> roles=userCustom.getRoleSet();
for(RoleCustom role:roles){
Set<Permission> permissionSet = role.getPermissionSet();
for (Permission permission:permissionSet) {
permissionList.add(permission.getPname());
}
}
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
authorizationInfo.addStringPermissions(permissionList);
return authorizationInfo;
}
同樣我們也需要配置:
[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=3
#進行自定義realm的配置
customRealm=com.beautifulsoup.shiro.demo.realm.ShiroDemoRealm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
OK,到現(xiàn)在為止上篇已經(jīng)對Shiro所有認證和授權的基礎知識做過了介紹,下篇開始對SSM和SpringBoot中的Shiro的使用進行整合敞临。
代碼地址: https://github.com/fuyunwang/ShiroDemo.git
如果對您有過幫助,感謝您的一個star。