1. 程式人生 > >Apache Shiro 快速入門教程

Apache Shiro 快速入門教程

第一部分 什麼是Apache Shiro



1、什麼是 apache shiro :


Apache Shiro是一個功能強大且易於使用的Java安全框架,提供了認證,授權,加密,和會話管理

如同 spring security 一樣都是是一個許可權安全框架,但是與Spring Security相比,在於他使用了和比較簡潔易懂的認證和授權方式。



2、Apache Shiro 的三大核心元件:


1、Subject :當前使用者的操作

2、SecurityManager:用於管理所有的Subject

3、Realms:用於進行許可權資訊的驗證


Subject:即當前使用者,在許可權管理的應用程式裡往往需要知道誰能夠操作什麼,誰擁有操作該程式的權利,shiro中則需要通過Subject來提供基礎的當前使用者資訊,Subject 不僅僅代表某個使用者,也可以是第三方程序、後臺帳戶(Daemon Account)或其他類似事物。

SecurityManager:即所有Subject的管理者,這是Shiro框架的核心元件,可以把他看做是一個Shiro框架的全域性管理元件,用於排程各種Shiro框架的服務。

Realms:Realms則是使用者的資訊認證器和使用者的許可權人證器,我們需要自己來實現Realms來自定義的管理我們自己系統內部的許可權規則。



3、Authentication 和 Authorization


在shiro的使用者許可權認證過程中其通過兩個方法來實現:

1、Authentication:是驗證使用者身份的過程。

2、Authorization:是授權訪問控制,用於對使用者進行的操作進行人證授權,證明該使用者是否允許進行當前操作,如訪問某個連結,某個資原始檔等。




4、其他元件:


除了以上幾個元件外,Shiro還有幾個其他元件:

1、SessionManager :Shiro為任何應用提供了一個會話程式設計正規化。

2、CacheManager :對Shiro的其他元件提供快取支援。 




5、Shiro 完整架構圖: 



圖片轉自:http://kdboy.iteye.com/blog/1154644



第二部分 Apache Shiro 整合Spring的Web程式構建


1、準備工具:


持久層框架:Hibernate4  這邊我使用了hibernate來對資料持久層進行操作

控制顯示層框架:SpringMVC 這邊我使用了SpringMVC實際開發中也可以是其他框架

資料庫MySQL

準備好所需要的jar放到專案中。

 


2、建立資料庫:



首先需要四張表,分別為 user(使用者)、role(角色)、permission(許可權)、userRole(使用者角色關係表)

這邊分別建立四張表的實體類,通過Hiberantehibernate.hbm2ddl.auto屬性的update 來自動生成資料表結構。


[java] view plain copy print ?
  1. /*** 
  2.  * 使用者表 
  3.  *  
  4.  * @author Swinglife 
  5.  *  
  6.  */  
  7. @Table(name = "t_user")  
  8. @Entity  
  9. public class User {  
  10.   
  11.     @Id  
  12.     @GeneratedValue(strategy = GenerationType.AUTO)  
  13.     Integer id;  
  14.     /** 使用者名稱 **/  
  15.     String username;  
  16.     /** 密碼 **/  
  17.     String password;  
  18.     /** 是否刪除 **/  
  19.     Integer isDelete;  
  20.     /** 建立時間 **/  
  21.     Date createDate;  
  22.     //多對多使用者許可權表  
  23.     @OneToMany(mappedBy = "user",cascade=CascadeType.ALL)  
  24.     List<UserRole> userRoles;  
  25.   
  26. 省略get set….  
  27.   
  28. }  

[java] view plain copy print ?
  1. /**** 
  2.  * 角色表 
  3.  *  
  4.  * @author Swinglife 
  5.  *  
  6.  */  
  7. @Entity  
  8. @Table(name = "t_role")  
  9. public class Role {  
  10.     @Id  
  11.     @GeneratedValue(strategy = GenerationType.AUTO)  
  12.     Integer id;  
  13.     /**角色名**/  
  14.     String name;  
  15.     /**角色說明**/  
  16.     String description;  
  17.   
  18.   
  19. }  

[java] view plain copy print ?
  1. /**** 
  2.  * 許可權表 
  3.  *  
  4.  * @author Swinglife 
  5.  *  
  6.  */  
  7. @Entity  
  8. @Table(name = "t_permission")  
  9. public class Permission {  
  10.   
  11.     @Id  
  12.     @GeneratedValue(strategy = GenerationType.AUTO)  
  13.     Integer id;  
  14.     /**token**/  
  15.     String token;  
  16.     /**資源url**/  
  17.     String url;  
  18.     /**許可權說明**/  
  19.     String description;  
  20.     /**所屬角色編號**/  
  21.     Integer roleId;  
  22.   
  23. }  

[java] view plain copy print ?
  1. /*** 
  2.  * 使用者角色表 
  3.  *  
  4.  * @author Swinglife 
  5.  *  
  6.  */  
  7. @Entity  
  8. @Table(name = "t_user_role")  
  9. public class UserRole {  
  10.   
  11.     @Id  
  12.     @GeneratedValue(strategy = GenerationType.AUTO)  
  13.     Integer id;  
  14.   
  15.     @ManyToOne(cascade = CascadeType.ALL)  
  16.     @JoinColumn(name = "userId", unique = true)  
  17.     User user;  
  18.     @ManyToOne  
  19.     @JoinColumn(name = "roleId", unique = true)  
  20.     Role role;  
  21.   
  22. }  

3、編寫操作使用者業務的Service:


[java] view plain copy print ?
  1. @Service  
  2. public class AccountService {  
  3.   
  4.     /**** 
  5.      * 通過使用者名稱獲取使用者物件 
  6.      *  
  7.      * @param username 
  8.      * @return 
  9.      */  
  10.     public User getUserByUserName(String username) {  
  11.         User user = (User) dao.findObjectByHQL("FROM User WHERE username = ?"new Object[] { username });  
  12.         return user;  
  13.     }  
  14.   
  15.     /*** 
  16.      * 通過使用者名稱獲取許可權資源 
  17.      *  
  18.      * @param username 
  19.      * @return 
  20.      */  
  21.     public List<String> getPermissionsByUserName(String username) {  
  22.         System.out.println("呼叫");  
  23.         User user = getUserByUserName(username);  
  24.         if (user == null) {  
  25.             return null;  
  26.         }  
  27.         List<String> list = new ArrayList<String>();  
  28.         // System.out.println(user.getUserRoles().get(0).get);  
  29.         for (UserRole userRole : user.getUserRoles()) {  
  30.             Role role = userRole.getRole();  
  31.             List<Permission> permissions = dao.findAllByHQL("FROM Permission WHERE roleId = ?"new Object[] { role.getId() });  
  32.             for (Permission p : permissions) {  
  33.                 list.add(p.getUrl());  
  34.             }  
  35.         }  
  36.         return list;  
  37.     }  
  38.   
  39.     // 公共的資料庫訪問介面  
  40.     // 這裡省略BaseDao dao的編寫  
  41.     @Autowired  
  42.     private BaseDao dao;  
  43. }  



4、編寫shiro元件自定義Realm:


[java] view plain copy print ?
  1. package org.swinglife.shiro;  
  2.   
  3. import java.util.List;  
  4.   
  5. import org.apache.shiro.authc.AuthenticationException;  
  6. import org.apache.shiro.authc.AuthenticationInfo;  
  7. import org.apache.shiro.authc.AuthenticationToken;  
  8. import org.apache.shiro.authc.SimpleAuthenticationInfo;  
  9. import org.apache.shiro.authc.UsernamePasswordToken;  
  10. import org.apache.shiro.authz.AuthorizationInfo;  
  11. import org.apache.shiro.authz.SimpleAuthorizationInfo;  
  12. import org.apache.shiro.realm.AuthorizingRealm;  
  13. import org.apache.shiro.subject.PrincipalCollection;  
  14. import org.swinglife.model.User;  
  15. import org.swinglife.service.AccountService;  
  16.   
  17. /**** 
  18.  * 自定義Realm 
  19.  *  
  20.  * @author Swinglife 
  21.  *  
  22.  */  
  23. public class MyShiroRealm extends AuthorizingRealm {  
  24.   
  25.     /*** 
  26.      * 獲取授權資訊 
  27.      */  
  28.     @Override  
  29.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {  
  30.         //根據自己系統規則的需要編寫獲取授權資訊,這裡為了快速入門只獲取了使用者對應角色的資源url資訊  
  31.         String username = (String) pc.fromRealm(getName()).iterator().next();  
  32.         if (username != null) {  
  33.             List<String> pers = accountService.getPermissionsByUserName(username);  
  34.             if (pers != null && !pers.isEmpty()) {  
  35.                 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
  36.                 for (String each : pers) {  
  37.                     //將許可權資源新增到使用者資訊中  
  38.                     info.addStringPermission(each);  
  39.                 }  
  40.                 return info;  
  41.             }  
  42.         }  
  43.         return null;  
  44.     }  
  45.     /*** 
  46.      * 獲取認證資訊 
  47.      */  
  48.     @Override  
  49.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken at) throws AuthenticationException {  
  50.         UsernamePasswordToken token = (UsernamePasswordToken) at;  
  51.         // 通過表單接收的使用者名稱  
  52.         String username = token.getUsername();  
  53.         if (username != null && !"".equals(username)) {  
  54.             User user = accountService.getUserByUserName(username);  
  55.             if (user != null) {  
  56.                 return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());  
  57.             }  
  58.         }  
  59.   
  60.         return null;  
  61.     }  
  62.       
  63.     /**使用者的業務類**/  
  64.     private AccountService accountService;  
  65.       
  66.     public AccountService getAccountService() {  
  67.         return accountService;  
  68.     }  
  69.   
  70.     public void setAccountService(AccountService accountService) {  
  71.         this.accountService = accountService;  
  72.     }  
  73.   
  74. }  

上述類繼承了Shiro的AuthorizingRealm類 實現了AuthorizationInfoAuthenticationInfo兩個方法,用於獲取使用者許可權和認證使用者登入資訊


5、編寫LoginController:



[java] view plain copy print ?
  1. package org.swinglife.controller;  
  2.   
  3. import org.apache.shiro.SecurityUtils;  
  4. import org.apache.shiro.authc.UsernamePasswordToken;  
  5. import org.apache.shiro.subject.Subject;  
  6. import org.springframework.beans.factory.annotation.Autowired;  
  7. import org.springframework.stereotype.Controller;  
  8. import org.springframework.web.bind.annotation.RequestMapping;  
  9. import org.springframework.web.bind.annotation.RequestMethod;  
  10. import org.springframework.web.portlet.ModelAndView;  
  11. import org.swinglife.model.User;  
  12. import org.swinglife.service.AccountService;  
  13.   
  14. /**** 
  15.  * 使用者登入Controller 
  16.  *  
  17.  * @author Swinglife 
  18.  *  
  19.  */  
  20. @Controller  
  21. public class LoginController {  
  22.   
  23.     /*** 
  24.      * 跳轉到登入頁面 
  25.      *  
  26.      * @return 
  27.      */  
  28.     @RequestMapping(value = "toLogin")  
  29.     public String toLogin() {  
  30.         // 跳轉到/page/login.jsp頁面  
  31.         return "login";  
  32.     }  
  33.   
  34.     /*** 
  35.      * 實現使用者登入 
  36.      *  
  37.      * @param username 
  38.      * @param password 
  39.      * @return 
  40.      */  
  41.     @RequestMapping(value = "login")  
  42.     public ModelAndView Login(String username, String password) {  
  43.         ModelAndView mav = new ModelAndView();  
  44.         User user = accountService.getUserByUserName(username);  
  45.         if (user == null) {  
  46.             mav.setView("toLogin");  
  47.             mav.addObject("msg""使用者不存在");  
  48.             return mav;  
  49.         }  
  50.         if (!user.getPassword().equals(password)) {  
  51.             mav.setView("toLogin");  
  52.             mav.addObject("msg""賬號密碼錯誤");  
  53.             return mav;  
  54.         }  
  55.         SecurityUtils.getSecurityManager().logout(SecurityUtils.getSubject());  
  56.         // 登入後存放進shiro token  
  57.         UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());  
  58.         Subject subject = SecurityUtils.getSubject();  
  59.         subject.login(token);  
  60.         // 登入成功後會跳轉到successUrl配置的連結,不用管下面返回的連結。  
  61.         mav.setView("redirect:/home");  
  62.         return mav;  
  63.     }  
  64.   
  65.     // 處理使用者業務類  
  66.     @Autowired  
  67.     private AccountService accountService;  
  68. }  


6、編寫資訊認證成功後的跳轉頁面:


[java] view plain copy print ?
  1. package org.swinglife.controller;  
  2.   
  3. import org.springframework.stereotype.Controller;  
  4. import org.springframework.web.bind.annotation.RequestMapping;  
  5.   
  6. @Controller  
  7. public class IndexController {  
  8.   
  9.     @RequestMapping("home")  
  10.     public String index() {  
  11.         System.out.println("登入成功");  
  12.         return "home";  
  13.     }  
  14. }  


7、Shiro的配置檔案.xml


[java] view plain copy print ?
  1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
  2.         <property name="securityManager" ref="securityManager" />  
  3.         <property name="loginUrl" value="/toLogin" />  
  4.         <property name="successUrl" value="/home" />  
  5.         <property name="unauthorizedUrl" value="/403" />  
  6.            
  7.         <property name="filterChainDefinitions">  
  8.             <value>  
  9.                 /toLogin = authc <!-- authc 表示需要認證才能訪問的頁面 -->  
  10.                 /home = authc, perms[/home]  <!-- perms 表示需要該許可權才能訪問的頁面 -->  
  11.             </value>  
  12.         </property>  
  13.     </bean>  
  14.   
  15.   
  16.   
  17.   
  18.     <bean id="myShiroRealm" class="org.swinglife.shiro.MyShiroRealm">  
  19.         <property name="accountService" ref="accountService" />  
  20.     </bean>  
  21.   
  22.     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
  23.         <property name="realm" ref="myShiroRealm"></property>  
  24.     </bean>  
  25.   
  26.     <bean id="accountService" class="org.swinglife.service.AccountService"></bean>  
  27.   
  28.     <!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">   
  29.         <property name="cacheManager" ref="cacheManager" /> </bean> -->  



loginUrl 用於配置登陸頁

successUrl 用於配置登入成功後返回的頁面,不過該引數只會在當登入頁面中並沒有任何返回頁面時才會生效,否則會跳轉到登入Controller中的指定頁面。

unauthorizedUrl 用於配置沒有許可權訪問頁面時跳轉的頁面


filterChainDefinitions:apache shiro通過filterChainDefinitions引數來分配連結的過濾,資源過濾有常用的以下幾個引數:

1、authc 表示需要認證的連結

2、perms[/url] 表示該連結需要擁有對應的資源/許可權才能訪問

3、roles[admin] 表示需要對應的角色才能訪問

4、perms[admin:url] 表示需要對應角色的資源才能訪問


8、登陸頁login.jsp

[html] view plain copy print ?
  1. <body>  
  2.   
  3. <h1>user login</h1>  
  4. <form action="login" method="post">  
  5. username:<input type="text" name="username"><p>  
  6. password:<input type="password" name="password">  
  7. <p>  
  8. ${msg }  
  9. <input type="submit" value="submit">  
  10. </form>  
  11. </body>  




9、執行程式

在資料庫中新增一條使用者、角色、以及許可權資料,並且在關聯表中新增一條關聯資料:








在瀏覽器中訪問: home頁面 就會跳轉到登入頁面:  



轉載出處:

最後輸入 賬號密碼 就會跳轉

shiro jar:http://download.csdn.net/detail/swingpyzf/8766673

專案原始碼:github:https://github.com/swinglife/shiro_ex