title: 談談Shiro的原理及在SSM和SpringBoot兩種環境下的使用姿式(上篇) categories:git
本篇主要是記錄關於Shiro進行認證和受權的大體原理,而後是單獨在Shiro中實現認證和受權的方式。最後主要說明在傳統SSM的工程中使用Shiro和在SpringBoot的工程中使用Shiro進行整合。關於認證和受權,我這裏採用的是規範的RBAC權限模型,數據庫的建表語句已經託管github的工程中。github
在進行Shiro具體認證和受權的流程介紹以前,首先說一下Shiro中幾個比較重要的概念(其中的接口或者類)。算法
Subject:含義爲主體。Subject做爲用戶端(使用Shiro進行受權的一端)的抽象,在Shiro中是經過一個接口來體現的。在使用Shiro的時候,咱們也就是經過調用Subject的認證和受權的方法來實現具體的認證和受權的。sql
SecurityManager:含義爲安全管理器。SecurityManager是整個Shiro的核心所在,它負責對全部的Subject進行安全管理,咱們在經過Subject進行受權和認證的時候,Subject實際上是經過SecurityManager來實現具體業務邏輯的。SecurityManager在Shiro中是經過一個接口來體現的,並且它繼承了Authenticator,Authorizer,SessionManager。以下圖:數據庫
這樣SecurityManager的認證會交給Authenticator定義的業務邏輯完成,受權會交給Authorizer定義的業務邏輯完成,會話管理會交給SessionManager來完成。Authenticator:含義爲認證器,主要是完成對用戶身份的認證。Authenticator在Shiro是一個接口,在Shiro中提供了一個ModularRealmAuthenticator的實現類用於完成認證。ModularRealmAuthenticator已經能夠完成大多數的認證需求,若是咱們有新的業務,那麼咱們經過自定義認證器來完成特殊的業務。apache
Authorizer:含義爲受權器,主要是完成對用戶操做的受權。Authorizer在Shiro中是一個接口,相比Authenticator,Shiro提供了更多的受權器的實現類,其中也包括相似的ModularRealmAuthorizer。這些默認的實現類能夠完成大多數需求,若是咱們有新的業務,那麼咱們經過自定義受權器來完成特殊的業務。緩存
realm:含義爲領域。Realm在Shiro中也是一個接口,SecurityManager進行認證和受權的時候,它所須要的數據信息都是從Realm中獲取的。Realm有多種實現類,表明了Realm能夠從多種數據源中讀取已經配置的數據信息用於認證和受權。Realm在Shiro中是一個至關關鍵的部分,由於咱們的受權器和認證器最終都是經過Realm來實現各自的業務的。安全
SessionDAO:含義爲Session會話.在Shiro中也是做爲一個接口來體現的。sessiondao能夠實現將session數據持久化。session
CacheManager:含義爲緩存管理器。用戶的認證和受權的信息能夠緩存到CacheManager中,從而提高數據的訪問性能。app
Cryptography:含義爲密碼管理。shiro提供了Cryptography來做爲咱們信息的加密和界面的工具。
ok,在說完了Shiro中幾個比較關鍵的概念以後,咱們開始看一下在Shiro中是如何進行的認證和受權的。
注:全部的sql都包含在了工程中
代碼地址: github.com/fuyunwang/S… 認證:
下面這張圖說明了Shiro中進行認證的大體流程。
能夠看到,Shiro最終其實經過Realm來完成最終的認證。咱們上面也已經提到,Realm其實做爲一種數據源的地位存在,其包含多個實現類表明着從不一樣的數據源中進行數據信息的獲取。我這裏經過使用其中一個實現類IniRealm來實現最簡單的認證流程。(具體代碼在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配置文件中獲取數據以後,咱們作一個自定義Realm,來完成從數據庫這一數據源中獲取數據。 自定義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就會生效了,咱們能夠實現從數據庫中獲取數據,而後校驗咱們主體subject的信息,從而實現判斷是否定證成功的功能。 這裏咱們認證的時候,在數據庫中採用明文存取的密碼,這固然是不合理的,因此一般狀況下,咱們會採用加鹽(salt)的方式,使用散列算法如MD5對咱們原有的密碼進行加密而後存入數據庫中。(改進以後的代碼在v0.2標籤下)
首先修改配置文件,定義散列算法和散列次數等:
[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類,該類獲得全部的系統配置的權限而後調用PermissionResolver進行了權限的匹配。
接上所講,咱們仍是使用ini的配置文件來配置shiro實現受權,主要是配置文件更加方便咱們的管理。
這裏咱們的權限信息定義在配置文件中,畢竟咱們的權限信息大多數是固定的,並且對於權限很少的狀況下,這種方式更簡單。對於受權的操做主要包括針對角色的受權和針對資源的受權兩種方式,因爲基於角色的權限控制不如基於資源的權限控制更加靈活,因此咱們採用基於資源的權限控制爲例來介紹。
配置文件進行配置的方式以下(代碼在v0.3標籤):
[users]
#用戶beautifulsoup具備role1和role3的角色
beautifulsoup=password,role1,role3
[roles]
#權限role1具備對01用戶訂單的建立權限和對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("用戶的認證狀態:"+subject.isAuthenticated());
boolean isPermitted=subject.isPermittedAll("item:create:01","item:query");
subject.checkPermissions("item:create:01","item:query");
System.out.println(isPermitted);
}
複製代碼
接下來使用自定義realm來實現用戶的受權。 在認證中已經提到繼承自AuthorizingRealm,其提供了兩個方法,咱們如今用第二個方法來實現受權的邏輯。(代碼在v0.4標籤)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//獲得認證成功以後憑證的身份信息
String username=(String) principals.getPrimaryPrincipal();
//查詢數據庫獲得全部的權限列表
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,到如今爲止上篇已經對Shiro全部認證和受權的基礎知識作過了介紹,下篇開始對SSM和SpringBoot中的Shiro的使用進行整合。 代碼地址: github.com/fuyunwang/S…
若是對您有過幫助,感謝您的一個star。