1. 程式人生 > >Spring Boot Shiro許可權管理2

Spring Boot Shiro許可權管理2

原文地址:https://412887952-qq-com.iteye.com/blog/2299777 

整合shiro大概分這麼一個步驟:

(a) pom.xml中新增Shiro依賴;
(b) 注入Shiro Factory和SecurityManager。
(c) 身份認證
(d) 許可權控制

 

 

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

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

 

Xml程式碼  
  1. <!-- shiro spring. -->  
  2.        <dependency>  
  3.            <groupId>org.apache.shiro</groupId>  
  4.            <artifactId>shiro-spring</artifactId>  
  5.            <version>1.2.2</version>  
  6.        </dependency>  

 

 

 

(b) 注入Shiro Factory和SecurityManager:

      在Spring中注入類都是使用配置檔案的方式,在Spring Boot中是使用註解的方式,那麼應該如何進行實現呢?

      我們在上一節說過,Shiro幾個核心的類,第一就是ShiroFilterFactory,第二就是SecurityManager,那麼最簡單的配置就是注入這兩個類就ok了,那麼如何注入呢?看如下程式碼:

新建類 com.kfit.config.shiro.ShiroConfiguration:

 

Java程式碼  
  1. package com.kfit.config.shiro;  
  2.    
  3. import java.util.LinkedHashMap;  
  4. import java.util.Map;  
  5.    
  6. import org.apache.shiro.mgt.SecurityManager;  
  7. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;  
  8. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;  
  9. import org.springframework.context.annotation.Bean;  
  10. import org.springframework.context.annotation.Configuration;  
  11.    
  12. /** 
  13.  * Shiro 配置 
  14.  * 
  15. Apache Shiro 核心通過 Filter 來實現,就好像SpringMvc 通過DispachServlet 來主控制一樣。 
  16. 既然是使用 Filter 一般也就能猜到,是通過URL規則來進行過濾和許可權校驗,所以我們需要定義一系列關於URL的規則和訪問許可權。 
  17.  * 
  18.  * @author Angel(QQ:412887952) 
  19.  * @version v.0.1 
  20.  */  
  21. @Configuration  
  22. public class ShiroConfiguration {  
  23.         
  24.      
  25.     /** 
  26.      * ShiroFilterFactoryBean 處理攔截資原始檔問題。 
  27.      * 注意:單獨一個ShiroFilterFactoryBean配置是或報錯的,以為在 
  28.      * 初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager 
  29.      * 
  30.         Filter Chain定義說明 
  31.        1、一個URL可以配置多個Filter,使用逗號分隔 
  32.        2、當設定多個過濾器時,全部驗證通過,才視為通過 
  33.        3、部分過濾器可指定引數,如perms,roles 
  34.      * 
  35.      */  
  36.     @Bean  
  37.     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){  
  38.        System.out.println("ShiroConfiguration.shirFilter()");  
  39.        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();  
  40.         
  41.         // 必須設定 SecurityManager   
  42.        shiroFilterFactoryBean.setSecurityManager(securityManager);  
  43.         
  44.         
  45.         
  46.        //攔截器.  
  47.        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();  
  48.         
  49.        //配置退出過濾器,其中的具體的退出程式碼Shiro已經替我們實現了  
  50.        filterChainDefinitionMap.put("/logout", "logout");  
  51.         
  52.        //<!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->:這是一個坑呢,一不小心程式碼就不好使了;  
  53.         //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->  
  54.        filterChainDefinitionMap.put("/**", "authc");  
  55.         
  56.        // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面  
  57.         shiroFilterFactoryBean.setLoginUrl("/login");  
  58.         // 登入成功後要跳轉的連結  
  59.         shiroFilterFactoryBean.setSuccessUrl("/index");  
  60.         //未授權介面;  
  61.         shiroFilterFactoryBean.setUnauthorizedUrl("/403");  
  62.         
  63.        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);  
  64.        returnshiroFilterFactoryBean;  
  65.     }  
  66.      
  67.      
  68.     @Bean  
  69.     public SecurityManager securityManager(){  
  70.        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();  
  71.        return securityManager;  
  72.     }  
  73.      
  74.      
  75.      
  76. }  

 

 

這裡說下: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的依賴:

 

Xml程式碼  
  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新建一個即可):

 

Properties程式碼  
  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、SysRole.java、SysPermission.java至於之前的關聯表我們使用JPA進行自動生成。

使用者:com.kfit.core.bean.UserInfo :

 

Java程式碼  
  1. package com.kfit.core.bean;  
  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 Angel(QQ:412887952) 
  18.  * @version v.0.1 
  19.  */  
  20. @Entity  
  21. public class UserInfo implements Serializable{  
  22.     private static final long serialVersionUID = 1L;  
  23.     @[email protected]  
  24.     privatelonguid;//使用者id;  
  25.      
  26.     @Column(unique=true)  
  27.     private String username;//賬號.  
  28.      
  29.     private String name;//名稱(暱稱或者真實姓名,不同系統不同定義)  
  30.      
  31.     private String password; //密碼;  
  32.     private String salt;//加密密碼的鹽  
  33.      
  34.     private byte state;//使用者狀態,0:建立未認證(比如沒有啟用,沒有輸入驗證碼等等)--等待驗證的使用者 , 1:正常狀態,2:使用者被鎖定.  
  35.    
  36.      
  37.     @ManyToMany(fetch=FetchType.EAGER)//立即從資料庫中進行載入資料;  
  38.     @JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "roleId") })  
  39.     private List<SysRole> roleList;// 一個使用者具有多個角色  
  40.      
  41.     public List<SysRole> getRoleList() {  
  42.        return roleList;  
  43.     }  
  44.    
  45.     public void setRoleList(List<SysRole> roleList) {  
  46.        this.roleList = roleList;  
  47.     }  
  48.    
  49.     public long getUid() {  
  50.        return uid;  
  51.     }  
  52.    
  53.     public void setUid(longuid) {  
  54.        this.uid = uid;  
  55.     }  
  56.    
  57.     public String getUsername() {  
  58.        return username;  
  59.     }  
  60.    
  61.     public void setUsername(String username) {  
  62.        this.username = username;  
  63.     }  
  64.    
  65.     public String getName() {  
  66.        return name;  
  67.     }  
  68.    
  69.     publicvoid setName(String name) {  
  70.        this.name = name;  
  71.     }  
  72.    
  73.     public String getPassword() {  
  74.        return password;  
  75.     }  
  76.    
  77.     public void setPassword(String password) {  
  78.        this.password = password;  
  79.     }  
  80.    
  81.     public String getSalt() {  
  82.        return salt;  
  83.     }  
  84.    
  85.     public void setSalt(String salt) {  
  86.        this.salt = salt;  
  87.     }  
  88.    
  89.     public byte getState() {  
  90.        return state;  
  91.     }  
  92.    
  93.     public void setState(bytestate) {  
  94.        this.state = state;  
  95.     }  
  96.    
  97.     /** 
  98.      * 密碼鹽. 
  99.      * @return 
  100.      */  
  101.     public String getCredentialsSalt(){  
  102.        return this.username+this.salt;  
  103.     }  
  104.    
  105.     @Override  
  106.     public String toString() {  
  107.        return "UserInfo [uid=" + uid + ", username=" + username + ", name=" + name + ", password=" + password  
  108.               + ", salt=" + salt + ", state=" + state + "]";  
  109.     }  
  110.    
  111.      
  112. }  

 

 

在這裡salt主要是用來進行密碼加密的,當然也可以使用明文進行編碼測試,實際開發中還是建議密碼進行加密。

 

getCredentialsSalt()

這個方法重新對鹽重新進行了定義,使用者名稱+salt,這樣就更加不容易被破解了。

 

角色類 > com.kfit.core.bean.SysRole:

 

Java程式碼  
  1. package com.kfit.core.bean;  
  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 Angel(QQ:412887952) 
  18.  * @version v.0.1 
  19.  */  
  20. @Entity  
  21. public class SysRole implements Serializable{  
  22.     private static final long serialVersionUID = 1L;  
  23.     @[email protected]  
  24.     private Long id; // 編號  
  25.     private String role; // 角色標識程式中判斷使用,如"admin",這個是唯一的:  
  26.     private String description; // 角色描述,UI介面顯示使用  
  27.     private Boolean available = Boolean.FALSE; // 是否可用,如果不可用將不會新增給使用者  
  28.      
  29.     //角色 -- 許可權關係:多對多關係;  
  30.     @ManyToMany(fetch=FetchType.EAGER)  
  31. @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="permissionId")})  
  32.     private List<SysPermission> permissions;  
  33.      
  34.     // 使用者 - 角色關係定義;  
  35.     @ManyToMany  
  36. @JoinTable(name="SysUserRole",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="uid")})  
  37.     private List<UserInfo> userInfos;// 一個角色對應多個使用者  
  38.      
  39.     public List<UserInfo> getUserInfos() {  
  40.        return userInfos;  
  41.     }  
  42.     public void setUserInfos(List<UserInfo> userInfos) {  
  43.        this.userInfos = userInfos;  
  44.     }  
  45.     public Long getId() {  
  46.        return id;  
  47.     }  
  48.     publicvoid setId(Long id) {  
  49.        this.id = id;  
  50.     }  
  51.     public String getRole() {  
  52.        return role;  
  53.     }  
  54.     public void setRole(String role) {  
  55.        this.role = role;  
  56.     }  
  57.     public String getDescription() {  
  58.        return description;  
  59.     }  
  60.     public void setDescription(String description) {  
  61.        this.description = description;  
  62.     }  
  63.     public Boolean getAvailable() {  
  64.        return available;  
  65.     }  
  66.     public void setAvailable(Boolean available) {  
  67.        this.available = available;  
  68.     }  
  69.     public List<SysPermission> getPermissions() {  
  70.        return permissions;  
  71.     }  
  72.     public void setPermissions(List<SysPermission> permissions) {  
  73.        this.permissions = permissions;  
  74.     }  
  75.     @Override  
  76.     public String toString() {  
  77.        return "SysRole [id=" + id + ", role=" + role + ", description=" + description + ", available=" + available  
  78.               + ", permissions=" + permissions + "]";  
  79.     }  
  80. }  

 

 

 

許可權 > com.kfit.core.bean.SysPermission :

 

Java程式碼  
  1. package com.kfit.core.bean;  
  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.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.  * @author Angel(QQ:412887952) 
  17.  * @version v.0.1 
  18.  */  
  19. @Entity  
  20. public class SysPermission implements Serializable{  
  21.     private static final long serialVersionUID = 1L;  
  22.      
  23.     @[email protected]  
  24.     privatelongid;//主鍵.  
  25.     private String name;//名稱.  
  26.      
  27.     @Column(columnDefinition="enum('menu','button')")  
  28.     private String resourceType;//資源型別,[menu|button]  
  29.     private String url;//資源路徑.  
  30.     private String permission; //許可權字串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view  
  31.     private Long parentId; //父編號  
  32.     private String parentIds; //父編號列表  
  33.     private Boolean available = Boolean.FALSE;  
  34.      
  35.     @ManyToMany  
  36. @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="permissionId")},inverseJoinColumns={@JoinColumn(name="roleId")})  
  37.     private List<SysRole> roles;  
  38.      
  39.     public long getId() {  
  40.        return id;  
  41.     }  
  42.     public void setId(longid) {  
  43.        this.id = id;  
  44.     }  
  45.     public String getName() {  
  46.        return name;  
  47.     }  
  48.     publicvoid setName(String name) {  
  49.        this.name = name;  
  50.     }  
  51.     public String getResourceType() {  
  52.        return resourceType;  
  53.     }  
  54.     public void setResourceType(String resourceType) {  
  55.        this.resourceType = resourceType;  
  56.     }  
  57.     public String getUrl() {  
  58.        return url;  
  59.     }  
  60.     public void setUrl(String url) {  
  61.        this.url = url;  
  62.     }  
  63.     public String getPermission() {  
  64.        return permission;  
  65.     }  
  66.     public void setPermission(String permission) {  
  67.        this.permission = permission;  
  68.     }  
  69.     public Long getParentId() {  
  70.        return parentId;  
  71.     }  
  72.     public void setParentId(Long parentId) {  
  73.        this.parentId = parentId;  
  74.     }  
  75.     public String getParentIds() {  
  76.        return parentIds;  
  77.     }  
  78.     public void setParentIds(String parentIds) {  
  79.        this.parentIds = parentIds;  
  80.     }  
  81.     public Boolean getAvailable() {  
  82.        return available;  
  83.     }  
  84.     public void setAvailable(Boolean available) {  
  85.        this.available = available;  
  86.     }  
  87.     public List<SysRole> getRoles() {  
  88.        return roles;  
  89.     }  
  90.     publicvoid setRoles(List<SysRole> roles) {  
  91.        this.roles = roles;  
  92.     }  
  93.     @Override  
  94.     public String toString() {  
  95.        return "SysPermission [id=" + id + ", name=" + name + ", resourceType=" + resourceType + ", url=" + url  
  96.               + ", permission=" + permission + ", parentId=" + parentId + ", parentIds=" + parentIds + ", available="  
  97.               + available + ", roles=" + roles + "]";  
  98.     }  
  99.      
  100. }  

 

 

ok,到這裡實體類就編碼完畢了,在這裡我們看到的是3個實體類,UserInfo,SysRole,SysPermission,對應的是資料庫的五張表:

1表UserInfo、2表SysUserRole、3表SysRole、4表SysRolePermission、5表SysPermission

這時候執行程式,就會自動建表,然後我們新增一些資料:

 

Sql程式碼  
  1. INSERT INTO `SysPermission` VALUES ('1', '1', '使用者管理', '0', '0/', 'userInfo:view', 'menu', 'userInfo/userList');  
  2. INSERT INTO `SysPermission` VALUES ('2', '1', '使用者新增', '1', '0/1', 'userInfo:add', 'button', 'userInfo/userAdd');  
  3. INSERT INTO `SysPermission` VALUES ('3', '1', '使用者刪除', '1', '0/1', 'userInfo:del', 'button', 'userInfo/userDel');  

 

 

 

 

Sql程式碼  
  1. INSERT INTO `SysRole` VALUES ('1', '1', '管理員', 'admin');  
  2. INSERT INTO `SysRole` VALUES ('2', '1', 'VIP會員', 'vip');  

 

 

 

 

Sql程式碼  
  1. INSERT INTO `SysRolePermission` VALUES ('1', '1');  
  2. INSERT INTO `SysRolePermission` VALUES ('1', '2');  

 

 

 

 

 

Sql程式碼  
  1. INSERT INTO `SysUserRole` VALUES ('1', '1');  
  2. INSERT INTO `SysUserRole` VALUES ('1', '2');  

 

 

 

 

Sql程式碼  
  1. INSERT INTO `UserInfo` VALUES ('1', '管理員', 'admin', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', '0');  

  

 

 

這時候資料都準備完畢了,那麼接下來就應該編寫Repository進行訪問資料了(在下載原始碼中有shiro.sql檔案,可自行匯入使用即可)。

com.kfit.core.repository.UserInfoRepository :

 

Java程式碼  
  1. package com.kfit.core.repository;  
  2.    
  3. import org.springframework.data.repository.CrudRepository;  
  4.    
  5. import com.kfit.core.bean.UserInfo;  
  6.    
  7. /** 
  8.  * UserInfo持久化類; 
  9.  * @author Angel(QQ:412887952) 
  10.  * @version v.0.1 
  11.  */  
  12. public interface UserInfoRepository extends CrudRepository<UserInfo,Long>{  
  13.      
  14.     /**通過username查詢使用者資訊;*/  
  15.     public UserInfo findByUsername(String username);  
  16.      
  17. }  

 

 

      在這裡你會發現我們只編寫了UserInfo的資料庫操作,那麼我們怎麼獲取我們的許可權資訊了,通過userInfo.getRoleList()可以獲取到對應的角色資訊,然後在通過對應的角色可以獲取到許可權資訊,當然這些都是JPA幫我們實現了,我們也可以進行直接獲取到許可權資訊,只要寫一個關聯查詢然後過濾掉重複的許可權即可,這裡不進行實現。

編寫一個業務處理類UserInfoService>

 

com.kfit.core.service.UserInfoService :

 

Java程式碼  
  1. package com.kfit.core.service;  
  2.    
  3. import com.kfit.core.bean.UserInfo;  
  4.    
  5. public interface UserInfoService {  
  6.      
  7.     /**通過username查詢使用者資訊;*/  
  8.     public UserInfo findByUsername(String username);  
  9.      
  10. }  

 

 

 

com.kfit.core.service.impl.UserInfoServiceImpl :

 

Java程式碼  
  1. package com.kfit.core.service.impl;  
  2.    
  3. import javax.annotation.Resource;  
  4.    
  5. import org.springframework.stereotype.Service;  
  6.    
  7. import com.kfit.core.bean.UserInfo;  
  8. import com.kfit.core.repository.UserInfoRepository;  
  9. import com.kfit.core.service.UserInfoService;  
  10.    
  11. @Service  
  12. public class UserInfoServiceImpl implements UserInfoService{  
  13.      
  14.     @Resource  
  15.     private UserInfoRepository userInfoRepository;  
  16.      
  17.     @Override  
  18.     public UserInfo findByUsername(String username) {  
  19.        System.out.println("UserInfoServiceImpl.findByUsername()");  
  20.        return userInfoRepository.findByUsername(username);  
  21.     }  
  22.      
  23. }  

 

 

這裡主要是為了滿足MVC程式設計模式,在例子中直接訪問DAO層也未嘗不可,實際開發中建議還是遵循MVC開發模式,至於有什麼好處,請自行百度MVC。

      好了以上都是為了實現身份認證,許可權控制的準備工作,想必大家看了也有點困惑了,堅持住,這個技術點不是每個人都能進行編碼的,你會發現在一個公司裡都是技術領導幫你實現了這一部分很複雜的編碼,你只需要通過介面進行配置而已,所以嘛,要做一個技術領導這一部分不掌握好像還不行了。好了,說重點吧,基本工作準備好之後,剩下的才是重點,shiro的認證最終是交給了Realm進行執行了,所以我們需要自己重新實現一個Realm,此Realm繼承AuthorizingRealm。

com.kfit.config.shiro.MyShiroRealm :

 

Java程式碼  
  1. package com.kfit.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.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.apache.shiro.util.ByteSource;  
  15.    
  16. import com.kfit.core.bean.SysPermission;  
  17. import com.kfit.core.bean.SysRole;  
  18. import com.kfit.core.bean.UserInfo;  
  19. import com.kfit.core.service.UserInfoService;  
  20.    
  21. /** 
  22.  *  身份校驗核心類; 
  23.  * @author Angel(QQ:412887952) 
  24.  * @version v.0.1 
  25.  */  
  26. public class MyShiroRealm extends AuthorizingRealm{  
  27.    
  28.          
  29.     @Resource  
  30.     private UserInfoService userInfoService;  
  31.      
  32.     /** 
  33.      * 認證資訊.(身份驗證) 
  34.      * : 
  35.      * Authentication 是用來驗證使用者身份 
  36.      * @param token 
  37.      * @return 
  38.      * @throws AuthenticationException 
  39.      */  
  40.     @Override  
  41.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
  42.        System.out.println("MyShiroRealm.doGetAuthenticationInfo()");  
  43.         
  44.         
  45.        //獲取使用者的輸入的賬號.  
  46.        String username = (String)token.getPrincipal();  
  47.        System.out.println(token.getCredentials());  
  48.         
  49.        //通過username從資料庫中查詢 User物件,如果找到,沒找到.  
  50.        //實際專案中,這裡可以根據實際情況做快取,如果不做,Shiro自己也是有時間間隔機制,2分鐘內不會重複執行該方法  
  51.        UserInfo userInfo = userInfoService.findByUsername(username);  
  52.        System.out.println("----->>userInfo="+userInfo);  
  53.        if(userInfo == null){  
  54.            return null;  
  55.        }  
  56.         
  57.        /* 
  58.         * 獲取許可權資訊:這裡沒有進行實現, 
  59.         * 請自行根據UserInfo,Role,Permission進行實現; 
  60.         * 獲取之後可以在前端for迴圈顯示所有連結; 
  61.         */  
  62.        //userInfo.setPermissions(userService.findPermissions(user));  
  63.         
  64.         
  65.        //賬號判斷;  
  66.         
  67.        //加密方式;  
  68.        //交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配,如果覺得人家的不好可以自定義實現  
  69.        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(  
  70.               userInfo, //使用者名稱  
  71.               userInfo.getPassword(), //密碼  
  72.                 ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt  
  73.                 getName()  //realm name  
  74.         );  
  75.         
  76.         //明文: 若存在,將此使用者存放到登入認證info中,無需自己做密碼對比,Shiro會為我們進行密碼對比校驗  
  77. //      SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(  
  78. //           userInfo, //使用者名稱  
  79. //           userInfo.getPassword(), //密碼  
  80. //             getName()  //realm name  
  81. //      );  
  82.         
  83.        return authenticationInfo;  
  84.     }  
  85.      
  86.      
  87.      
  88.     /** 
  89.      * 此方法呼叫  hasRole,hasPermission的時候才會進行回撥. 
  90.      * 
  91.      * 許可權資訊.(授權): 
  92.      * 1、如果使用者正常退出,快取自動清空; 
  93.      * 2、如果使用者非正常退出,快取自動清空; 
  94.      * 3、如果我們修改了使用者的許可權,而使用者不退出系統,修改的許可權無法立即生效。 
  95.      * (需要手動程式設計進行實現;放在service進行呼叫) 
  96.      * 在許可權修改後呼叫realm中的方法,realm已經由spring管理,所以從spring中獲取realm例項, 
  97.      * 呼叫clearCached方法; 
  98.      * :Authorization 是授權訪問控制,用於對使用者進行的操作授權,證明該使用者是否允許進行當前操作,如訪問某個連結,某個資原始檔等。 
  99.      * @param principals 
  100.      * @return 
  101.      */  
  102.     @Override  
  103.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  104.        /* 
  105.         * 當沒有使用快取的時候,不斷重新整理頁面的話,這個程式碼會不斷執行, 
  106.         * 當其實沒有必要每次都重新設定許可權資訊,所以我們需要放到快取中進行管理; 
  107.         * 當放到快取中時,這樣的話,doGetAuthorizationInfo就只會執行一次了, 
  108.         * 快取過期之後會再次執行。 
  109.         */  
  110.        System.out.println("許可權配置-->MyShiroRealm.doGetAuthorizationInfo()");  
  111.         
  112.        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
  113.        UserInfo userInfo  = (UserInfo)principals.getPrimaryPrincipal();  
  114.         
  115.        //實際專案中,這裡可以根據實際情況做快取,如果不做,Shiro自己也是有時間間隔機制,2分鐘內不會重複執行該方法  
  116. //     UserInfo userInfo = userInfoService.findByUsername(username)  
  117.         
  118.         
  119.        //許可權單個新增;  
  120.        // 或者按下面這樣新增  
  121.         //新增一個角色,不是配置意義上的新增,而是證明該使用者擁有admin角色     
  122. //     authorizationInfo.addRole("admin");   
  123.         //新增許可權   
  124. //     authorizationInfo.addStringPermission("userInfo:query");  
  125.         
  126.         
  127.         
  128.        ///在認證成功之後返回.  
  129.        //設定角色資訊.  
  130.        //支援 Set集合,  
  131.        //使用者的角色對應的所有許可權,如果只使用角色定義訪問許可權,下面的四行可以不要  
  132. //        List<Role> roleList=user.getRoleList();  
  133. //        for (Role role : roleList) {  
  134. //            info.addStringPermissions(role.getPermissionsName());  
  135. //        }  
  136.        for(SysRole role:userInfo.getRoleList()){  
  137.            authorizationInfo.addRole(role.getRole());  
  138.            for(SysPermission p:role.getPermissions()){  
  139.               authorizationInfo.addStringPermission(p.getPermission());  
  140.            }  
  141.        }  
  142.         
  143.        //設定許可權資訊.  
  144. //     authorizationInfo.setStringPermissions(getStringPermissions(userInfo.getRoleList()));  
  145.         
  146.        return authorizationInfo;  
  147.     }  
  148.      
  149.      
  150.     /** 
  151.      * 將許可權物件中的許可權code取出. 
  152.      * @param permissions 
  153.      * @return 
  154.      */  
  155. //  public Set<String> getStringPermissions(Set<SysPermission> permissions){  
  156. //     Set<String> stringPermissions = new HashSet<String>();  
  157. //     if(permissions != null){  
  158. //         for(SysPermission p : permissions) {  
  159. //            stringPermissions.add(p.getPermission());  
  160. //          }  
  161. //     }  
  162. //       return stringPermissions;  
  163. //  }  
  164.      
  165. }  

 

 

繼承AuthorizingRealm主要需要實現兩個方法:

 

 

doGetAuthenticationInfo();

doGetAuthorizationInfo();

其中doGetAuthenticationInfo主要是用來進行身份認證的,也就是說驗證使用者輸入的賬號和密碼是否正確。