1. 程式人生 > >shiro學習筆記

shiro學習筆記

成功 通過 是什麽 etsec png authent exc span edi

一、概念:

shiroapache旗下一個開源框架,它將軟件系統的安全認證相關的功能抽取出來,實現用戶身份認證、權限授權、加密、會話管理等功能,組成了一個通用的安全認證框架。

(一)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的交互。

Subjectshiro中是一個接口,接口中定義了很多認證授權相關的方法,外部程序通過subject進行認證授,而subject是通過SecurityManager安全管理器進行認證授權。

2.SecurityManager

SecurityManager即安全管理器,對全部的

subject進行安全管理,它是shiro的核心,負責對所有的subject進行安全管理。通過SecurityManager可以完成subject的認證、授權等,實質上SecurityManager是通過Authenticator進行認證,通過Authorizer進行授權,通過SessionManager進行會話管理等。

SecurityManager是一個接口,繼承了Authenticator, Authorizer, SessionManager這三個接口。

3.Authenticator

Authenticator即認證器,對用戶身份進行認證,Authenticator是一個接口,shiro

提供ModularRealmAuthenticator實現類,通過ModularRealmAuthenticator基本上可以滿足大多數需求,也可以自定義認證器。

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學習筆記