shiro學習筆記
一、概念:
shiro是apache旗下一個開源框架,它將軟件系統的安全認證相關的功能抽取出來,實現用戶身份認證、權限授權、加密、會話管理等功能,組成了一個通用的安全認證框架。
(一)shiro的功能:
1.驗證用戶。
2.對用戶執行訪問控制,如: 判斷用戶是否擁有角色admin,判斷用戶是否擁有訪問的權限。
3.在任何環境下使用Session API。例如CS程序。
4.可以使用多個用戶數據源。例如一個是oracle用戶庫,另外一個是mysql用戶庫。
5.單點登錄(SSO)功能。
6."Remember Me"服務,類似購物車的功能,shiro官方建議開啟。
(二)整體架構:
(三)名詞概念:
1.Subject:
Subject 是與程序進行交互的對象,可以是人也可以是服務或者其他,通常就理解為用戶。
所有Subject 實例都必須綁定到一個SecurityManager上。我們與一個 Subject交互,運行時shiro會自動轉化為與 SecurityManager交互的特定 subject的交互。
Subject在shiro中是一個接口,接口中定義了很多認證授權相關的方法,外部程序通過subject進行認證授,而subject是通過SecurityManager安全管理器進行認證授權。
2.SecurityManager:
SecurityManager即安全管理器,對全部的
SecurityManager是一個接口,繼承了Authenticator, Authorizer, SessionManager這三個接口。
3.Authenticator:
Authenticator即認證器,對用戶身份進行認證,Authenticator是一個接口,shiro
4.Authorizer:
Authorizer即授權器,用戶通過認證器認證通過,在訪問功能時需要通過授權器判斷用戶是否有此功能的操作權限。
5.Realm:
Realm即領域,相當於datasource數據源,securityManager進行安全認證需要通過Realm獲取用戶權限數據,比如:如果用戶身份數據在數據庫那麽realm就需要從數據庫獲取用戶身份信息。
註意:不要把realm理解成只是從數據源取數據,在realm中還有認證授權校驗的相關的代碼。
6.sessionManager:
sessionManager即會話管理,shiro框架定義了一套會話管理,它不依賴web容器的session,所以shiro可以使用在非web應用上,也可以將分布式應用的會話集中在一點管理,此特性可使它實現單點登錄。
7.SessionDAO:
SessionDAO即會話dao,是對session會話操作的一套接口,比如要將session存儲到數據庫,可以通過jdbc將會話存儲到數據庫。
8.CacheManager:
CacheManager即緩存管理,將用戶權限數據存儲在緩存,這樣可以提高性能。
9.Cryptography:
Cryptography即密碼管理,shiro提供了一套加密/解密的組件,方便開發。比如提供常用的散列、加/解密等功能。
二、認證流程:
三、一個認證的簡單Demo:
(一)添加POM依賴:
1 <dependency> 2 <groupId>org.apache.shiro</groupId> 3 <artifactId>shiro-core</artifactId> 4 <version>1.2.3</version> 5 </dependency>
(二)配置shiro.ini文件:
1 #用戶名=密碼,角色1,角色2...,角色n 2 [users] 3 root = secret, admin 4 guest = guest, guest 5 test = 123456, role1, role2 6 7 # 角色名=權限1,權限2...權限n 8 [roles] 9 admin = * 10 guest = guest 11 role1=perm1,perm2 12 role2=perm3
(三)代碼實現:
1 public static void main(String[] args) { 2 //1. 這裏的SecurityManager是org.apache.shiro.mgt.SecurityManager 3 // 而不是java.lang.SecurityManager 4 // 加載配置文件 5 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); 6 //2.解析配置文件,並且返回一些SecurityManger實例 7 SecurityManager securityManager = factory.getInstance(); 8 //3.將SecurityManager綁定給SecurityUtils 9 SecurityUtils.setSecurityManager(securityManager); 10 // 安全操作,Subject是當前登錄的用戶 11 Subject currentUser = SecurityUtils.getSubject(); 12 13 // 測試在應用的當前會話中設置屬性 14 Session session = currentUser.getSession(); 15 //放進去一個key和一個value 16 session.setAttribute("someKey", "aValue"); 17 //根據key拿到value 18 String value = (String) session.getAttribute("someKey"); 19 if ("aValue".equals(value)) {//比較拿到的值和原來的值是否一致 20 log.info("檢索到正確的值[" + value + "]"); 21 } 22 //嘗試進行登錄用戶,如果登錄失敗了,我們進行一些處理 23 if (!currentUser.isAuthenticated()) {//如果用戶沒有登錄過 24 UsernamePasswordToken token = new UsernamePasswordToken("test", "123456"); 25 token.setRememberMe(true);//是否記住用戶 26 try { 27 currentUser.login(token); 28 //當我們獲登錄用戶之後 29 log.info("用戶 [" + currentUser.getPrincipal() + "] 登陸成功"); 30 //登出 31 currentUser.logout(); 32 } catch (UnknownAccountException uae) { 33 log.info(token.getPrincipal() + "賬戶不存在"); 34 } catch (IncorrectCredentialsException ice) { 35 log.info(token.getPrincipal() + "密碼不正確"); 36 } catch (LockedAccountException lae) { 37 log.info(token.getPrincipal() + "用戶被鎖定了 "); 38 } catch (AuthenticationException ae) { 39 log.info(ae.getMessage()); //無法判斷是什麽錯了 40 } 41 } 42 }
(四)自定義Realm:
1、配置Realm:
1 <!--自定義Realm --> 2 <bean id="myRealm" class="com.ssm.shiro.realm.MyRealm" /> 3 <!--安全管理 --> 4 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 5 <property name="realm" ref="myRealm"></property> 6 </bean>
2、實現代碼:
1 /* 2 * 自定義Realm 3 */ 4 public class CustomRealm extends AuthorizingRealm{ 5 //設置Realm名稱 6 @Override 7 public void setName(String name){ 8 super.setName("customRealm"); 9 } 10 11 //用於認證 12 @Override 13 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException{ 14 //1.從token取出用戶身份信息 15 String userCode = (String)token.getPrincipal(); 16 17 //2.根據用戶userCode查詢數據庫 18 //模擬從數據庫查詢到的密碼 19 String password = "123"; 20 21 //查詢不到返回null 22 23 //查詢到返回認證信息 24 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userCode,password,this.getName()); 25 26 return authenticationInfo; 27 } 28 29 //用於授權 30 @Override 31 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){ 32 33 return null; 34 } 35 }
四、與SpringMVC整合的Demo:
1、配置web.xml:
1 <!-- 添加shiro過濾器 --> 2 <filter> 3 <filter-name>shiroFilter</filter-name> 4 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 5 </filter> 6 <filter-mapping> 7 <filter-name>shiroFilter</filter-name> 8 <url-pattern>/*</url-pattern> 9 </filter-mapping>
2、配置applicationContext.xml:
1 <!--自定義Realm --> 2 <bean id="myRealm" class="com.ssm.shiro.realm.MyRealm" /> 3 <!--安全管理 --> 4 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 5 <property name="realm" ref="myRealm"></property> 6 </bean> 7 <!--shiro 過濾器 --> 8 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 9 <!-- Shiro過濾器的核心安全接口,這個屬性是必須的 --> 10 <property name="securityManager" ref="securityManager" /> 11 <!--身份認證失敗,則跳轉到登錄頁面的配置 --> 12 <property name="loginUrl" value="/login.jsp" /> 13 <!--權限認證失敗,則跳轉到指定頁面 --> 14 <property name="unauthorizedUrl" value="/unauthorized.jsp" /> 15 <!-- Shiro連接約束配置,即過濾鏈的定義 --> 16 <property name="filterChainDefinitions"> 17 <value> 18 /login.jsp = anon 19 /user/login=anon 20 /user/logout = logout 21 /admin.jsp=roles[admin] 22 /** = authc 23 </value> 24 </property> 25 </bean>
3、實現登陸login:
1 @RequestMapping("/login") 2 public String login(Entity user, HttpServletRequest request){ 3 System.out.println("user:"+user.getUsername()); 4 Subject subject=SecurityUtils.getSubject(); 5 ByteSource salt = ByteSource.Util.bytes(user.getUsername()); 6 //加密 7 String getpassword = new Md5Hash(user.getPassword(), salt).toString(); 8 UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(),getpassword); 9 try { 10 //調用subject.login(token)進行登錄,會自動委托給securityManager,調用之前 11 subject.login(token);//會跳到我們自定義的realm中 12 request.getSession().setAttribute("user",user); 13 return "success"; 14 } catch (AuthenticationException ae) { 15 //unexpected condition? error? 16 System.out.println("登錄失敗: " + ae.getMessage()); 17 return "redirect:/login.jsp"; 18 } 19 }
4、Demo代碼例子:
https://github.com/wangshanghello/shiro
shiro學習筆記