談談Shiro的原理及在SSM和SpringBoot兩種環境下的使用姿勢(上篇)
本篇主要是記錄關於Shiro進行認證和授權的大致原理,然後是單獨在Shiro中實現認證和授權的方式。最後主要說明在傳統SSM的工程中使用Shiro和在SpringBoot的工程中使用Shiro進行整合。關於認證和授權,我這裡採用的是規範的RBAC許可權模型,資料庫的建表語句已經託管github的工程中。
在進行Shiro具體認證和授權的流程介紹之前,首先說一下Shiro中幾個比較重要的概念(其中的介面或者類)。
- Subject:含義為主體。Subject作為使用者端(使用Shiro進行授權的一端)的抽象,在Shiro中是通過一個介面來體現的。在使用Shiro的時候,我們也就是通過呼叫Subject的認證和授權的方法來實現具體的認證和授權的。
SecurityManager:含義為安全管理器。SecurityManager是整個Shiro的核心所在,它負責對所有的Subject進行安全管理,我們在通過Subject進行授權和認證的時候,Subject其實是通過SecurityManager來實現具體業務邏輯的。SecurityManager在Shiro中是通過一個介面來體現的,而且它繼承了Authenticator,Authorizer,SessionManager。如下圖:
這樣SecurityManager的認證會交給Authenticator定義的業務邏輯完成,授權會交給Authorizer定義的業務邏輯完成,會話管理會交給SessionManager來完成。Authenticator:含義為認證器,主要是完成對使用者身份的認證。Authenticator在Shiro是一個介面,在Shiro中提供了一個ModularRealmAuthenticator的實現類用於完成認證。ModularRealmAuthenticator已經可以完成大多數的認證需求,如果我們有新的業務,那麼我們通過自定義認證器來完成特殊的業務。
Authorizer:含義為授權器,主要是完成對使用者操作的授權。Authorizer在Shiro中是一個介面,相比Authenticator,Shiro提供了更多的授權器的實現類,其中也包括類似的ModularRealmAuthorizer。這些預設的實現類可以完成大多數需求,如果我們有新的業務,那麼我們通過自定義授權器來完成特殊的業務。
realm:含義為領域。Realm在Shiro中也是一個介面,SecurityManager進行認證和授權的時候,它所需要的資料資訊都是從Realm中獲取的。Realm有多種實現類,代表了Realm可以從多種資料來源中讀取已經配置的資料資訊用於認證和授權。Realm在Shiro中是一個相當關鍵的部分,因為我們的授權器和認證器最終都是通過Realm來實現各自的業務的。
SessionDAO:含義為Session會話.在Shiro中也是作為一個介面來體現的。sessiondao可以實現將session資料持久化。
CacheManager:含義為快取管理器。使用者的認證和授權的資訊可以快取到CacheManager中,從而提升資料的訪問效能。
Cryptography:含義為密碼管理。shiro提供了Cryptography來作為我們資訊的加密和介面的工具。
ok,在說完了Shiro中幾個比較關鍵的概念之後,我們開始看一下在Shiro中是如何進行的認證和授權的。
注:所有的sql都包含在了工程中
下面這張圖說明了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的使用進行整合。
程式碼地址: https://github.com/fuyunwang/ShiroDemo.git
如果對您有過幫助,感謝您的一個star.
相關推薦
談談Shiro的原理及在SSM和SpringBoot兩種環境下的使用姿勢(上篇)
本篇主要是記錄關於Shiro進行認證和授權的大致原理,然後是單獨在Shiro中實現認證和授權的方式。最後主要說明在傳統SSM的工程中使用Shiro和在SpringBoot的工程中使用Shiro進行整合。關於認證和授權,我這裡採用的是規範的RBAC許可權模型
SSM和SSH兩種框架的比較
1.SSH為Struts+Spring+Hibernate的縮寫,SSM為SpringMVC+Spring+Mybatis的縮寫。 2.Struts和SpringMVC用作控制器,Spring用作管理元件,Hibernate和Mybatis用作資料持久化。 3.兩者的共同點都是用了Spr
Native和H5兩種情況的頭像上傳
最近的工作中接觸了一個小的功能,上傳頭像。上傳頭像是很多應用中的東西,描述下自己的應用。上傳頭像應用的地方: 1.最開始進入app的時候會提示註冊,然後就會呼叫。 2.成功的註冊完畢以後,可以在個人資訊裡面修改頭像。 3.在H5(某個WebView)中也可以修改
JMS訊息中介軟體原理及ActiveMQ在企業中的應用(接上篇)
程式碼實現:傳送訊息---》接受訊息---》伺服器配置 //1 傳送訊息(接受回覆訊息) public class SenderMessageService { //釋出指定訊息到指定地址(在釋出之前,建議將訊息儲存到資料庫) public void publish(St
virtualBox中有線和無線兩種情況下centos虛擬機和本地機互ping的方案
隨機 需要 保存 bubuko 可能 ping htm 兩個 方案 之前寫微信點餐系統的時候,剛開始是無線連接,然後每次進去虛擬機ip和本地ip都會改變,所以每次都需要配置一下nginx,還有本地的路徑。之後換有線連接,就研究了一下橋接模式有線情況下虛擬機靜態ip設置,
MapReduce實現兩表的Join--原理及python和java程式碼實現
用Hive一句話搞定的,但是有時必須要用mapreduce 方法介紹 1. 概述 在傳統資料庫(如:MYSQL)中,JOIN操作是非常常見且非常耗時的。而在HADOOP中進行JOIN操作,同樣常見且耗時,由於Hadoop的獨特設計思想,當進行JOIN操作時,有一
分析RedisRDB和AOF兩種持久化機制的工作原理及優劣勢
一、RDB和AOF兩種持久化機制的介紹 RDB持久化機制,對redis中的資料執行週期性的持久化 AOF機制對每條寫入命令作為日誌,以append-only(追加)的模式寫入一個日誌檔案中,在redis重啟的時候,可以通過回放AOF日誌中的寫入指令來重新構建整個資料集 如果我們想要redis僅僅作
Django爬蟲基本原理及Request和Response分析
detail 密碼 href Go 模塊 ica 正則表達式 ons CI 一、爬蟲互聯網是由網絡設備(網線,路由器,交換機,防火墻等等)和一臺臺計算機連接而成,像一張網一樣。互聯網的核心價值在於數據的共享/傳遞:數據是存放於一臺臺計算機上的,而將計算機互聯到一起的目的就是
使用maven構建專案時,SSM和springboot專案的打包與雲伺服器部署
下面講講如何打包SSM和springboot專案,並部署到雲伺服器上。 由於使用的IDE不同,有的使用eclipse,有的使用idea,所以如果在IDE中按照 maven clean 再 maven install的方式打包會稍有不同,下面介紹一種通用的方式,不論SS
Redis詳解 - SpringBoot整合Redis,RedisTemplate和註解兩種方式的使用
本文主要講 Redis 的使用,如何與 SpringBoot 專案整合,如何使用註解方式和 RedisTemplate 方式實現快取。最後會給一個用 Redis 實現分散式鎖,用在秒殺系統中的案例。 更多 Redis 的實際運用場景請關注開源專案 coderiver 專案地址:github.com/cac
springboot+mybatis+druid實現多資料來源配置,支援註解和xml兩種sql書寫方式
https://github.com/cheegoday/springboot-demo-djg 要點: 一、依次建立以下幾個Bean 資料來源:DataSource session工廠:SqlSessionFactory 執行緒安全session:Sql
資料結構和演算法 | 氣泡排序演算法原理及實現和優化
氣泡排序(Bubble Sort)是排序演算法裡面比較簡單的一個排序。它重複地走訪要排序的數列,一次比較兩個資料元素,如果順序不對則進行交換,並一直重複這樣的走訪操作,直到沒有要交換的資料元素為止。 氣泡排序的原理 為了更深入地理解氣泡排序的操作步驟,我們現在
資料結構和演算法 | 插入排序演算法原理及實現和優化
插入排序演算法是所有排序方法中最簡單的一種演算法,其主要的實現思想是將資料按照一定的順序一個一個的插入到有序的表中,最終得到的序列就是已經排序好的資料。 直接插入排序是插入排序演算法中的一種,採用的方法是:在新增新的記錄時,使用順序查詢的方式找到其要插入的位置,
排序演算法 | 希爾排序演算法原理及實現和優化
希爾排序也是一種插入排序演算法,也叫作縮小增量排序,是直接插入排序的一種更高效的改進演算法。 希爾排序因其設計者希爾(Donald Shell)的名字而得名,該演算法在 1959 年被公佈。一些老版本的教科書和參考手冊把該演算法命名為 Shell-Metzner
資料結構和演算法 | 歸併排序演算法原理及實現和優化
歸併排序(MERGE-SORT)是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。 歸併排序的原理 歸
分散式理論基礎(一)一致性及解決一致性的兩種方式:2PC和3PC (轉載 不錯)
分散式理論基礎(一)一致性及解決一致性的兩種方式:2PC和3PC 1 一致性 1.1 簡述 一致性,是指對每個節點一個數據的更新,整個叢集都知道更新,並且是一致的 假設一個具有N個節點的分散式系統,當其滿足以下條件時,我們說這個系統滿足一致性: 全認同: 所有N個節點都認同一個結果 值合法: 該結果必須
20-02、圖解分析redis的RDB和AOF兩種持久化機制的工作原理
分析redis的RDB和AOF兩種持久化機制的工作原理 我們已經知道對於一個企業級的redis架構來說,持久化是不可減少的。 企業級redis叢集架構:海量資料、高併發、高可用。 持久化主要是做災難恢復,資料恢復,也可以歸類到高可用的一個環節裡面去。 比如你redis整個掛了,然後red
SpringBoot建立資料庫連線JdbcTemplate和Mybatis兩種方式
Spring Boot有兩種方法與資料庫建立連線,一種是使用JdbcTemplate,另一種整合Mybatis,下面分別為大家介紹一下如何整合和使用這兩種方式。1. 使用JdbcTemplate<dependency> <groupId>mysq
SpringBoot的properties和yml兩種配置方式, 配置注入引數, 以及配置檔案讀取失效的問題
SpringBoot支援兩種配置方式,一種是properties檔案,一種是yml 首先在pom檔案中新增依賴: <dependency> <groupId>org.springframework.boot</gro
Echart實現從資料庫獲取資料展示圖表(結合Servlet和SSM實現的兩種例項)
2018年5月30日(UPDATE): Google郵箱不怎麼上,建議Email [email protected]------------------------------