spring boot 框架spring date jpa整合shiro

DreamWeaver_Zhou發表於2018-01-12

本來想寫一篇spring boot整合Shiro實現許可權驗證的文章,發現這篇寫的非常不錯,就直接借鑑了!


(1). Shiro簡單介紹

Shiro是Apache下的一個開源專案,我們稱之為Apache Shiro。它是一個很易用與Java專案的的安全框架,提供了認證、授權、加密、會話管理,與spring Security 一樣都是做一個許可權的安全框架,但是與Spring Security 相比,在於 Shiro 使用了比較簡單易懂易於使用的授權方式。
Apache Shiro 的三大核心元件


- Subject 當前使用者操作 
- SecurityManager 用於管理所有的Subject 
- Realms 用於進行許可權資訊的驗證,也是我們需要自己實現的。
我們需要實現Realms的Authentication 和 Authorization。其中 Authentication 是用來驗證使用者身份,Authorization 是授權訪問控制,用於對使用者進行的操作授權,證明該使用者是否允許進行當前操作,如訪問某個連結,某個資原始檔等。

Apache Shiro 核心通過 Filter 來實現,就好像SpringMvc 通過DispachServlet 來主控制一樣。 
既然是使用 Filter 一般也就能猜到,是通過URL規則來進行過濾和許可權校驗,所以我們需要定義一系列關於URL的規則和訪問許可權。
另外我們可以通過Shiro 提供的會話管理來獲取Session中的資訊。Shiro 也提供了快取支援,使用 CacheManager 來管理。

官方網站:http://shiro.apache.org/ 

完整架構圖:



Shiro是很強大的一個安全框架,這裡只是拋裝引玉下,還有很多的需要大家自己去學習Shiro。

(2). 整合Shiro核心分析
      整合Shiro的話,我們需要知道Shiro框架大概的一些管理物件。
第一:ShiroFilterFactory,Shiro過濾器工廠類,具體的實現類是:ShiroFilterFactoryBean,此實現類是依賴於SecurityManager安全管理器。
第二:SecurityManager,Shiro的安全管理,主要是身份認證的管理,快取管理,cookie管理,所以在實際開發中我們主要是和SecurityManager進行打交道的,ShiroFilterFactory主要配置好了Filter就可以了。當然SecurityManager並進行身份認證快取的實現,我們需要進行對應的編碼然後進行注入到安全管理器中。
第三:Realm,用於身份資訊許可權資訊的驗證。
第四:其它的就是快取管理,記住登入之類的,這些大部分都是需要自己進行簡單的實現,然後注入到SecurityManager讓Shiro的安全管理器進行管理就好了。

(3). 無Shiro的Spring Boot
      我們先編寫一個無Shiro的簡單的框架,在這個框架中我們可以訪問到index,login,userInfo,userInfoAdd。
      這個步驟對於有Spring Boot基礎的就應該很簡單了,在這裡簡單的介紹下:
(a) 新建一個maven Java project,取名為spring-boot-shiro1
(b) 在pom.xml中引入基本依賴,在這裡還沒有引入shiro等的依賴:

[html] view plain copy
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  3.     <modelVersion>4.0.0</modelVersion>  
  4.     <groupId>com.example</groupId>  
  5.     <artifactId>spring-boot-shiro1</artifactId>  
  6.     <version>0.0.1-SNAPSHOT</version>  
  7.     <packaging>jar</packaging>  
  8.   
  9.   
  10.     <properties>  
  11.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  12.     </properties>  
  13.   
  14.   
  15.     <!-- Inherit defaults from Spring Boot -->  
  16.     <parent>  
  17.         <groupId>org.springframework.boot</groupId>  
  18.         <artifactId>spring-boot-starter-parent</artifactId>  
  19.         <version>1.4.0.RELEASE</version>  
  20.     </parent>  
  21.   
  22.   
  23.     <dependencies>  
  24.         <!-- spring boot web支援:mvc,aop... -->  
  25.         <dependency>  
  26.             <groupId>org.springframework.boot</groupId>  
  27.             <artifactId>spring-boot-starter-web</artifactId>  
  28.         </dependency>  
  29.         <!-- thmleaf模板依賴. -->  
  30.         <dependency>  
  31.             <groupId>org.springframework.boot</groupId>  
  32.             <artifactId>spring-boot-starter-thymeleaf</artifactId>  
  33.         </dependency>  
  34.          
  35.        <!-- 熱部署 -->  
  36.         <dependency>  
  37.             <groupId>org.springframework.boot</groupId>  
  38.             <artifactId>spring-boot-devtools</artifactId>  
  39.             <optional>true</optional>  
  40.         </dependency>  
  41.   
  42.     </dependencies>  
  43. </project>  

(c) 編寫網頁檔案:
index.html,login.html,userInfo.html,userInfoAdd.html
這個檔案存在在src/main/resouces/templates, 這幾個檔案中都是簡單的程式碼,只有登入介面中有賬號和密碼:

index.html

[html] view plain copy
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4. <meta charset="UTF-8" />  
  5. <title>Insert title here</title>  
  6. </head>  
  7. <body>  
  8.     <h3>index</h3>  
  9. </body>  
  10. </html>  

login.html

[html] view plain copy
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4. <meta charset="UTF-8" />  
  5. <title>Insert title here</title>  
  6. </head>  
  7. <body>  
  8.             錯誤資訊:<h4 th:text="${msg}"></h4>  
  9.        <form action="" method="post">  
  10.            <p>賬號:<input type="text" name="username" value="admin"/></p>  
  11.            <p>密碼:<input type="text" name="password" value="123456"/></p>  
  12.            <p><input type="submit" value="登入"/></p>  
  13.        </form>  
  14. </body>  
  15. </html>  
userInfo.html,

[html] view plain copy
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4. <meta charset="UTF-8" />  
  5. <title>Insert title here</title>  
  6. </head>  
  7. <body>  
  8.     <h3>使用者查詢介面</h3>  
  9. </body>  
  10. </html>  
userInfoAdd.html

<h3>使用者新增介面</h3>

(d)編寫啟動類

[html] view plain copy
  1. package com.example;  
  2.   
  3. import org.springframework.boot.SpringApplication;  
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;  
  5.   
  6. @SpringBootApplication  
  7. public class Application {  
  8.      public static void main(String[] args) {  
  9.         SpringApplication.run(Application.class, args);  
  10.     }  
  11. }  
(e)編寫HomeController類
新建HomeController類

[html] view plain copy
  1. package com.example.controller;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import javax.servlet.http.HttpServletRequest;  
  6.   
  7. import org.apache.shiro.authc.IncorrectCredentialsException;  
  8. import org.apache.shiro.authc.UnknownAccountException;  
  9. import org.springframework.stereotype.Controller;  
  10. import org.springframework.web.bind.annotation.RequestMapping;  
  11. import org.springframework.web.bind.annotation.RequestMethod;  
  12.   
  13. @Controller  
  14. public class HomeController {  
  15.     @RequestMapping({ "/", "index" })  
  16.     public String index() {  
  17.         return "/index";  
  18.     }  
  19.   
  20.     @RequestMapping(value = "/login"method = RequestMethod.GET)  
  21.     public String login() {  
  22.         return "/login";  
  23.     }  
  24. }  

整合shiro大概分這麼一個步驟:
(a) pom.xml中新增Shiro依賴;
(b) 注入Shiro Factory和SecurityManager。
(c) 身份認證
(d) 許可權控制

(a) pom.xml中新增Shiro依賴;

要使用Shiro進行許可權控制,那麼很明顯的就需要新增對Shiro的依賴包,在pom.xml中加入如下配置:

[html] view plain copy
  1. <!-- shiro spring. -->  
  2.        <dependency>  
  3.            <groupId>org.apache.shiro</groupId>  
  4.            <artifactId>shiro-spring</artifactId>  
  5.            <version>1.2.3</version>  
  6.        </dependency>  
(b) 注入Shiro Factory和SecurityManager。
      在Spring中注入類都是使用配置檔案的方式,在Spring Boot中是使用註解的方式,那麼應該如何進行實現呢?
      Shiro幾個核心的類,第一就是ShiroFilterFactory,第二就是SecurityManager,那麼最簡單的配置就是注入這兩個類就ok了,那麼如何注入呢?看如下程式碼:

      新建ShiroConfiguration.java

     

[html] view plain copy
  1. package com.example.config.shiro;  
  2.   
  3. import java.util.LinkedHashMap;  
  4. import java.util.Map;  
  5.   
  6. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;  
  7. import org.apache.shiro.mgt.SecurityManager;  
  8. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;  
  9. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;  
  10. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;  
  11. import org.springframework.context.annotation.Bean;  
  12. import org.springframework.context.annotation.Configuration;  
  13.   
  14. @Configuration  
  15. public class ShiroConfiguration {  
  16.   
  17.     @Bean  
  18.     public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {  
  19.         System.out.println("ShiroConfiguration.shiroFilter()");  
  20.         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();  
  21.   
  22.         // 必須設定SecuritManager  
  23.         shiroFilterFactoryBean.setSecurityManager(securityManager);  
  24.   
  25.         // 攔截器  
  26.         Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();  
  27.   
  28.         // 配置退出過濾器,其中的具體程式碼Shiro已經替我們實現了  
  29.         filterChainDefinitionMap.put("/logout", "logout");  
  30.   
  31.         // <!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->:這是一個坑呢,一不小心程式碼就不好使了;  
  32.         // <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->  
  33.   
  34.         filterChainDefinitionMap.put("/**", "authc");  
  35.   
  36.         // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面  
  37.         shiroFilterFactoryBean.setLoginUrl("/login");  
  38.         // 登入成功後要跳轉的連結  
  39.         shiroFilterFactoryBean.setSuccessUrl("/index");  
  40.         // 未授權介面;  
  41.         shiroFilterFactoryBean.setUnauthorizedUrl("/403");  
  42.   
  43.         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);  
  44.         return shiroFilterFactoryBean;  
  45.   
  46.     }  
  47.   
  48.     @Bean  
  49.     public SecurityManager securityManager() {  
  50.         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();  
  51.           
  52.         return securityManager;  
  53.     }  
  54.   
  55.       
  56.   
  57. }  

   這裡說下:ShiroFilterFactory中已經由Shiro官方實現的過濾器:
Shiro內建的FilterChain


Filter Name

Class

anon

org.apache.shiro.web.filter.authc.AnonymousFilter

authc

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms

org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port

org.apache.shiro.web.filter.authz.PortFilter

rest

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

org.apache.shiro.web.filter.authz.SslFilter

user

org.apache.shiro.web.filter.authc.UserFilter



anon:所有url都都可以匿名訪問;
authc: 需要認證才能進行訪問;
user:配置記住我或認證通過可以訪問;
這幾個是我們會用到的,在這裡說明下,其它的請自行查詢文件進行學習。
這時候我們執行程式,訪問/index頁面我們會發現自動跳轉到了login頁面,當然這個時候輸入賬號和密碼是無法進行訪問的。下面這才是重點:任何身份認證,如何許可權控制。
(c) 身份認證
      在認證、授權內部實現機制中都有提到,最終處理都將交給Real進行處理。因為在Shiro中,最終是通過Realm來獲取應用程式中的使用者、角色及許可權資訊的。通常情況下,在Realm中會直接從我們的資料來源中獲取Shiro需要的驗證資訊。可以說,Realm是專用於安全框架的DAO.
認證實現
Shiro的認證過程最終會交由Realm執行,這時會呼叫Realm的getAuthenticationInfo(token)方法。
該方法主要執行以下操作:
1、檢查提交的進行認證的令牌資訊
2、根據令牌資訊從資料來源(通常為資料庫)中獲取使用者資訊
3、對使用者資訊進行匹配驗證。
4、驗證通過將返回一個封裝了使用者資訊的AuthenticationInfo例項。
5、驗證失敗則丟擲AuthenticationException異常資訊。
而在我們的應用程式中要做的就是自定義一個Realm類,繼承AuthorizingRealm抽象類,過載doGetAuthenticationInfo (),重寫獲取使用者資訊的方法。
既然需要進行身份許可權控制,那麼少不了建立使用者實體類,許可權實體類。
      在許可權管理系統中,有這麼幾個角色很重要,這個要是不清楚的話,那麼就很難理解,我們為什麼這麼編碼了。第一是使用者表:在使用者表中儲存了使用者的基本資訊,賬號、密碼、姓名,性別等;第二是:許可權表(資源+控制許可權):這個表中主要是儲存了使用者的URL地址,許可權資訊;第三就是角色表:在這個表重要儲存了系統存在的角色;第四就是關聯表:使用者-角色管理表(使用者在系統中都有什麼角色,比如admin,vip等),角色-許可權關聯表(每個角色都有什麼許可權可以進行操作)。依據這個理論,我們進行來進行編碼,很明顯的我們第一步就是要進行實體類的建立。在這裡我們使用Mysql和JPA進行運算元據庫。
那麼我們先在pom.xml中引入mysql和JPA的依賴:

UserInfo.java、SysRole.java、SysPermission.java至於之前的關聯表我們使用JPA進行自動生成。

[html] view plain copy
  1. <!-- Spirng data JPA依賴; -->  
  2.        <dependency>  
  3.            <groupId>org.springframework.boot</groupId>  
  4.            <artifactId>spring-boot-starter-data-jpa</artifactId>  
  5.        </dependency>  
  6.         
  7.        <!-- mysql驅動; -->  
  8.        <dependency>  
  9.            <groupId>mysql</groupId>  
  10.            <artifactId>mysql-connector-java</artifactId>  
  11.        </dependency>  
配置src/main/resouces/application.properties配置資料庫和jpa(application.properties新建一個即可):

[html] view plain copy
  1. ########################################################  
  2. ###datasource  
  3. ########################################################  
  4. spring.datasource.url = jdbc:mysql://localhost:3306/test  
  5. spring.datasource.username = root  
  6. spring.datasource.password = root  
  7. spring.datasource.driverClassName = com.mysql.jdbc.Driver  
  8. spring.datasource.max-active=20  
  9. spring.datasource.max-idle=8  
  10. spring.datasource.min-idle=8  
  11. spring.datasource.initial-size=10  
  12.    
  13.    
  14.    
  15. ########################################################  
  16. ### Java Persistence Api  
  17. ########################################################  
  18. # Specify the DBMS  
  19. spring.jpa.database = MYSQL  
  20. # Show or not log for each sql query  
  21. spring.jpa.show-sql = true  
  22. # Hibernate ddl auto (create, create-drop, update)  
  23. spring.jpa.hibernate.ddl-auto = update  
  24. # Naming strategy  
  25. #[org.hibernate.cfg.ImprovedNamingStrategy | org.hibernate.cfg.DefaultNamingStrategy]  
  26. spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.DefaultNamingStrategy  
  27. # stripped before adding them to the entity manager)  
  28. spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect  
準備工作準備好之後,那麼就可以編寫實體類了:

UserInfo.java

[html] view plain copy
  1. package com.example.domain;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.List;  
  5.   
  6. import javax.persistence.Column;  
  7. import javax.persistence.Entity;  
  8. import javax.persistence.FetchType;  
  9. import javax.persistence.GeneratedValue;  
  10. import javax.persistence.Id;  
  11. import javax.persistence.JoinColumn;  
  12. import javax.persistence.JoinTable;  
  13. import javax.persistence.ManyToMany;  
  14.   
  15. /**  
  16.  * 使用者資訊.  
  17.  * @author Administrator  
  18.  *  
  19.  */  
  20. @Entity  
  21. public class UserInfo implements Serializable {  
  22.   
  23.     /**  
  24.      *   
  25.      */  
  26.     private static final long serialVersionUID = 1L;  
  27.   
  28.     @Id  
  29.     @GeneratedValue  
  30.     private long uid;// 使用者id  
  31.   
  32.     @Column(unique = true)  
  33.     private String username;// 帳號  
  34.   
  35.     private String name;// 名稱(暱稱或者真實姓名,不同系統不同定義)  
  36.   
  37.     private String password; // 密碼;  
  38.     private String salt;// 加密密碼的鹽  
  39.   
  40.     private byte state;// 使用者狀態,0:建立未認證(比如沒有啟用,沒有輸入驗證碼等等)--等待驗證的使用者 ,  
  41.                         // 1:正常狀態,2:使用者被鎖定.  
  42.   
  43.     @ManyToMany(fetch = FetchType.EAGER) // 立即從資料庫中進行載入資料  
  44.     @JoinTable(name = "SysUserRole"joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns = {  
  45.             @JoinColumn(name = "roleId") })  
  46.     private List<SysRole> roleList;// 一個使用者具有多個角色  
  47.   
  48.     public long getUid() {  
  49.         return uid;  
  50.     }  
  51.   
  52.     public void setUid(long uid) {  
  53.         this.uid = uid;  
  54.     }  
  55.   
  56.     public String getUsername() {  
  57.         return username;  
  58.     }  
  59.   
  60.     public void setUsername(String username) {  
  61.         this.username = username;  
  62.     }  
  63.   
  64.     public String getName() {  
  65.         return name;  
  66.     }  
  67.   
  68.     public void setName(String name) {  
  69.         this.name = name;  
  70.     }  
  71.   
  72.     public String getPassword() {  
  73.         return password;  
  74.     }  
  75.   
  76.     public void setPassword(String password) {  
  77.         this.password = password;  
  78.     }  
  79.   
  80.     public String getSalt() {  
  81.         return salt;  
  82.     }  
  83.   
  84.     public void setSalt(String salt) {  
  85.         this.salt = salt;  
  86.     }  
  87.   
  88.     public byte getState() {  
  89.         return state;  
  90.     }  
  91.   
  92.     public void setState(byte state) {  
  93.         this.state = state;  
  94.     }  
  95.   
  96.     public List<SysRole> getRoleList() {  
  97.         return roleList;  
  98.     }  
  99.   
  100.     public void setRoleList(List<SysRole> roleList) {  
  101.         this.roleList = roleList;  
  102.     }  
  103.   
  104.     /**  
  105.      * 密碼鹽.  
  106.      *   
  107.      * @return  
  108.      */  
  109.     public String getCredentialsSalt() {  
  110.         return this.username + this.salt;  
  111.     }  
  112.   
  113.     @Override  
  114.     public String toString() {  
  115.         return "UserInfo [uid=" + uid + "username=" + username + "name=" + name + "password=" + password  
  116.                 + ", salt=" + salt + "state=" + state + "]";  
  117.     }  
  118.   
  119. }  

在這裡salt主要是用來進行密碼加密的,當然也可以使用明文進行編碼測試,實際開發中還是建議密碼進行加密。
getCredentialsSalt()
這個方法重新對鹽重新進行了定義,使用者名稱+salt,這樣就更加不容易被破解了

SysRole.java

[html] view plain copy
  1. package com.example.domain;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.List;  
  5.   
  6. import javax.persistence.Entity;  
  7. import javax.persistence.FetchType;  
  8. import javax.persistence.GeneratedValue;  
  9. import javax.persistence.Id;  
  10. import javax.persistence.JoinColumn;  
  11. import javax.persistence.JoinTable;  
  12. import javax.persistence.ManyToMany;  
  13.   
  14. /**  
  15.  * 系統角色實體類;  
  16.  *   
  17.  * @author Administrator  
  18.  *  
  19.  */  
  20. @Entity  
  21. public class SysRole implements Serializable {  
  22.     private static final long serialVersionUID = 1L;  
  23.     @Id  
  24.     @GeneratedValue  
  25.     private Long id; // 編號  
  26.     private String role; // 角色標識程式中判斷使用,如"admin",這個是唯一的:  
  27.     private String description; // 角色描述,UI介面顯示使用  
  28.     private Boolean available = Boolean.FALSE; // 是否可用,如果不可用將不會新增給使用者  
  29.   
  30.     // 角色 -- 許可權關係:多對多關係;  
  31.     @ManyToMany(fetch = FetchType.EAGER)  
  32.     @JoinTable(name = "SysRolePermission"joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = {  
  33.             @JoinColumn(name = "permissionId") })  
  34.     private List<SysPermission> permissions;  
  35.   
  36.     // 使用者 - 角色關係定義;  
  37.     @ManyToMany  
  38.     @JoinTable(name = "SysUserRole"joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = {  
  39.             @JoinColumn(name = "uid") })  
  40.     private List<UserInfo> userInfos;// 一個角色對應多個使用者  
  41.   
  42.     public List<UserInfo> getUserInfos() {  
  43.         return userInfos;  
  44.     }  
  45.   
  46.     public void setUserInfos(List<UserInfo> userInfos) {  
  47.         this.userInfos = userInfos;  
  48.     }  
  49.   
  50.     public Long getId() {  
  51.         return id;  
  52.     }  
  53.   
  54.     public void setId(Long id) {  
  55.         this.id = id;  
  56.     }  
  57.   
  58.     public String getRole() {  
  59.         return role;  
  60.     }  
  61.   
  62.     public void setRole(String role) {  
  63.         this.role = role;  
  64.     }  
  65.   
  66.     public String getDescription() {  
  67.         return description;  
  68.     }  
  69.   
  70.     public void setDescription(String description) {  
  71.         this.description = description;  
  72.     }  
  73.   
  74.     public Boolean getAvailable() {  
  75.         return available;  
  76.     }  
  77.   
  78.     public void setAvailable(Boolean available) {  
  79.         this.available = available;  
  80.     }  
  81.   
  82.     public List<SysPermission> getPermissions() {  
  83.         return permissions;  
  84.     }  
  85.   
  86.     public void setPermissions(List<SysPermission> permissions) {  
  87.         this.permissions = permissions;  
  88.     }  
  89.   
  90.     @Override  
  91.     public String toString() {  
  92.         return "SysRole [id=" + id + "role=" + role + "description=" + description + "available=" + available  
  93.                 + ", permissions=" + permissions + "]";  
  94.     }  
  95. }  

SysPermission.java

[html] view plain copy
  1. package com.example.domain;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.List;  
  5.   
  6. import javax.persistence.Column;  
  7. import javax.persistence.Entity;  
  8. import javax.persistence.FetchType;  
  9. import javax.persistence.GeneratedValue;  
  10. import javax.persistence.Id;  
  11. import javax.persistence.JoinColumn;  
  12. import javax.persistence.JoinTable;  
  13. import javax.persistence.ManyToMany;  
  14.   
  15. /**  
  16.  * 許可權實體類;  
  17.  *   
  18.  */  
  19. @Entity  
  20. public class SysPermission implements Serializable {  
  21.     private static final long serialVersionUID = 1L;  
  22.   
  23.     @Id  
  24.     @GeneratedValue  
  25.     private long id;// 主鍵.  
  26.     private String name;// 名稱.  
  27.   
  28.     @Column(columnDefinition = "enum('menu','button')")  
  29.     private String resourceType;// 資源型別,[menu|button]  
  30.     private String url;// 資源路徑.  
  31.     private String permission; // 許可權字串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view  
  32.     private Long parentId; // 父編號  
  33.     private String parentIds; // 父編號列表  
  34.     private Boolean available = Boolean.FALSE;  
  35.   
  36. //  @ManyToMany(fetch = FetchType.LAZY)  
  37. //  @JoinTable(name = "SysRolePermission"joinColumns = { @JoinColumn(name = "permissionId") }, inverseJoinColumns = {  
  38. //          @JoinColumn(name = "roleId") })  
  39. //  private List<SysRole> roles;  
  40.   
  41.     public long getId() {  
  42.         return id;  
  43.     }  
  44.   
  45.     public void setId(long id) {  
  46.         this.id = id;  
  47.     }  
  48.   
  49.     public String getName() {  
  50.         return name;  
  51.     }  
  52.   
  53.     public void setName(String name) {  
  54.         this.name = name;  
  55.     }  
  56.   
  57.     public String getResourceType() {  
  58.         return resourceType;  
  59.     }  
  60.   
  61.     public void setResourceType(String resourceType) {  
  62.         this.resourceType = resourceType;  
  63.     }  
  64.   
  65.     public String getUrl() {  
  66.         return url;  
  67.     }  
  68.   
  69.     public void setUrl(String url) {  
  70.         this.url = url;  
  71.     }  
  72.   
  73.     public String getPermission() {  
  74.         return permission;  
  75.     }  
  76.   
  77.     public void setPermission(String permission) {  
  78.         this.permission = permission;  
  79.     }  
  80.   
  81.     public Long getParentId() {  
  82.         return parentId;  
  83.     }  
  84.   
  85.     public void setParentId(Long parentId) {  
  86.         this.parentId = parentId;  
  87.     }  
  88.   
  89.     public String getParentIds() {  
  90.         return parentIds;  
  91.     }  
  92.   
  93.     public void setParentIds(String parentIds) {  
  94.         this.parentIds = parentIds;  
  95.     }  
  96.   
  97.     public Boolean getAvailable() {  
  98.         return available;  
  99.     }  
  100.   
  101.     public void setAvailable(Boolean available) {  
  102.         this.available = available;  
  103.     }  
  104.   
  105. //  public List<SysRole> getRoles() {  
  106. //      return roles;  
  107. //  }  
  108. //  
  109. //  public void setRoles(List<SysRole> roles) {  
  110. //      this.roles = roles;  
  111. //  }  
  112.   
  113.     @Override  
  114.     public String toString() {  
  115.         return "SysPermission [id=" + id + "name=" + name + "resourceType=" + resourceType + "url=" + url  
  116.                 + ", permission=" + permission + "parentId=" + parentId + "parentIds=" + parentIds + "available="  
  117.                 + available + "]";  
  118.     }  
  119.   
  120. }  
ok,到這裡實體類就編碼完畢了,這時候執行Application,就會自動建表

MySQL> show tables;
+---------------------+
| Tables_in_test      |
+---------------------+
| sys_permission      |
| sys_role            |
| sys_role_permission |
| sys_user_role       |
| user_info           |
+---------------------+
5 rows in set (0.08 sec)

mysql>

sql

[html] view plain copy
  1. INSERT INTO `sys_permission`(name,parent_id,parent_ids,available,permission,resource_type,url)   
  2. VALUES ('使用者管理',0,'0/' ,1,'userInfo:view', 'menu', 'userInfo/userList');  
  3.   
  4. INSERT INTO `sys_permission`(name,parent_id,parent_ids,available,permission,resource_type,url)   
  5. VALUES ('使用者新增',1,'0/1',1,'userInfo:add', 'button', 'userInfo/userAdd');  
  6.   
  7. INSERT INTO `sys_permission`(name,parent_id,parent_ids,available,permission,resource_type,url)   
  8. VALUES ('使用者刪除',1,'0/1',1,'userInfo:del', 'button', 'userInfo/userDel');  
  9.   
  10. INSERT INTO `sys_role`(available,description,role) VALUES (1,'管理員','admin');  
  11. INSERT INTO `sys_role`(available,description,role) VALUES (1,'VIP會員','vip');  
  12.   
  13. INSERT INTO `sys_role_permission`(permission_id,role_id) VALUES ('1', '1');  
  14. INSERT INTO `sys_role_permission`(permission_id,role_id) VALUES ('2', '1');  
  15.   
  16. INSERT INTO `user_info`(name,password,salt,state,username) VALUES ('管理員', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', '0', 'admin');  
  17.   
  18. INSERT INTO `sys_user_role`(uid,role_id) VALUES (1,1);  
  19. INSERT INTO `sys_user_role`(uid,role_id) VALUES (1,2);  

這時候資料都準備完畢了,那麼接下來就應該編寫Repository進行訪問資料了

[html] view plain copy
  1. package com.example.repository;  
  2.   
  3. import org.springframework.data.repository.CrudRepository;  
  4.   
  5. import com.example.domain.UserInfo;  
  6.   
  7. /**  
  8.  * UserInfo持久化類  
  9.  *   
  10.  * @author Administrator  
  11.  *  
  12.  */  
  13. public interface UserInfoRepository extends CrudRepository<UserInfo, Long> {  
  14.     /** 通過username查詢使用者資訊 **/  
  15.     public UserInfo findByUsername(String username);  
  16.   
  17. }  
在這裡你會發現我們只編寫了UserInfo的資料庫操作,那麼我們怎麼獲取我們的許可權資訊了,通過userInfo.getRoleList()可以獲取到對應的角色資訊,然後在通過對應的角色可以獲取到許可權資訊,當然這些都是JPA幫我們實現了,我們也可以進行直接獲取到許可權資訊,只要寫一個關聯查詢然後過濾掉重複的許可權即可,這裡不進行實現。
編寫一個業務處理類UserInfoService>

[html] view plain copy
  1. package com.example.service;  
  2.   
  3. import com.example.domain.UserInfo;  
  4.   
  5. public interface UserInfoService {  
  6.   
  7.     public UserInfo findByUsername(String username);  
  8.   
  9. }  
[html] view plain copy
  1. package com.example.service.impl;  
  2.   
  3. import javax.annotation.Resource;  
  4.   
  5. import org.springframework.stereotype.Service;  
  6. import org.springframework.transaction.annotation.Transactional;  
  7.   
  8. import com.example.domain.UserInfo;  
  9. import com.example.repository.UserInfoRepository;  
  10. import com.example.service.UserInfoService;  
  11.   
  12. @Service  
  13. public class UserInfoServiceImpl implements UserInfoService{  
  14.     @Resource  
  15.     private UserInfoRepository userInfoRepository;  
  16.   
  17.     @Transactional(readOnly=true)  
  18.     @Override  
  19.     public UserInfo findByUsername(String username) {  
  20.         System.out.println("UserInfoServiceImpl.findByUsername()");  
  21.         return userInfoRepository.findByUsername(username);  
  22.     }  
  23.   
  24. }  
基本工作準備好之後,剩下的才是重點,shiro的認證最終是交給了Realm進行執行了,所以我們需要自己重新實現一個Realm,此Realm繼承AuthorizingRealm。

[html] view plain copy
  1. package com.example.config.shiro;  
  2.   
  3. import javax.annotation.Resource;  
  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.authz.AuthorizationInfo;  
  10. import org.apache.shiro.authz.SimpleAuthorizationInfo;  
  11. import org.apache.shiro.realm.AuthorizingRealm;  
  12. import org.apache.shiro.subject.PrincipalCollection;  
  13. import org.apache.shiro.util.ByteSource;  
  14. import org.springframework.context.annotation.Bean;  
  15.   
  16. import com.example.domain.SysPermission;  
  17. import com.example.domain.SysRole;  
  18. import com.example.domain.UserInfo;  
  19. import com.example.service.UserInfoService;  
  20.   
  21. /**  
  22.  * 身份校驗核心類  
  23.  *   
  24.  * @author Administrator  
  25.  *  
  26.  */  
  27. public class MyShiroRealm extends AuthorizingRealm {  
  28.   
  29.     @Resource  
  30.     private UserInfoService userInfoService;  
  31.   
  32.     /**  
  33.      * 認證資訊(身份驗證) Authentication 是用來驗證使用者身份  
  34.      */  
  35.     @Override  
  36.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
  37.         System.out.println("MyShiroRealm.doGetAuthenticationInfo()");  
  38.         // 獲取使用者的輸入帳號  
  39.         String username = (String) token.getPrincipal();  
  40.         System.out.println(token.getCredentials());  
  41.         // 通過username從資料庫中查詢 User物件,如果找到,沒找到.  
  42.         // 實際專案中,這裡可以根據實際情況做快取,如果不做,Shiro自己也是有時間間隔機制,2分鐘內不會重複執行該方法  
  43.         UserInfo userInfo = userInfoService.findByUsername(username);  
  44.         System.out.println("----->>userInfo=" + userInfo);  
  45.         if (userInfo == null) {  
  46.             return null;  
  47.         }  
  48.   
  49.         SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo, // 使用者名稱  
  50.                 userInfo.getPassword(), // 密碼  
  51.                 ByteSource.Util.bytes(userInfo.getCredentialsSalt()), // salt=username+salt  
  52.                 getName() // realm name  
  53.         );  
  54.         return authenticationInfo;  
  55.     }  
  56.   
  57.     @Override  
  58.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  59.         // TODO Auto-generated method stub  
  60.         System.out.println("許可權配置-->MyShiroRealm.doGetAuthorizationInfo()");  
  61.   
  62.         SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
  63.         UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();  
  64.           
  65.         for(SysRole role:userInfo.getRoleList()){  
  66.                 
  67.                authorizationInfo.addRole(role.getRole());  
  68.                System.out.println(role.getPermissions());  
  69.                for(SysPermission p:role.getPermissions()){  
  70.                    System.out.println(p);  
  71.                   authorizationInfo.addStringPermission(p.getPermission());  
  72.                }  
  73.            }  
  74.         return authorizationInfo;  
  75.     }  
  76.       
  77.       
  78. }  
繼承AuthorizingRealm主要需要實現兩個方法:
doGetAuthenticationInfo();
doGetAuthorizationInfo();
其中doGetAuthenticationInfo主要是用來進行身份認證的,也就是說驗證使用者輸入的賬號和密碼是否正確。

[html] view plain copy
  1. SimpleAuthenticationInfoauthenticationInfo =  
  2.  new SimpleAuthenticationInfo(  
  3.                 userInfo, //使用者名稱  
  4.                 userInfo.getPassword(), //密碼  
  5.                 ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt  
  6.                 getName()  //realm name  
  7.         );  
交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配,如果覺得人家的不好可以自定義實現
如果你是進行明文進行編碼的話,那麼使用使用如下方式:

[html] view plain copy
  1. SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(  
  2.              userInfo, //使用者名稱  
  3.              userInfo.getPassword(), //密碼  
  4.              getName()  //realm name  
  5.       );  
至於doGetAuthorizationInfo()是許可權控制,當訪問到頁面的時候,使用了相應的註解或者shiro標籤才會執行此方法否則不會執行,所以如果只是簡單的身份認證沒有許可權的控制的話,那麼這個方法可以不進行實現,直接返回null即可。
在這個方法中主要是使用類:SimpleAuthorizationInfo
進行角色的新增和許可權的新增。
authorizationInfo.addRole(role.getRole());
authorizationInfo.addStringPermission(p.getPermission());
當然也可以新增集合:
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(stringPermissions);

到這裡我們還需要有一個步驟很重要就是將我們自定義的Realm注入到SecurityManager中。

在ShiroConfiguration.java中新增方法

[html] view plain copy
  1. /**  
  2.      * 身份認證realm;  
  3.      *   
  4.      */  
  5.     @Bean  
  6.     public MyShiroRealm myShiroRealm(){  
  7.        MyShiroRealm myShiroRealm = new MyShiroRealm();  
  8.        return myShiroRealm;  
  9.     }  
將myShiroRealm注入到securityManager中:

[html] view plain copy
  1. @Bean  
  2.     public SecurityManager securityManager(){  
  3.        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();  
  4.        //設定realm.  
  5.        securityManager.setRealm(myShiroRealm());  
  6.        return securityManager;  
  7.     }  
到這裡的話身份認證許可權控制基本是完成了,最後我們在編寫一個登入的時候,登入的處理:
在HomeController中新增login post處理:

[html] view plain copy
  1. package com.example.controller;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import javax.servlet.http.HttpServletRequest;  
  6.   
  7. import org.apache.shiro.authc.IncorrectCredentialsException;  
  8. import org.apache.shiro.authc.UnknownAccountException;  
  9. import org.springframework.stereotype.Controller;  
  10. import org.springframework.web.bind.annotation.RequestMapping;  
  11. import org.springframework.web.bind.annotation.RequestMethod;  
  12.   
  13. @Controller  
  14. public class HomeController {  
  15.     @RequestMapping({ "/", "index" })  
  16.     public String index() {  
  17.         return "/index";  
  18.     }  
  19.   
  20.     @RequestMapping(value = "/login"method = RequestMethod.GET)  
  21.     public String login() {  
  22.         return "/login";  
  23.     }  
  24.   
  25.     @RequestMapping(value = "/login"method = RequestMethod.POST)  
  26.     public String login(HttpServletRequest request, Map<String, Object> map) {  
  27.         System.out.println("HomeController.login");  
  28.         // 登入失敗從request中獲取shiro處理的異常資訊  
  29.         // shiroLoginFailure:就是shiro異常類的全類名  
  30.         String exception = (String) request.getAttribute("shiroLoginFailure");  
  31.         String msg = "";  
  32.         if (exception != null) {  
  33.             if (UnknownAccountException.class.getName().equals(exception)) {  
  34.                 System.out.println("UnknownAccountException -->帳號不存在:");  
  35.                 msg = "UnknownAccountException -->帳號不存在:";  
  36.             } else if (IncorrectCredentialsException.class.getName().equals(exception)) {  
  37.                 System.out.println("IncorrectCredentialsException -- > 密碼不正確:");  
  38.                 msg = "IncorrectCredentialsException -- > 密碼不正確:";  
  39.             } else if ("kaptchaValidateFailed".equals(exception)) {  
  40.                 System.out.println("kaptchaValidateFailed -- > 驗證碼錯誤");  
  41.                 msg = "kaptchaValidateFailed -- > 驗證碼錯誤";  
  42.             } else {  
  43.                 msg = "else >> " + exception;  
  44.                 System.out.println("else -- >" + exception);  
  45.             }  
  46.         }  
  47.         map.put("msg", msg);  
  48.         // 此方法不處理登入成功,由shiro進行處理.  
  49.         return "/login";  
  50.     }  
  51. }  
這時候我們啟動應用程式,訪問http://127.0.0.1:8080/index
會自動跳轉到http://127.0.0.1:8080/login 介面,然後輸入賬號和密碼:admin/123456,這時候會提示:IncorrectCredentialsException -- > 密碼不正確。
這主要是因為我們在上面進行了密文的方式,那麼怎麼加密方式,我們並沒有告訴Shiro,所以認證失敗了

在這裡我們需要編寫一個加密演算法類,當然Shiro也已經有了具體的實現HashedCredentialsMatcher
我們只需要進行注入使用即可:
在ShiroConfiguration中加入方法:

[html] view plain copy
  1. /**  
  2.      * 憑證匹配器  
  3.      * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了  
  4.      *  所以我們需要修改下doGetAuthenticationInfo中的程式碼;  
  5.      * )  
  6.      * @return  
  7.      */  
  8.     @Bean  
  9.     public HashedCredentialsMatcher hashedCredentialsMatcher(){  
  10.        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();  
  11.         
  12.        hashedCredentialsMatcher.setHashAlgorithmName("md5");//雜湊演算法:這裡使用MD5演算法;  
  13.        hashedCredentialsMatcher.setHashIterations(2);//雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));  
  14.         
  15.        return hashedCredentialsMatcher;  
  16.     }  
在myShiroRealm()方法中注入憑證匹配器:

[html] view plain copy
  1. @Bean  
  2.     public MyShiroRealm myShiroRealm(){  
  3.        MyShiroRealm myShiroRealm = new MyShiroRealm();  
  4.        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());;  
  5.        return myShiroRealm;  
  6.     }  
這時候在訪問/login進行登入就可以登陸到/index介面了。

(d) 許可權控制
在我們新建一個UserInfoController

[html] view plain copy
  1. package com.example.controller;  
  2.   
  3. import org.apache.shiro.authz.annotation.RequiresPermissions;  
  4. import org.springframework.stereotype.Controller;  
  5. import org.springframework.web.bind.annotation.RequestMapping;  
  6.   
  7. @Controller  
  8. @RequestMapping("userInfo")  
  9. public class UserInfoController {  
  10.     /**  
  11.      * 使用者查詢.  
  12.      * @return  
  13.      */  
  14.     @RequestMapping("/userList")  
  15.     public String userInfo(){  
  16.        return "userInfo";  
  17.     }  
  18.      
  19.     /**  
  20.      * 使用者新增;  
  21.      * @return  
  22.      */  
  23.     @RequestMapping("/userAdd")  
  24.     public String userInfoAdd(){  
  25.        return "userInfoAdd";  
  26.     }  
  27.     /**  
  28.      * 使用者刪除;  
  29.      * @return  
  30.      */  
  31.     @RequestMapping("/userDel")  
  32.     @RequiresPermissions("userInfo:del")//許可權管理;  
  33.     public String userDel(){  
  34.        return "userInfoDel";  
  35.     }  
  36. }  
然後執行登入進行訪問:http://127.0.0.1:8080/userInfo/userAdd
並沒有執行doGetAuthorizationInfo()列印資訊,所以我們會發現我們的身份認證是好使了,但是許可權控制好像沒有什麼作用哦。

我們少了幾部分程式碼,

第一就是開啟shiro aop註解支援,這個只需要在ShiroConfiguration加入如下方法進行開啟即可:

[html] view plain copy
  1. /**  
  2.      *  開啟shiro aop註解支援.  
  3.      *  使用代理方式;所以需要開啟程式碼支援;  
  4.      * @param securityManager  
  5.      * @return  
  6.      */  
  7.     @Bean  
  8.     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){  
  9.        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();  
  10.        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);  
  11.        return authorizationAttributeSourceAdvisor;  
  12.     }  
第二就是在controller方法中加入相應的註解:

[html] view plain copy
  1. package com.example.controller;  
  2.   
  3. import org.apache.shiro.authz.annotation.RequiresPermissions;  
  4. import org.springframework.stereotype.Controller;  
  5. import org.springframework.web.bind.annotation.RequestMapping;  
  6.   
  7. @Controller  
  8. @RequestMapping("userInfo")  
  9. public class UserInfoController {  
  10.     /**  
  11.      * 使用者查詢.  
  12.      * @return  
  13.      */  
  14.     @RequestMapping("/userList")  
  15.     @RequiresPermissions("userInfo:view")//許可權管理;  
  16.     public String userInfo(){  
  17.        return "userInfo";  
  18.     }  
  19.      
  20.     /**  
  21.      * 使用者新增;  
  22.      * @return  
  23.      */  
  24.     @RequestMapping("/userAdd")  
  25.     @RequiresPermissions("userInfo:add")//許可權管理;  
  26.     public String userInfoAdd(){  
  27.        return "userInfoAdd";  
  28.     }  
  29.     /**  
  30.      * 使用者刪除;  
  31.      * @return  
  32.      */  
  33.     @RequestMapping("/userDel")  
  34.     @RequiresPermissions("userInfo:del")//許可權管理;  
  35.     public String userDel(){  
  36.        return "userInfoDel";  
  37.     }  
  38. }  
這時候在訪問http://127.0.0.1:8080/userInfo/userAdd 會看到控制檯列印資訊

[html] view plain copy
  1. 許可權配置-->MyShiroRealm.doGetAuthorizationInfo()  

如果訪問:http://127.0.0.1:8080/userInfo/userDel會看到控制檯列印資訊

[html] view plain copy
  1. org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method  
[html] view plain copy
  1. 頁面上看到  
[html] view plain copy
  1. Whitelabel Error Page  
  2.   
  3. This application has no explicit mapping for /error, so you are seeing this as a fallback.  
  4.   
  5. Sun Aug 28 21:36:31 CST 2016  
  6. There was an unexpected error (type=Internal Server Error, status=500).  
  7. Subject does not have permission [userInfo:del]  

----------------------------------------------

pom.xml

[html] view plain copy
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  3.     <modelVersion>4.0.0</modelVersion>  
  4.     <groupId>com.example</groupId>  
  5.     <artifactId>spring-boot-shiro1</artifactId>  
  6.     <version>0.0.1-SNAPSHOT</version>  
  7.     <packaging>jar</packaging>  
  8.   
  9.     <properties>  
  10.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  11.     </properties>  
  12.   
  13.     <!-- Inherit defaults from Spring Boot -->  
  14.     <parent>  
  15.         <groupId>org.springframework.boot</groupId>  
  16.         <artifactId>spring-boot-starter-parent</artifactId>  
  17.         <version>1.4.0.RELEASE</version>  
  18.     </parent>  
  19.   
  20.     <dependencies>  
  21.         <!-- spring boot web支援:mvc,aop... -->  
  22.         <dependency>  
  23.             <groupId>org.springframework.boot</groupId>  
  24.             <artifactId>spring-boot-starter-web</artifactId>  
  25.         </dependency>  
  26.         <!-- thmleaf模板依賴. -->  
  27.         <dependency>  
  28.             <groupId>org.springframework.boot</groupId>  
  29.             <artifactId>spring-boot-starter-thymeleaf</artifactId>  
  30.         </dependency>  
  31.         <!-- shiro spring. -->  
  32.        <dependency>  
  33.            <groupId>org.apache.shiro</groupId>  
  34.            <artifactId>shiro-spring</artifactId>  
  35.            <version>1.2.3</version>  
  36.        </dependency>  
  37.        <!-- Spirng data JPA依賴; -->  
  38.        <dependency>  
  39.            <groupId>org.springframework.boot</groupId>  
  40.            <artifactId>spring-boot-starter-data-jpa</artifactId>  
  41.        </dependency>  
  42.          
  43.        <!-- 熱部署 -->  
  44.         <dependency>  
  45.             <groupId>org.springframework.boot</groupId>  
  46.             <artifactId>spring-boot-devtools</artifactId>  
  47.             <optional>true</optional>  
  48.         </dependency>  
  49.         
  50.        <!-- mysql驅動; -->  
  51.        <dependency>  
  52.            <groupId>mysql</groupId>  
  53.            <artifactId>mysql-connector-java</artifactId>  
  54.        </dependency>  
  55.     </dependencies>  
  56. </project>  

[html] view plain copy
  1. <pre name="code" class="html" style="font-size: 14px; line-height: 28px;">ShiroConfiguration.java  

[html] view plain copy
  1. package com.example.config.shiro;  
  2.   
  3. import java.util.LinkedHashMap;  
  4. import java.util.Map;  
  5.   
  6. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;  
  7. import org.apache.shiro.mgt.SecurityManager;  
  8. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;  
  9. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;  
  10. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;  
  11. import org.springframework.context.annotation.Bean;  
  12. import org.springframework.context.annotation.Configuration;  
  13.   
  14. @Configuration  
  15. public class ShiroConfiguration {  
  16.   
  17.     @Bean  
  18.     public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {  
  19.         System.out.println("ShiroConfiguration.shiroFilter()");  
  20.         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();  
  21.   
  22.         // 必須設定SecuritManager  
  23.         shiroFilterFactoryBean.setSecurityManager(securityManager);  
  24.   
  25.         // 攔截器  
  26.         Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();  
  27.   
  28.         // 配置退出過濾器,其中的具體程式碼Shiro已經替我們實現了  
  29.         filterChainDefinitionMap.put("/logout", "logout");  
  30.   
  31.         // <!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->:這是一個坑呢,一不小心程式碼就不好使了;  
  32.         // <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->  
  33.   
  34.         filterChainDefinitionMap.put("/**", "authc");  
  35.   
  36.         // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面  
  37.         shiroFilterFactoryBean.setLoginUrl("/login");  
  38.         // 登入成功後要跳轉的連結  
  39.         shiroFilterFactoryBean.setSuccessUrl("/index");  
  40.         // 未授權介面;  
  41.         shiroFilterFactoryBean.setUnauthorizedUrl("/403");  
  42.   
  43.         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);  
  44.         return shiroFilterFactoryBean;  
  45.   
  46.     }  
  47.   
  48.     @Bean  
  49.     public SecurityManager securityManager() {  
  50.         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();  
  51.         // 設定realm.  
  52.         securityManager.setRealm(myShiroRealm());  
  53.         return securityManager;  
  54.     }  
  55.   
  56.     /**  
  57.      * 身份認證realm; (這個需要自己寫,賬號密碼校驗;許可權等)  
  58.      *   
  59.      * @return  
  60.      */  
  61.     @Bean  
  62.     public MyShiroRealm myShiroRealm() {  
  63.         MyShiroRealm myShiroRealm = new MyShiroRealm();  
  64.         myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());  
  65.         return myShiroRealm;  
  66.     }  
  67.     /**  
  68.      * 憑證匹配器  
  69.      * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了  
  70.      *  所以我們需要修改下doGetAuthenticationInfo中的程式碼;  
  71.      * )  
  72.      * @return  
  73.      */  
  74.     @Bean  
  75.     public HashedCredentialsMatcher hashedCredentialsMatcher(){  
  76.        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();  
  77.         
  78.        hashedCredentialsMatcher.setHashAlgorithmName("md5");//雜湊演算法:這裡使用MD5演算法;  
  79.        hashedCredentialsMatcher.setHashIterations(2);//雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));  
  80.         
  81.        return hashedCredentialsMatcher;  
  82.     }  
  83.       
  84.     /**  
  85.      *  開啟shiro aop註解支援.  
  86.      *  使用代理方式;所以需要開啟程式碼支援;  
  87.      * @param securityManager  
  88.      * @return  
  89.      */  
  90.     @Bean  
  91.     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){  
  92.        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();  
  93.        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);  
  94.        return authorizationAttributeSourceAdvisor;  
  95.     }  
  96.   
  97. }  

相關文章