apache shiro框架簡介
apache shiro是一個強大而靈活的開源安全框架,它能夠干凈利落地處理身份認證,授權,企業會話管理和加密。現在,使用apache shiro的人越來越多,因為它相當簡單,相比比spring security,shiro可能沒有spring security那么多強大的功能,但是在實際工作時可能并不需要那么復雜的東西,所以使用簡單的shiro就足夠了。
以下是你可以用 apache shiro所做的事情:
shiro的4大核心部分——身份驗證,授權,會話管理和加密
authentication:身份驗證,簡稱“登錄”。
authorization:授權,給用戶分配角色或者權限資源
session management:用戶session管理器,可以讓cs程序也使用session來控制權限
cryptography:把jdk中復雜的密碼加密方式進行封裝。
除了以上功能,shiro還提供很多擴展
web support:主要針對web應用提供一些常用功能。
caching:緩存可以使應用程序運行更有效率。
concurrency:多線程相關功能。
testing:幫助我們進行測試相關功能
run as:一個允許用戶假設為另一個用戶身份(如果允許)的功能,有時候在管理腳本很有用。
remember me:記住用戶身份,提供類似購物車功能。
shiro框架認證流程
application code:應用程序代碼,由開發人員負責開發的
subject:框架提供的接口,是與程序進行交互的對象,可以是人也可以是服務或者其他,通常就理解為用戶。所有subject 實例都必須綁定到一個securitymanager上。我們與一個 subject 交互,運行時shiro會自動轉化為與 securitymanager交互的特定 subject的交互。
securitymanager:框架提供的接口,是 shiro的核心,代表安全管理器對象。初始化時協調各個模塊運行。然而,一旦 securitymanager協調完畢,securitymanager 會被單獨留下,且我們只需要去操作subject即可,無需操作securitymanager 。 但是我們得知道,當我們正與一個 subject 進行交互時,實質上是 securitymanager在處理 subject 安全操作。
realm:可以開發人員編寫,框架也提供一些。realms在 shiro中作為應用程序和安全數據之間的“橋梁”或“連接器”。他獲取安全數據來判斷subject是否能夠登錄,subject擁有什么權限。他有點類似dao。在配置realms時,需要至少一個realm。而且shiro提供了一些常用的 realms來連接數據源,如ldap數據源的jndildaprealm,jdbc數據源的jdbcrealm,ini文件數據源的inirealm,properties文件數據源的propertiesrealm,等等。我們也可以插入自己的 realm實現來代表自定義的數據源。 像其他組件一樣,realms也是由securitymanager控制。
更詳細的圖
下面就開始shiro與ssm工程的整合使用
下載地址:
下載下來這兩個個文件,一個jar包,一個源碼文件
首先,第一步,將jar包導入到工程中
然后,第二步,在web.xml中配置spring框架提供的用于整合shiro框架的過濾器(一定要放到springmvc或struts框架過濾器的前面,為了保險起見,放到最上面就好了)
1
2
3
4
5
6
7
8
9
|
<!--配置過濾器,用于整合shiro--> <filter> <filter-name>shirofilter</filter-name> <filter- class >org.springframework.web.filter.delegatingfilterproxy</filter- class > </filter> <filter-mapping> <filter-name>shirofilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
第三步,在spring配置文件中配置bean,id為shirofilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<!-- 配置shiro框架的過濾器工廠bean --> <bean id= "shirofilter" class = "org.apache.shiro.spring.web.shirofilterfactorybean" > <property name= "securitymanager" ref= "securitymanager" /> <property name= "loginurl" value= "/index.jsp" /> <property name= "successurl" value= "" /> <property name= "unauthorizedurl" value= "/noprivilegeui.jsp" /> <!-- 指定url級別攔截策略 --> <property name= "filterchaindefinitions" > <value> /script/** = anon /style/** = anon /index.jsp* = anon /noprivilegeui.jsp* = anon /user/login = anon /role/findallrolelist = perms[ "角色管理" ] /** = authc </value> </property> </bean> |
securitymanager:這個屬性是必須的。
loginurl :沒有登錄的用戶請求需要登錄的頁面時自動跳轉到登錄頁面,不是必須的屬性,不輸入地址的話會自動尋找項目web項目的根目錄下的”/login.jsp”頁面。
successurl :登錄成功默認跳轉頁面,可以不配置,不配置則跳轉至”/”。如果登陸前點擊的一個需要登錄的頁面,則在登錄自動跳轉到那個需要登錄的頁面。不跳轉到此。
unauthorizedurl :沒有權限默認跳轉的頁面。
anon: 例子/admins/** = anon 沒有參數,表示可以匿名使用(需要認證(登錄))。
authc : 例如/admins/user/** = authc 表示需要認證(登錄)才能使用,沒有參數
roles:例子/admins/user/** = roles[admin], 參數可以寫多個,多個時必須加上引號,并且參數之間用逗號分割,當有多個參數時,例如admins/user/** = roles["admin,guest"], 每個參數通過才算通過,相當于hasallroles()方法。
perms:例子/admins/user/** = perms[user:add:*], 參數可以寫多個,多個時必須加上引號,并且參數之間用逗號分割,例如/admins/user/** = perms["user:add:*,user:modify:*"],當有多個參數時必須每個參數都通過才通過,想當于 ispermitedall()方法。
rest:例子/admins/user/** = rest[user];根據請求的方法,相當于/admins/user/** = perms[user:method] ;其中method為post,get,delete等。
port:例子/admins/user/** = port[8081]; 當請求的url的端口不是8081是跳轉到schemal://servername:8081?querystring,其中schmal是協議http或https等,servername是你訪問的host,8081是url配置里port的端口,querystring
是你訪問的url里的?后面的參數。
authcbasic:例如/admins/user/** = authcbasic; 沒有參數表示httpbasic認證
ssl:例子/admins/user/** = ssl;沒有參數,表示安全的url請求,協議為https
user:例如/admins/user/** = user; 沒有參數表示必須存在用戶,當登入操作時不做檢查
注:anon,authcbasic,auchc,user是認證過濾器,
perms,roles,ssl,rest,port是授權過濾器
第四步:配置安全管理器
1
2
|
<!-- 配置安全管理器 --> <bean id= "securitymanager" class = "org.apache.shiro.web.mgt.defaultwebsecuritymanager" /> |
第五步:寫一個login方法,使用shiro提供的方式進行認證操作
這是我之前的login方法,以這種方法,shiro是不知道登錄驗證通過了的,一直不通過,所以我們要以shiro提供的認證操作方式進行登錄操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** * 登錄 */ @requestmapping ( "/login" ) public string login(user user, httpservletrequest request, model model){ httpsession session = request.getsession(); user newuser = userservice.login(user); if (newuser != null ){ session.setattribute( "loginuser" ,newuser); return "home/home" ; } model.addattribute( "errormessage" , "用戶名或者密碼不正確!" ); return "forward:/index.jsp" ; } |
修改后的login方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/** * 使用shiro框架提供的方式進行認證登錄 */ @requestmapping ( "/login" ) public string login(user user, httpservletrequest request, model model){ httpsession session = request.getsession(); //使用shiro框架提供的方式進行認證 subject subject = securityutils.getsubject(); //獲得當前登錄用戶對象,現在狀態為 “未認證” //用戶名密碼令牌 authenticationtoken token = new usernamepasswordtoken(user.getloginname(), md5utils.md5(user.getpassword())); try { subject.login(token); //執行你自定義的realm user user1 = (user) subject.getprincipal(); session.setattribute( "loginuser" ,user1); return "home/home" ; } catch (unknownaccountexception e){ e.printstacktrace(); model.addattribute( "errormessage" , "此用戶名不存在!" ); } catch (incorrectcredentialsexception e){ e.printstacktrace(); model.addattribute( "errormessage" , "密碼不正確!" ); } catch (exception e){ e.printstacktrace(); } return "forward:/index.jsp" ; } |
第六步:自定義realm,并注入給安全管理器
創建一個userrealm類,繼承authorizingrealm這個類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class userrealm extends authorizingrealm{ @autowired private userdao userdao; //認證方法 @override protected authenticationinfo dogetauthenticationinfo(authenticationtoken token) throws authenticationexception { system.out.println( "realm中的認證方法執行了。。。。" ); usernamepasswordtoken mytoken = (usernamepasswordtoken) token; string loginname = mytoken.getusername(); //根據用戶名查詢數據庫中的用戶,這個方法是自己寫的 user user = userdao.finduserbyloginname(loginname); if (user == null ){ //用戶名不存在 return null ; } //如果能查詢到,再由框架比對數據庫中查詢到的密碼和頁面提交的密碼是否一致 authenticationinfo info = new simpleauthenticationinfo(user, user.getpassword(), this .getname()); return info; } //授權方法 @override protected authorizationinfo dogetauthorizationinfo(principalcollection principals) { return null ; } } |
將自定義realm注入給安全管理器
1
2
3
4
5
6
7
|
<!-- 配置安全管理器 --> <bean id= "securitymanager" class = "org.apache.shiro.web.mgt.defaultwebsecuritymanager" > <!-- 注入realm --> <property name= "realm" ref= "userrealm" /> </bean> <!-- 注冊自定義realm --> <bean id= "userrealm" class = "com.demo.privilege.service.realm.userrealm" /> |
到這里程序就可以正常運行了,登錄后進入首頁
但是點擊角色管理,會進入沒有權限的頁面
這是因為我在spring配置文件中配置了 /role/findallrolelist = perms["角色管理"],而我還沒有給當前用戶授權,所以當前用戶沒有權限訪問此路徑
所以要給該用戶授權,在userrealm類中,編寫授權方法
1
2
3
4
5
6
7
8
9
10
11
12
|
//授權方法 @override protected authorizationinfo dogetauthorizationinfo(principalcollection principals){ simpleauthorizationinfo info = new simpleauthorizationinfo(); user user = (user) principals.getprimaryprincipal(); for (role role : user.getroles()){ for (privilege privilege : role.getprivileges()){ info.addstringpermission(privilege.getname()); } } return info; } |
這樣,就可以正常訪問了,這個授權方法是在訪問/role/findallrolelist這個路徑時,shiro框架自動調用的
我們之前進行權限控制是在spring配置文件中配置了 /role/findallrolelist = perms["角色管理"] ,現在介紹另一種方式,也是我比較喜歡的方式, 使用注解方式進行權限控制
使用shiro的方法注解方式權限控制
第一步:在springmvc配置文件中開啟shiro注解支持(注意:springmvc框架,放到springmvc配置文件中,struts放到spring配置文件中)
1
2
3
4
5
6
7
8
9
10
11
|
<!-- 保證實現了shiro內部lifecycle函數的bean執行 --> <bean id= "lifecyclebeanpostprocessor" class = "org.apache.shiro.spring.lifecyclebeanpostprocessor" /> <!-- 開啟shiro框架注解支持 --> <!-- 自動代理 --> <bean id= "defaultadvisorautoproxycreator" class = "org.springframework.aop.framework.autoproxy.defaultadvisorautoproxycreator" depends-on= "lifecyclebeanpostprocessor" > <!-- value必須設置為 true 使用cglib方式為對象創建代理對象, 默認為 false ,設為 false ,就是使用jdk方式為對象創建代理對象,程序會出錯 --> <property name= "proxytargetclass" value= "true" /> </bean> <!-- 配置shiro框架提供的切面類,用于創建代理對象 --> <bean class = "org.apache.shiro.spring.security.interceptor.authorizationattributesourceadvisor" /> |
第二步:在controller的方法上使用shiro注解------ @requirespermissions("") 執行這個方法必須有相應的權限
1
2
3
4
5
6
7
8
9
10
|
/** * 查詢崗位列表 */ @requirespermissions ( "角色列表" ) //執行這個方法必須有角色列表這個權限 @requestmapping ( "/findallrolelist" ) public string findallrolelist(model model){ list<role> rolelist = roleservice.findallrolelist(); model.addattribute( "rolelist" ,rolelist); return "role/list" ; } |
@requiresauthentication
驗證用戶是否登錄,等同于方法subject.isauthenticated() 結果為true時。
@ requiresuser
驗證用戶是否被記憶,user有兩種含義:
一種是成功登錄的(subject.isauthenticated() 結果為true);
另外一種是被記憶的( subject.isremembered()結果為true)。
@ requiresguest
驗證是否是一個guest的請求,與@ requiresuser完全相反。
換言之,requiresuser == ! requiresguest 。
此時subject.getprincipal() 結果為null.
@ requiresroles
例如:
1
2
|
@requiresroles ( "arolename" ); void somemethod(); |
如果subject中有arolename角色才可以訪問方法somemethod。如果沒有這個權限則會拋出異常authorizationexception。
@requirespermissions
例如:
1
2
|
@requirespermissions ( { "file:read" , "write:afile.txt" } ) void somemethod(); |
要求subject中必須同時含有file:read和write:afile.txt的權限才能執行方法somemethod()。否則拋出異常authorizationexception。
注解方式的權限控制就完成了,但這種方式沒有權限時不會自動跳轉到沒有權限的頁面,而是直接把異常拋到頁面了,所以我們要配置一個全局的異常處理
第三步:在springmvc配置文件中,進行如下配置,配置全局異常捕獲,當shiro框架拋出權限不足異常時,跳轉到權限不足提示頁面
1
2
3
4
5
6
7
8
|
<!-- 全局異常處理 --> <bean class = "org.springframework.web.servlet.handler.simplemappingexceptionresolver" > <property name= "exceptionmappings" > <props> <prop key= "org.apache.shiro.authz.unauthorizedexception" >redirect:/noprivilegeui.jsp</prop> </props> </property> </bean> |
使用shiro提供的頁面標簽方式權限控制
最后,說一說shiro提供的頁面標簽
第一步:在jsp頁面中引入shiro的標簽庫
1
|
<%@ taglib uri= "http://shiro.apache.org/tags" prefix= "shiro" %> |
第二步:使用shiro的標簽控制頁面元素展示
這樣,一個shiro入門程序就完成了。
總結
以上所述是小編給大家介紹的apache shiro的簡單介紹與使用教程(與spring整合使用),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:http://www.cnblogs.com/xiaobai1226/p/7634575.html