前面簡單介紹了shiro這個框架的一些基本知識,包括其架構模型,主要的功能,關鍵名稱的含義,以及核心模塊和對應的接口定義。
開始的話
今天我們從一個簡單示例,先了解使用shiro實現Web應用認證時,一名開發者需要做些什么。同樣秉持著技術學習的原則,我們僅僅使用shiro框架以及一些其他簡化開發的工具庫,不會涉及到一些IOC容器,這樣在進行模塊配置以及依賴關系梳理時,通過手動的配置的方式,讓我們更加容易理解...
文章介紹
通過這篇文章,你可以有以下幾個方面的收獲:
- 基于maven創建一個項目,養成項目依賴統一管理的習慣
- 了解shiro在項目中的使用過程以及相關的配置
- 了解如何實現通過shiro完成認證以及授權
- 了解shiro認證流程
- 對shiro從理論的認識升華到基礎實踐
- 獲得一個演示示例
示例實現
創建項目
1) 你可以選擇通過你的IDE快速創建一個項目,比如通過Intellij Idea,通過File->New->Project選擇Maven Archetype創建一個空項目,這里archetype可以選擇quickstart
這里你很可能遇到一個idea的bug,按上圖提交后,發現idea卡死了,項目創建失敗且無法打開,如果沒有就恭喜你了
2)最終我們會得到一個文件夾,里面包含一個pom.xml文件,結構如下(如果有其他的內容建議刪除,比如src,因為這個pom我們作為項目parent維護)
添加依賴
細心的你會注意到,在根目錄下有個pom.xml,同時還有個ui-mvc目錄下也有個pom.xml文件,根目錄下的我一般習慣作為整個項目的父級依賴配置文件,用來管理所有依賴、插件版本以及屬性值,ui-mvc下的pom主要通過parent實現屬性繼承,這樣來實現配置集中化管理
./pom.xml
< project >
//...
< groupId >com.sucls.security< /groupId >
< artifactId >auth-shiro< /artifactId >
< version >1.0-SNAPSHOT< /version >
< packaging >pom< /packaging >
< properties >
< project.build.sourceEncoding >UTF-8< /project.build.sourceEncoding >
< maven.compiler.source >1.8< /maven.compiler.source >
< maven.compiler.target >1.8< /maven.compiler.target >
< shiro.version >1.9.1< /shiro.version >
// ...
< /properties >
< dependencyManagement >
< dependencies >
< !-- 核心 -- >
< dependency >
< groupId >org.apache.shiro< /groupId >
< artifactId >shiro-core< /artifactId >
< version >${shiro.version}< /version >
< /dependency >
< dependency >
< groupId >org.apache.shiro< /groupId >
< artifactId >shiro-web< /artifactId >
< version >${shiro.version}< /version >
< /dependency >
// ...
< /dependencies >
< /dependencyManagement >
// ...
< /project >
./ui-mvc/pom.xml
< parent >
< groupId >com.sucls.security< /groupId >
< artifactId >auth-shiro< /artifactId >
< version >1.0-SNAPSHOT< /version >
< relativePath >../../pom.xml< /relativePath >
< /parent >
< artifactId >auth-shiro-ui-mvc< /artifactId >
< packaging >war< /packaging >
< name >ui-mvc< /name >
< dependencies >
< !-- 核心 -- >
< dependency >
< groupId >org.apache.shiro< /groupId >
< artifactId >shiro-core< /artifactId >
< /dependency >
< dependency >
< groupId >org.apache.shiro< /groupId >
< artifactId >shiro-web< /artifactId >
< /dependency >
< /dependencies >
大部分內容都省略了,可以參考文末源代碼,主要引入了shiro相關的依賴,以及一個提高開發效率的工具包
編寫shiro相關配置
我們所有代碼都會寫到ui-mvc模塊下,看到名稱就知道我們通過mvc的結構,因此前端是通過jsp這個老技術,后面會講到如何通過前后端分離來實現認證功能。
下面主要從以下幾個方面進行配置:
web.xml以前說過,認證基本都是基于Filter實現,同樣shiro有一個核心的過濾器(該過濾器會將我們的配置解析成一個個過濾器鏈)
< filter >
< filter-name >shiroFilter< /filter-name >
< filter-class >org.apache.shiro.web.servlet.ShiroFilter< /filter-class >
< /filter >
< filter-mapping >
< filter-name >shiroFilter< /filter-name >
< url-pattern >/*< /url-pattern >
< /filter-mapping >
配置監聽,在系統啟動時基于ServletContextListener調用初始化參數完成一些基本的系統初始化工作
< context-param >
< param-name >shiroEnvironmentClass< /param-name >
< param-value >org.apache.shiro.web.env.DefaultWebEnvironment< /param-value >
< /context-param >
< listener >
< listener-class >com.sucls.security.security.SimpleEnvironmentLoaderListener< /listener-class >
< /listener >
這里通過自定義的監聽,SimpleEnvironmentLoaderListener類承載了shiro基本上所有的配置,在這里我們會完整地構建SecurityManager對象以及Filter的配置,如果讀過以前的文章你就會知道,SecurityManager主要的工作包括:
- 代理實現用戶身份的認證
- 對API結果進行緩存
- 實現了用戶多種形式登錄(多Realm)
- 實現用戶會話管理
- 實現記住我功能
- ...
ShiroFilter配置
FormAuthenticationFilter authcFilter = (FormAuthenticationFilter) filterChainResolver.getFilterChainManager().getFilters().get(DefaultFilter.authc.name());
authcFilter.setLoginUrl("/login.jsp");
authcFilter.setSuccessUrl("/index.html");
filterChainResolver.getFilterChainManager().addToChain("/webjars/**", DefaultFilter.anon.name());
filterChainResolver.getFilterChainManager().addToChain("/assets/**", DefaultFilter.anon.name());
filterChainResolver.getFilterChainManager().addToChain("/**", DefaultFilter.authc.name());
這里主要通過配置FilterChainResolver對象來完成ShiroFilter對象的構建,后面講到源代碼時會細說。
SecurityManager配置
private void configureWebSecurityManager(DefaultWebSecurityManager securityManager) {
//
securityManager.setAuthenticator(newAuthenticator());
securityManager.setRealms(Arrays.asList(initRealm()));
}
private Authenticator newAuthenticator() {
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); // 至少一個Realm
authenticator.setRealms(Arrays.asList(initRealm()) );
return authenticator;
}
private Realm initRealm(){
return RealmBuilder.create()
.inMemoryRealm() .user("admin").password("123456").role("ROLE_ADMIN").permissions(Arrays.asList("read","edit","create","delete"))
.and()
.user("user").password("123456").role("ROLE_USER").permission("read")
.build();
}
這里僅根據需要配置了Realm以及認證規則Authenticator
引入頁面
前端模板來源bootstrap quick,在此基礎上修改了遠程資源(js、css)為本地引用,以及引入jsp文件頭對應的配置
啟動項目
通過引入tomcat插件來啟動項目:
< plugin >
< groupId >org.apache.tomcat.maven< /groupId >
< artifactId >tomcat7-maven-plugin< /artifactId >
< version >2.2< /version >
< configuration >
< port >8080< /port >
< path >/< /path >
< /configuration >
< /plugin >
這樣不需要我們單獨弄個tomcat,通過插件即可實現項目熱啟動,方便調試與靜態頁面的修改
執行認證
1)進入系統 http://localhost:8080/ 由于沒有登錄,跳轉到登錄頁http://localhost:8080/login.jsp
2)輸入用戶名密碼,按以上的配置admin/123456,登錄成功,進入主頁
3)用戶鑒權,上面可以看到admin配置了ROLE_ADMIN角色,user配置了ROLE_USER角色,所以
admin登錄時:
訪問 http://localhost:8080/system/getProperties.json 正常返回數據 ;
訪問 http://localhost:8080/subject/getSubject.json 進入未授權頁面
關于shiro鑒權部分主要是基于Filter以及AOP完成,其中請求交易基于Filter,在請求時基于登錄權限信息進行交易攔截,而AOP則可以針對方法的調用階段,更加靈活和通用。具體的實現過程后面會細說,同時可以看到示例簡單的實現過程
示例分析
在整個示例中,回憶我們做了什么,有什么用?
- 引入shiro相關的依賴包,這點沒什么說的
- 配置web.xml的Filter以及Listener Listener則是基于Servlet的ServletContextListener規則,在web容器啟動后,調用contextInitialized方法完成容器初始化工作。這里的初始化包括:
- 對SecurityManager的配置,后面我們會看到關于shiro的配置基本都是針對這個對象。包括Realm、多Realm認證策略、RememberMe、SessionManager等等
- 對FilterChainResolver的配置,目的是為了完善ShiroFilter對象,主要針對請求路徑對應的過濾器,下面每一行都是一個根據請求路徑匹配的過濾器鏈,一段請求匹配,則進入對應過濾器鏈,所有注意配置順序
/assets/** = anon
/login = authc
/admin/** = roles[ROLE_ADMIN]
/admin/add* = perms[add:*] //[action:type:instance]
/** = authc
- 引入靜態頁面,這里基于jsp實現,當然還有其他前端模板引擎可以使用,其中靜態資源通過引入webjars來加載
- 由于沒有引入springmvc,在資源映射(請求到頁面或交易)以及依賴管理、AOP裝配等等都是手動通過代碼完成,增加了代碼復雜度,但是結構會更清晰,更容易理解
要知道,我們的目的是為SecurityManager注入屬性以及配置ShiroFilter過濾規則,并不一定需要基于ServletContextListener,這只是一個選擇,比如還可以在Filter的init方法中完成,只要保證系統啟動后對應的配置加載或處理完成即可。
-
模塊
+關注
關注
7文章
2730瀏覽量
47656 -
接口
+關注
關注
33文章
8687瀏覽量
151674 -
容器
+關注
關注
0文章
499瀏覽量
22092 -
Web應用
+關注
關注
0文章
16瀏覽量
3511
發布評論請先 登錄
相關推薦
評論