--觀點:html
學習任何的知識,咱們首先要知道它是什麼,而後經過是什麼(定義)來分析它的做用、行爲。從而圈定學習的範圍。咱們將這個過程稱爲,學習思路!!java
官方說明:mysql
href="#what-is-apache-shiro-" What is Apache Shiro?git Apache Shiro is a powerful and flexible open-source security framework that cleanly handles authentication, authorization, enterprise session management and cryptography.github |
Shiro是一個很是強大的、易於使用的、開源的、權限框架。它包括了權限校驗、權限授予、會話管理、安全加密等組件。web
若是你是須要設計RBAC(Role Based Access Control)基礎系統,須要編寫大量用於權限控制的代碼時。那麼你須要使用Shiro。由於Shiro已經將RBAC系統大量的代碼封裝好,能夠減小咱們大量的工做量。算法
如:頁面的顯示的HTML控件根據登陸用戶的權限不一樣而不一樣。使用Shiro能夠輕鬆解決。spring
shiro的下載路徑:http://shiro.apache.org/download.htmlsql
--全部包的說明,根據列表的說明,下載咱們須要的jar包數據庫
包名 |
Maven 座標 |
說明 |
Not Recommended |
Includes all binary functionality for Shiro |
|
<dependency> |
Required in all environments. Slf4j's slf4j-api jar and one of its binding jars is required. commons-beanutils is required only if using INI config.
只要使用shiro必須的核心包,它依賴slf4j 和 commons-beanutils 以及還須要一個INI配置文件 |
|
<dependency> |
Enables support for web-based applications.
支持基於Web的應用 |
|
<dependency> |
||
<dependency> |
Enables Jasig CAS support. the Apache Shiro based buji-pac4j project. 對cas單點登陸框架的支持 |
|
<dependency> |
Enables Ehcache-based famework caching. 對echche緩存框架的支持 |
|
<dependency> |
||
<dependency> |
||
<dependency> |
Enables Google Guice integration.
guice 相似spring的ioc框架。 |
|
<dependency> |
Enables Quartz-based scheduling for Shiro native session validation.
quartz定時器框架 |
|
<dependency> |
--Shiro經常使用包
|
|
Authentication:權限校驗,每次操做校驗用戶是否有訪問權限
Authorization:受權,用戶登陸時,授予用戶對應的權限
Session Management:會話管理,用於記錄用戶的登陸狀態
Cryptography:加密,加密算法的實現(SHA、MD5)
web Support:對Web項目的支持,Shiro的標籤!!
咱們知道學習任何的框架,都是從配置流程開始的。咱們咱們學習Shiro也是同樣。因此首先要了解Shiro的訪問流程。從而知道配置的配置,快速入門。
|
--登陸流程!!!!
根據訪問流程圖,咱們要使用訪問Shiro權限框架的功能。首先須要有一個配置文件shiro.ini配置文件配置了用戶的權限認證信息。而後經過SessionManager對象讀取配置文件,得到用戶身份Subject,
客戶端代碼經過當前用戶訪問有權限的操做。
由此,得出配置步驟:
第一步:任何框架都須要導入包
第二步:建立一個shiro.ini配置文件。(文件名任意編寫,後綴必須爲ini)
第三步:編寫測試代碼
注意事項:shiro框架依賴slf4j以及beanutils包。
|
建立一個shiro.ini配置,編寫權限認證信息。
注意事項:
shiro.ini規則說明
[main] #用於配置SecurityManager裏面的對象 對象名=類全限制名 對象名.屬性[.屬性...] = 值
[users] #用於配置用戶名信息 用戶名= 密碼, 角色1, 角色2, …, 角色N
[roles] #用於配置角色信息 角色名= 權限1, 權限2, …, 權限N #所有權限使用 * (星號)
[urls] #用於配置路徑攔截規則 |
權限格式使用:權限:操做:操做
代碼以下:
[users] jim = jim,admin
[roles] admin = * |
建立一個SecurityManager對象,而且測試校驗是否成功
package cn.gzsxt.shiro;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
public class ShiroDemo {
public static void main(String[] args) { // 1.得到SecurityManager對象 IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.createInstance();
//2.設置SecurityUtils使用的安全管理器是securityManager SecurityUtils.setSecurityManager(securityManager);
//2.得到subject Subject subject = SecurityUtils.getSubject(); AuthenticationToken token=new UsernamePasswordToken("jim", "jim"); //3.校驗用戶名密碼是否正確
try { Subject resultSubject = securityManager.login(subject, token); //得到用戶名 System.out.println(resultSubject.getPrincipal()); //判斷是否擁有admin角色 boolean hasRole = resultSubject.hasRole("admin"); System.out.println(hasRole); } catch (AuthenticationException e) { System.out.println("校驗失敗,用戶名或者密碼不正確"); e.printStackTrace(); } } } |
以上示例,咱們須要記住而且理解入門示例中的幾個API的使用
IniSecurityManagerFactory:做用加載ini配置文件得到SecurityManagerFactory對象
SecurityManager:安全管理容器,就是不然整個Shiro框架受權校驗對象的管理
SecurityUtils :SecurityManager對象幫助類
Subject:驗證經過後用於存儲受權信息的身份對象
UsernamePasswordToken :用於設置校驗信息的對象
IncorrectCredentialsException :密碼出錯異常
UnknownAccountException:用戶名出錯異常
以上案例,咱們看到,咱們的用戶驗證信息來自於ini配置文件的[users]以及[roles]標記。這樣的難以符合咱們實際的需求。
咱們但願能夠將Shiro校驗的用戶信息存儲在數據庫裏面,在從數據庫裏面讀取出來。
解決方案:Shiro是經過Realm機制,實現將配置文件的校驗用戶信息存放在數據庫、LDAP等數據存儲系統裏面。
說白了,如今咱們要作的事情,就是從數據庫裏面得到用戶的驗證信息!!!
|
如圖所示:
第一步:導入包
第二步:建立shiro.ini配置文件
第三步:建立入口的測試類對象
第四步:建立Realm對象
第五步:配置shiro.ini調用Realm對象
|
[main] #聲明realm對象 myRealm=cn.gzsxt.realm.MyRealm
#指定securityManager的realm對象 securityManager.realms=$myRealm |
package cn.gzsxt.test;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.junit.Test;
public class PermissionTest {
@Test public void authc(){
try { //第一步:得到SecurityManager對象 IniSecurityManagerFactory ismf=new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = ismf.createInstance();
//第二步:構造一個subject SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//第三步:封裝用戶名密碼(身份信息) UsernamePasswordToken token=new UsernamePasswordToken("zhangsan", "123456");
//第四步:校驗(登陸) Subject resultSubject = securityManager.login(subject, token);
//第五步:驗證是否經過 System.out.println(resultSubject.isAuthenticated()); } catch (AuthenticationException e) { e.printStackTrace(); }
} } |
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection;
/** * 自動一個MyRealm類,用於權限驗證以及權限授予 * * @author ranger * */ public class MyRealm extends AuthorizingRealm {
/** * 權限驗證 所謂的權限驗證,就是驗證訪問者(subject).是否使用有使用權限的身份。 說白了,就驗證用戶名密碼是否正確 * * 若是校驗成功,咱們就返回AuthenticationInfo對象 若是校驗失敗,咱們須要返回一個異常 * UnknownAccountException * */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("用戶名:" + token.getPrincipal()); if (token.getPrincipal().equals("zhangsan")) { return new SimpleAuthenticationInfo(token.getPrincipal(), "123456", this.getName());
} else { return null; } }
/** * 受權 根據經過校驗的身份(subject),說白了就是登陸成功的訪問者,咱們給予什麼權限。 * 將查詢到的權限信息封裝在AuthorizationInfo裏面返回!! */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection collection) { return null; }
}
|
需求:咱們的密碼是明文的,咱們須要將密碼Md5加密。
問題:咱們發現咱們本身寫的Md5的類,沒法傳遞給SimpleAuthenticationInfo對象,做爲密碼校驗。如何解決的這個問題呢?
答:shiro框架自帶的密碼加密的功能。
|
|
|
--建立Md5密碼
package cn.gzsxt.test;
import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource;
//注意事項:設置的建立密碼的參數必須與校驗器的參數保持一致 public class CreatePasswordUtils {
public static void main(String[] args) { //加密方式 String hashAlgorithmName = "MD5"; //明文密碼 Object credentials = "123456"; //鹽值 Object salt = ByteSource.Util.bytes("nchu234we"); //迭代加密的次數, int hashIterations = 1; //返回加密的結果 Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); //加密後的密碼 System.out.println(result); }
}
|
--修改配置文件
[main] # 對象名 =類全限制名 ,就是建立一個對象 myRealm = cn.gzsxt.realm.MyRealm
#加密的對象 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #設置加密的算法爲md5 ,屬性對應的是set方法 credentialsMatcher.hashAlgorithmName=md5 #設置md5加密次數爲1次 credentialsMatcher.hashIterations=1 #設置md5算法是加鹽 credentialsMatcher.hashSalted=true
#將加密對象指定給對應給myRealm對象 myRealm.credentialsMatcher=$credentialsMatcher
#將realm對象加入到securityManager容器裏面 #引用 securityManager.realms = $對象名 #securityManager對象名是shiro固定的。用於指定securityManager容器 #對象的引用格式爲: 對象名.屬性名 = $對象名; securityManager.realms = $myRealm |
--修改校驗代碼
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
/** * Realm :全部Realm的父接口 * AuthenticatingRealm :只有權限校驗,沒有權限受權的Realm * AuthorizingRealm : 既有權限校驗,權限受權的Realm (主要就是使用它) * JdbcRealm:使用實現的對數據庫 操做的代碼的Realm。它已經寫死了表名,必要要按它定義的表名定義數據庫表。(靈活性差) * * 注意事項:使用Realm,將驗證認證信息與得到受權信息,都放在一塊兒!!!!! * @author ranger * * */ public class MyRealm extends AuthorizingRealm {
/** * 用於權限校驗 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { try { System.out.println("=權限校驗=");
//第一步:得到請求過來的用戶名 Object principal = token.getPrincipal();
//第二步:根據用戶查詢數據庫,返回用戶信息 (模擬數據,查詢到了"admin") if("admin".equals(principal)){ //第三步:若是用戶名相同,校驗密碼,若是密碼正確,不會報異常,若是校驗不經過就報異常 //參數1:用戶名 //參數2:校驗的密碼,從數據庫查詢出來的 //參數3:realm的名字 。隨便設置一個惟一字符串
//4). md5加密鹽值 ByteSource salt = ByteSource.Util.bytes("nchu234we");
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(principal,"8763d15228c560fed665e1fe73b2f601",salt,this.getName()); return authenticationInfo; } } catch (Exception e) { e.printStackTrace(); } return null; }
/** * 權限受權 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) { return null; } } |
注意:如今校驗後返回的認證信息是一個字符串的用戶名,而咱們若是將Shiro的校驗功能用到登陸的邏輯裏面,我明顯須要返回的不是一個用戶名,而是用戶的信息。
用戶的信息,咱們只能使用一個實體類來封裝。能夠是JavaBean或者是Map
答:咱們校驗方法返回的SimpleAuthenticationInfo的構建方法的第一個參數就是用於指定,返回的用戶認證信息的。能夠將用戶名修改成一個咱們指定的實體類對象就能夠了!!!
--構建一個實體類
package cn.gzsxt.javabean;
import java.io.Serializable;
/** * 注意:由於Shiro是一個支持緩存的框架,因此實體類須要實現序列化接口 * @author ranger * */ public class ActiveUser implements Serializable{
private static final long serialVersionUID = -1354674546192347496L; private Integer userId; public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } private String username; private String password; private Integer age; private Integer status; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public static long getSerialversionuid() { return serialVersionUID; }
}
|
--修改返回的認證信息
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.javabean.ActiveUser;
/** * Realm :全部Realm的父接口 * AuthenticatingRealm :只有權限校驗,沒有權限受權的Realm * AuthorizingRealm : 既有權限校驗,權限受權的Realm (主要就是使用它) * JdbcRealm:使用實現的對數據庫 操做的代碼的Realm。它已經寫死了表名,必要要按它定義的表名定義數據庫表。(靈活性差) * * 注意事項:使用Realm,將驗證認證信息與得到受權信息,都放在一塊兒!!!!! * @author ranger * * */ public class MyRealm extends AuthorizingRealm {
/** * 用於權限校驗 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { try { System.out.println("=權限校驗=");
//第一步:得到請求過來的用戶名 Object principal = token.getPrincipal();
//第二步:根據用戶查詢數據庫,返回用戶信息 (模擬數據,查詢到了"admin") if("admin".equals(principal)){ //第三步:若是用戶名相同,校驗密碼,若是密碼正確,不會報異常,若是校驗不經過就報異常 //參數1:用戶名 //參數2:校驗的密碼,從數據庫查詢出來的 //參數3:realm的名字 。隨便設置一個惟一字符串
ActiveUser user=new ActiveUser(); user.setUserId(1); user.setAge(20); user.setPassword("8763d15228c560fed665e1fe73b2f601"); user.setUsername("admin"); user.setStatus(0);
//4). md5加密鹽值 ByteSource credentialsSalt = ByteSource.Util.bytes("nchu234we"); //參數1:用於設置認證信息,返回給調用對象的 //參數2:校驗的密碼 //參數3:若是配置了Md5加密,並且設置了須要加鹽,該參數就是密碼的鹽 //參數4:指定realm的名字,隨便寫個惟一的字符串就能夠。建議直接使用this.getName(); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,"8763d15228c560fed665e1fe73b2f601",credentialsSalt,this.getName()); return authenticationInfo; } } catch (Exception e) { e.printStackTrace(); } //若是校驗成功,返回null return null; }
/** * 權限受權 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) { return null; } } |
--校驗代碼
package cn.gzsxt.test;
import java.util.ArrayList; import java.util.List;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
/** * 注意:根據示例 * 1.身份信息是放在Subject這個身份對象裏面的 * 2.Shiro的全部對象是經過securityManager來管理 * 3.能夠在配置文件配置,認證的信息(數據庫裏面的表數據) * @author ranger * */ public class ShiroTest {
public static void main(String[] args) { //第一步:讀取配置文件 IniSecurityManagerFactory factory= new IniSecurityManagerFactory("classpath:shiro.ini"); //第二步:得到安全管理器 SecurityManager securityManager = factory.createInstance(); //第三步:經過安全管理器幫助類構建一個Subject(身份) SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第四步:構建身份信息(用戶名/密碼) UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
//第五步:校驗是否成功,將一個空的身份,以及身份信息經過登陸方法,若是成功返回帶身份信息的Subject對象。若是不成功就報異常 try { Subject result = securityManager.login(subject, token); System.out.println("認證成功,用戶名"+result.getPrincipals());
boolean flag = result.isAuthenticated(); //使用校驗方法,校驗用戶對應的角色 boolean hasRole = result.hasRole("roleAdmin"); System.out.println("是否有指定的角色:"+hasRole); List<String> hasRoles=new ArrayList<>(); hasRoles.add("roleAdmin"); hasRoles.add("roleEdu"); boolean allRoles = result.hasAllRoles(hasRoles); System.out.println("是否都包括集合的全部角色:"+allRoles);
//校驗用戶是否有對應的權限 boolean permitted = result.isPermitted("user:add"); System.out.println("是否有user:add權限:"+permitted);
boolean permitted2 = result.isPermitted("modular:add"); System.out.println("是否有modular:add權限:"+permitted2);
System.out.println(flag); } catch (AuthenticationException e) { System.out.println("用戶名或者密碼出錯"); e.printStackTrace(); } } } |
---配置文件
#users標籤:用於指定用戶信息,以及用戶的角色 #注意:shiro的支持一個用於有多個角色的 #用戶名= 密碼, 角色1, 角色2, …, 角色N #若是出現多個角色,用於取的是角色的並集 [users] admin = 123456,roleAdmin,roleEdu #roles標籤:用於設置角色的信息,包括的角色以及權限 #權限字符串的格式爲:模塊名:操做:操做... 相似咱們的子權限的設置 [roles] roleAdmin = user:query,user:add,user:delete,modular:* roleEdu = edu:query,edu:add |
根據以上代碼,咱們確認,在配置文件配置的角色以及權限只能夠經過權限校驗的。
問題:咱們之後開發程序是不能夠將程序的權限以及角色寫在配置文件裏面的。而是寫在數據庫裏面!!!咱們如何將數據庫裏面的權限設置給咱們對象的認證者呢?
答:在咱們AuthorizingRealm提供了權限授予的方法doGetAuthorizationInfo能夠用於受權。替換在配置文件的權限、角色信息。
--JavaBean
package cn.gzsxt.javabean;
import java.io.Serializable;
/** * 注意:由於Shiro是一個支持緩存的框架,因此實體類須要實現序列化接口 * @author ranger * */ public class ActiveUser implements Serializable{
private static final long serialVersionUID = -1354674546192347496L; private Integer userId; public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } private String username; private String password; private Integer age; private Integer status;
public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public static long getSerialversionuid() { return serialVersionUID; }
}
|
--Realm
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.javabean.ActiveUser; import cn.gzsxt.javabean.Role;
/** * Realm :全部Realm的父接口 * AuthenticatingRealm :只有權限校驗,沒有權限受權的Realm * AuthorizingRealm : 既有權限校驗,權限受權的Realm (主要就是使用它) * JdbcRealm:使用實現的對數據庫 操做的代碼的Realm。它已經寫死了表名,必要要按它定義的表名定義數據庫表。(靈活性差) * * 注意事項:使用Realm,將驗證認證信息與得到受權信息,都放在一塊兒!!!!! * @author ranger * * */ public class MyRealm extends AuthorizingRealm {
/** * 用於權限校驗 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { try { System.out.println("=權限校驗=");
//第一步:得到請求過來的用戶名 Object principal = token.getPrincipal();
//第二步:根據用戶查詢數據庫,返回用戶信息 (模擬數據,查詢到了"admin") if("admin".equals(principal)){ //第三步:若是用戶名相同,校驗密碼,若是密碼正確,不會報異常,若是校驗不經過就報異常 //參數1:用戶名 //參數2:校驗的密碼,從數據庫查詢出來的 //參數3:realm的名字 。隨便設置一個惟一字符串
ActiveUser user=new ActiveUser(); user.setUserId(1); user.setAge(20); user.setPassword("8763d15228c560fed665e1fe73b2f601"); user.setUsername("admin"); user.setStatus(0);
//4). md5加密鹽值 ByteSource credentialsSalt = ByteSource.Util.bytes("nchu234we"); //參數1:用於設置認證信息,返回給調用對象的 //參數2:校驗的密碼 //參數3:若是配置了Md5加密,並且設置了須要加鹽,該參數就是密碼的鹽 //參數4:指定realm的名字,隨便寫個惟一的字符串就能夠。建議直接使用this.getName(); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,"8763d15228c560fed665e1fe73b2f601",credentialsSalt,this.getName()); return authenticationInfo; } } catch (Exception e) { e.printStackTrace(); }
return null; }
/** * 權限受權,必須在經過驗證後才執行的方法 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) {
System.out.println("===權限授予===="); //得到當前經過驗證的用戶,爲何能夠得到校驗後的用戶信息呢? //答:由於就是若是通不過校驗,確定就會有有受權,進入了受權確定就有校驗的認證用戶了
ActiveUser user=(ActiveUser) token.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
authorizationInfo.addStringPermission("user:query"); authorizationInfo.addStringPermission("user:add"); authorizationInfo.addStringPermission("user:delete"); authorizationInfo.addStringPermission("user:edit"); authorizationInfo.addStringPermission("modular:*");
authorizationInfo.addRole("roleAdmin");
//將權限設置角色裏面 Role role=new Role(); role.setPermisssion(authorizationInfo.getStringPermissions()); user.setRole(role);
return authorizationInfo; } } |
--配置文件
[main] # 對象名 =類全限制名 ,就是建立一個對象 myRealm = cn.gzsxt.realm.MyRealm
#加密的對象 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #設置加密的算法爲md5 credentialsMatcher.hashAlgorithmName=md5 #設置md5加密次數爲1次 credentialsMatcher.hashIterations=1 #設置md5算法是加鹽 credentialsMatcher.hashSalted=true
#將加密對象指定給對應給myRealm對象 myRealm.credentialsMatcher=$credentialsMatcher
#將realm對象加入到securityManager容器裏面 #引用 securityManager.realms = $對象名 #securityManager對象名是shiro固定的。用於指定securityManager容器 #對象的引用格式爲: 對象名.屬性名 = $對象名; securityManager.realms = $myRealm |
--測試代碼
package cn.gzsxt.test;
import java.util.ArrayList; import java.util.List;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
import cn.gzsxt.javabean.ActiveUser; import cn.gzsxt.javabean.Role;
/** * 注意:根據示例 * 1.身份信息是放在Subject這個身份對象裏面的 * 2.Shiro的全部對象是經過securityManager來管理 * 3.能夠在配置文件配置,認證的信息(數據庫裏面的表數據) * @author ranger * */ public class ShiroTest {
public static void main(String[] args) { //第一步:讀取配置文件 IniSecurityManagerFactory factory= new IniSecurityManagerFactory("classpath:shiro.ini"); //第二步:得到安全管理器 SecurityManager securityManager = factory.createInstance(); //第三步:經過安全管理器幫助類構建一個Subject(身份) SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第四步:構建身份信息(用戶名/密碼) UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
//第五步:校驗是否成功,將一個空的身份,以及身份信息經過登陸方法,若是成功返回帶身份信息的Subject對象。若是不成功就報異常 try { Subject result = securityManager.login(subject, token); System.out.println("認證成功,用戶名"+result.getPrincipals());
boolean flag = result.isAuthenticated(); //使用校驗方法,校驗用戶對應的角色 boolean hasRole = result.hasRole("roleAdmin"); System.out.println("是否有指定的角色:"+hasRole); List<String> hasRoles=new ArrayList<>(); hasRoles.add("roleAdmin"); hasRoles.add("roleEdu"); boolean allRoles = result.hasAllRoles(hasRoles); System.out.println("是否都包括集合的全部角色:"+allRoles);
//校驗用戶是否有對應的權限 boolean permitted = result.isPermitted("user:add"); System.out.println("是否有user:add權限:"+permitted);
boolean permitted2 = result.isPermitted("modular:add"); System.out.println("是否有modular:add權限:"+permitted2);
System.out.println(flag);
} catch (AuthenticationException e) { System.out.println("用戶名或者密碼出錯"); e.printStackTrace(); } } } |
在數據庫裏面設計,權限信息表。而後經過shiro讀取數據庫的信息。實現驗證與受權。
-- 導出 shiro-1115 的數據庫結構 DROP DATABASE IF EXISTS `shiro-1115`; CREATE DATABASE IF NOT EXISTS `shiro-1115` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `shiro-1115`;
-- 導出 表 shiro-1115.tb_admin 結構 DROP TABLE IF EXISTS `tb_admin`; CREATE TABLE IF NOT EXISTS `tb_admin` ( `admin_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '管理員編號', `admin_name` varchar(50) DEFAULT NULL COMMENT '管理員名', `admin_account` varchar(50) DEFAULT NULL COMMENT '登陸帳號', `admin_pwd` varchar(50) DEFAULT NULL COMMENT '登陸密碼', `create_date` datetime DEFAULT NULL COMMENT '建立日期', `admin_status` int(11) DEFAULT NULL COMMENT '管理員狀態 0可用,1禁用', `role_id` bigint(20) DEFAULT NULL COMMENT '角色編號', PRIMARY KEY (`admin_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='管理員';
-- 正在導出表 shiro-1115.tb_admin 的數據:~1 rows (大約) DELETE FROM `tb_admin`; /*!40000 ALTER TABLE `tb_admin` DISABLE KEYS */; INSERT INTO `tb_admin` (`admin_id`, `admin_name`, `admin_account`, `admin_pwd`, `create_date`, `admin_status`, `role_id`) VALUES (1, '配置管理員', 'admin', '123456', '2019-04-02 16:33:39', 0, 1); /*!40000 ALTER TABLE `tb_admin` ENABLE KEYS */;
-- 導出 表 shiro-1115.tb_dictionary 結構 DROP TABLE IF EXISTS `tb_dictionary`; CREATE TABLE IF NOT EXISTS `tb_dictionary` ( `dictionary_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '字典編號', `dictionary_name` varchar(200) DEFAULT NULL COMMENT '字典名', `dictionary_value` varchar(500) DEFAULT NULL COMMENT '字典值', `dictionary_type_code` bigint(20) DEFAULT NULL COMMENT '字段類型編碼', `dictionary_type_name` varchar(200) DEFAULT NULL COMMENT '字段類型名稱', PRIMARY KEY (`dictionary_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='tb_dictionary';
-- 正在導出表 shiro-1115.tb_dictionary 的數據:~0 rows (大約) DELETE FROM `tb_dictionary`; /*!40000 ALTER TABLE `tb_dictionary` DISABLE KEYS */; /*!40000 ALTER TABLE `tb_dictionary` ENABLE KEYS */;
-- 導出 表 shiro-1115.tb_modular 結構 DROP TABLE IF EXISTS `tb_modular`; CREATE TABLE IF NOT EXISTS `tb_modular` ( `modular_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '模塊編號', `modular_name` varchar(200) DEFAULT NULL COMMENT '模塊名', `modular_sort` bigint(20) DEFAULT NULL COMMENT '排序順序', PRIMARY KEY (`modular_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='模塊表';
-- 正在導出表 shiro-1115.tb_modular 的數據:~0 rows (大約) DELETE FROM `tb_modular`; /*!40000 ALTER TABLE `tb_modular` DISABLE KEYS */; INSERT INTO `tb_modular` (`modular_id`, `modular_name`, `modular_sort`) VALUES (1, '系統管理模塊', 0); /*!40000 ALTER TABLE `tb_modular` ENABLE KEYS */;
-- 導出 表 shiro-1115.tb_permission 結構 DROP TABLE IF EXISTS `tb_permission`; CREATE TABLE IF NOT EXISTS `tb_permission` ( `permission_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '權限編號', `permission_name` varchar(200) DEFAULT NULL COMMENT '權限名', `permission_action` varchar(500) DEFAULT NULL COMMENT '權限路徑,匹配路徑是否有權限', `permission_is_show` int(11) DEFAULT NULL COMMENT '是否顯示', `permission_key` varchar(500) DEFAULT NULL COMMENT '父菜單編號,0表示頂級菜單', `modular_id` bigint(20) DEFAULT NULL COMMENT '模塊編號', PRIMARY KEY (`permission_id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='權限表';
-- 正在導出表 shiro-1115.tb_permission 的數據:~2 rows (大約) DELETE FROM `tb_permission`; /*!40000 ALTER TABLE `tb_permission` DISABLE KEYS */; INSERT INTO `tb_permission` (`permission_id`, `permission_name`, `permission_action`, `permission_is_show`, `permission_key`, `modular_id`) VALUES (1, '管理員管理', '/admin/toAdminList', 0, 'admin:list', 1), (2, '管理員管理-To增長', '/admin/toAdminAdd', 0, 'admin:list:to_add', 1), (3, '管理員管理-增長', '/admin/addAdmin', 0, 'admin:list:add', 1); /*!40000 ALTER TABLE `tb_permission` ENABLE KEYS */;
-- 導出 表 shiro-1115.tb_role 結構 DROP TABLE IF EXISTS `tb_role`; CREATE TABLE IF NOT EXISTS `tb_role` ( `role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色編號', `role_name` varchar(50) DEFAULT NULL COMMENT '角色名', `role_permissions` varchar(1000) DEFAULT NULL COMMENT '權限集,權限編號使用逗號分隔', PRIMARY KEY (`role_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色';
-- 正在導出表 shiro-1115.tb_role 的數據:~1 rows (大約) DELETE FROM `tb_role`; /*!40000 ALTER TABLE `tb_role` DISABLE KEYS */; INSERT INTO `tb_role` (`role_id`, `role_name`, `role_permissions`) VALUES (1, '超級管理員', '1,2,3'); /*!40000 ALTER TABLE `tb_role` ENABLE KEYS */; |
--配置文件
[users] admin = 123456,roleAdmin [roles] roleAdmin = user:list,user:list:add,user:list:to_add |
--測試入口
package cn.gzsxt.test;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
public class ShiroTest {
public static void main(String[] args) { //第一步:讀取配置文件,建立SecurityManager容器 IniSecurityManagerFactory factory=new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.createInstance();
//第二步:經過SecurityUtil得到一個身份對象 SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第三步:構建一個身份信息(Token) UsernamePasswordToken token=new UsernamePasswordToken("admin","123456"); //驗證信息,得到配置文件的身份信息以及權限
Subject resultSubject = securityManager.login(subject, token);
System.out.println("用戶信息:"+resultSubject.getPrincipal()); }
} |
--Admin
package cn.gzsxt.pojo;
import java.io.Serializable;
public class Admin implements Serializable {
private static final long serialVersionUID = -4165903044029644075L;
private Long adminId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '管理員編號', private String adminName;//VARCHAR(50) NULL DEFAULT NULL COMMENT '管理員名', private String adminAccount;//VARCHAR(50) NULL DEFAULT NULL COMMENT '登陸帳號', private String adminPwd;//VARCHAR(50) NULL DEFAULT NULL COMMENT '登陸密碼', private String createDate;//DATETIME NULL DEFAULT NULL COMMENT '建立日期', private Integer adminStatus;//INT(11) NULL DEFAULT NULL COMMENT '管理員狀態 0可用,1禁用', private Long roleId;//BIGINT(20) NULL DEFAULT NULL COMMENT '角色編號',
//一個管理員只能是有一個角色 private Role role;
public Role getRole() { return role; } public void setRole(Role role) { this.role = role; } public Long getAdminId() { return adminId; } public void setAdminId(Long adminId) { this.adminId = adminId; } public String getAdminName() { return adminName; } public void setAdminName(String adminName) { this.adminName = adminName; } public String getAdminAccount() { return adminAccount; } public void setAdminAccount(String adminAccount) { this.adminAccount = adminAccount; } public String getAdminPwd() { return adminPwd; } public void setAdminPwd(String adminPwd) { this.adminPwd = adminPwd; } public String getCreateDate() { return createDate; } public void setCreateDate(String createDate) { this.createDate = createDate; } public Integer getAdminStatus() { return adminStatus; } public void setAdminStatus(Integer adminStatus) { this.adminStatus = adminStatus; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } } |
--Role
package cn.gzsxt.pojo;
import java.io.Serializable; import java.util.List;
public class Role implements Serializable {
private static final long serialVersionUID = 7439278538619165462L;
private Long roleId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '角色編號', private String roleName;//VARCHAR(50) NULL DEFAULT NULL COMMENT '角色名', private String rolePermissions;//VARCHAR(1000) NULL DEFAULT NULL COMMENT '權限集,權限編號使用逗號分隔', //一個就是有多個權限 private List<Permission> permissions; public List<Permission> getPermissions() { return permissions; } public void setPermissions(List<Permission> permissions) { this.permissions = permissions; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRolePermissions() { return rolePermissions; } public void setRolePermissions(String rolePermissions) { this.rolePermissions = rolePermissions; }
} |
--Permission
package cn.gzsxt.pojo;
import java.io.Serializable;
public class Permission implements Serializable {
private static final long serialVersionUID = -8720560773258570437L;
private Long permissionId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '權限編號', private String permissionName;//VARCHAR(200) NULL DEFAULT NULL COMMENT '權限名', private String permissionAction;//VARCHAR(500) NULL DEFAULT NULL COMMENT '權限路徑,匹配路徑是否有權限', private Integer permissionIsShow;//INT(11) NULL DEFAULT NULL COMMENT '是否顯示', private String permissionKey;//VARCHAR(500) NULL DEFAULT NULL COMMENT '父菜單編號,0表示頂級菜單', private Long modularId;//BIGINT(20) NULL DEFAULT NULL COMMENT '模塊編號', public Long getPermissionId() { return permissionId; } public void setPermissionId(Long permissionId) { this.permissionId = permissionId; } public String getPermissionName() { return permissionName; } public void setPermissionName(String permissionName) { this.permissionName = permissionName; } public String getPermissionAction() { return permissionAction; } public void setPermissionAction(String permissionAction) { this.permissionAction = permissionAction; } public Integer getPermissionIsShow() { return permissionIsShow; } public void setPermissionIsShow(Integer permissionIsShow) { this.permissionIsShow = permissionIsShow; } public String getPermissionKey() { return permissionKey; } public void setPermissionKey(String permissionKey) { this.permissionKey = permissionKey; } public Long getModularId() { return modularId; } public void setModularId(Long modularId) { this.modularId = modularId; } } |
package cn.gzsxt.utils;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException;
public class DbUitls {
//1.得到數據庫鏈接 public static Connection getConnection(){ //四要素 String driver="org.gjt.mm.mysql.Driver"; String url="jdbc:mysql://localhost:3306/shiro-1115"; String user="root"; String password="123456";
//加載驅動 try { Class.forName(driver); return DriverManager.getConnection(url, user, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return null; }
public static void main(String[] args) { System.out.println(DbUitls.getConnection()); }
} |
--權限驗證代碼
package cn.gzsxt.realm;
import java.sql.SQLException; import java.util.List;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.dao.AdminDAO; import cn.gzsxt.dao.PermissionDAO; import cn.gzsxt.dao.RoleDAO; import cn.gzsxt.pojo.Admin; import cn.gzsxt.pojo.Permission; import cn.gzsxt.pojo.Role;
public class MyRealm extends AuthorizingRealm {
private AdminDAO adminDAO = new AdminDAO(); private RoleDAO roleDAO=new RoleDAO(); private PermissionDAO permissionDAO =new PermissionDAO();
/** * 權限驗證 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=權限驗證==");
// 鏈接數據庫,經過用戶名查詢用戶信息 // 1.得到認證信息,帳號名 String adminAccount = (String) token.getPrincipal(); // 2.經過帳號名查詢查詢指定的管理員記錄 try { Admin admin = adminDAO.findByAccount(adminAccount); //密碼加鹽 ByteSource salt=ByteSource.Util.bytes("asdfwera"+admin.getAdminSalt()); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(admin,admin.getAdminPwd(),salt,this.getName()); return authenticationInfo; } catch (SQLException e) { e.printStackTrace(); }
return null; }
/** * 權限受權,授予必須依賴權限驗證經過 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
}
}
|
--配置文件修改
#[users] # admin = 123456,roleAdmin #[roles] # roleAdmin = user:list,user:list:add,user:list:to_add
[main] #自定義Realm myRealm = cn.gzsxt.realm.MyRealm #建立一個Hash算法的對象 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #設置hash算法是Md5算法 credentialsMatcher.hashAlgorithmName=md5 #設置迭代加密的次數 credentialsMatcher.hashIterations=2 #設置是否加鹽 credentialsMatcher.hashSalted=true
#指定自定義Realm支持hash算法加密的密碼 myRealm.credentialsMatcher=$credentialsMatcher securityManager.realms=$myRealm |
package cn.gzsxt.realm;
import java.sql.SQLException; import java.util.List;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.dao.AdminDAO; import cn.gzsxt.dao.PermissionDAO; import cn.gzsxt.dao.RoleDAO; import cn.gzsxt.pojo.Admin; import cn.gzsxt.pojo.Permission; import cn.gzsxt.pojo.Role;
public class MyRealm extends AuthorizingRealm {
private AdminDAO adminDAO = new AdminDAO(); private RoleDAO roleDAO=new RoleDAO(); private PermissionDAO permissionDAO =new PermissionDAO();
/** * 權限驗證 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=權限驗證==");
// 鏈接數據庫,經過用戶名查詢用戶信息 // 1.得到認證信息,帳號名 String adminAccount = (String) token.getPrincipal(); // 2.經過帳號名查詢查詢指定的管理員記錄 try { Admin admin = adminDAO.findByAccount(adminAccount); //密碼加鹽 ByteSource salt=ByteSource.Util.bytes("asdfwera"+admin.getAdminSalt()); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(admin,admin.getAdminPwd(),salt,this.getName()); return authenticationInfo; } catch (SQLException e) { e.printStackTrace(); }
return null; }
/** * 權限受權,授予必須依賴權限驗證經過 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //得到當前驗證經過的管理員 Admin admin=(Admin) principalCollection.getPrimaryPrincipal(); //經過當前管理員得到角色編號 Long roleId = admin.getRoleId(); try { //經過角色編號查詢惟一的角色(單角色權限驗證) Role role = roleDAO.findByRoleId(roleId); //將角色信息放在管理員的實體類裏面 admin.setRole(role);
//經過就是的權限ID集,得到角色的權限 String rolePermissions = role.getRolePermissions(); List<Permission> permissions = permissionDAO.findByIds(rolePermissions); //將角色的權限賦予角色對象 role.setPermissions(permissions);
//建立一個權限授予對象 SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo(); //將角色設置給該對象 authorizationInfo.addRole(role.getRoleName()); //將權限設置給Shiro的權限對象 for (Permission permission : permissions) { authorizationInfo.addStringPermission(permission.getPermissionKey()); } //返回 return authorizationInfo;
} catch (SQLException e) { e.printStackTrace(); }
return null; }
}
|
一個權限控制框架。
就是在實現Rbac系統的時候,使用它來作權限驗證,能夠減小咱們的開發的代碼。
IniSecurityManagerFactory : 用於加載配置文件,建立SecurityManager對象
SecurityManager :就是整個Shiro的控制對象
SecurityUtils :SecurityManager 工具類,用於得到Subject對象
Subject :身份類,存儲返回的數據信息、提供了校驗的權限的方法
UsernamePasswordToken 身份信息構建類 (Token 令牌,做用就是傳入校驗參數)
AuthorizingRealm 支持校驗與受權的Realm
AuthenticationInfo 校驗成功返回的信息的父接口
SimpleAuthenticationInfo 校驗成功返回信息類
Md5Hash Md5加密類
ByteSource 字節碼處理工具類,咱們在構造Md5加鹽時使用到。
HashedCredentialsMatcher Md5算法校驗器,用於支持Md5校驗
AuthorizationInfo 受權成功返回的信息類的父接口
PrincipalCollection 授予是得到驗證信息的類
SimpleAuthorizationInfo 受權成功返回的信息類的實現類
|
上面的知識,咱們已經瞭解Shiro的權限受權、權限校驗、Md5的加密密碼的配置。
可是,咱們的需求將Shiro框架用到Web項目。
因此,咱們須要使用Shiro整合SpringMVC使用!!!
|
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <a href="${pageContext.request.contextPath }/admin/addAdmin">addAdmin</a> </body> </html> |
package cn.gzsxt.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.annotation.SessionScope;
@Controller @SessionScope @RequestMapping(value="admin") public class AdminController {
@RequestMapping(value="/addAdmin") public String addAdmin(){ System.out.println("=增長管理員="); return "/index.jsp"; }
}
|
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>shiro-demo-08-springmvc</display-name>
<!-- 配置核心控制器。攔截器全部的請求 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定配置文件的路徑 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-*.xml</param-value> </init-param> <!-- 配置啓動的時候就建立對象 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> |
--SpringMVC配置文件,spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!-- 啓動默認註解支持 --> <mvc:annotation-driven /> <!-- 放開靜態資源訪問 --> <mvc:default-servlet-handler/>
</beans>
|
--Spring容器配置文件,spring-context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 掃描組件配置 --> <context:component-scan base-package="cn.gzsxt" /> </beans>
|
|
[users] admin = 123456,adminRole [roles] adminRole = admin:list,admin:add,admin:delete,admin:edit |
package cn.gzsxt.shiro.test;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
public class ShiroTest {
public static void main(String[] args) { //第一步:加載ini配置文件,得到SecurityManagerFactory對象 IniSecurityManagerFactory factory=new IniSecurityManagerFactory("classpath:shiro.ini"); //第二步:得到SecurityManager對象 SecurityManager securityManager = factory.createInstance(); //第三步:得到Subject身份對象 SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第四步:構建一個身份信息對象(Token) UsernamePasswordToken token=new UsernamePasswordToken("admin","123456");
//第五步:login操做。校驗權限 Subject resultSubject = securityManager.login(subject, token);
//第六步:校驗是否成功 boolean isAuth= resultSubject.isAuthenticated(); System.out.println(isAuth);
}
}
|
--修改配置文件
#[users] # admin = 123456,adminRole #[roles] # adminRole = admin:list,admin:add,admin:delete,admin:edit
[main] myRealm = cn.gzsxt.realm.MyRealm securityManager.realms=$myRealm |
--MyRealm類
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection;
public class MyRealm extends AuthorizingRealm {
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=權限校驗="); try { if (token.getPrincipal().equals("admin")) { SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), "123456", this.getName()); return info; } } catch (Exception e) { e.printStackTrace(); }
return null; }
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); info.addRole("role_admin"); info.addStringPermission("user:add"); info.addStringPermission("user:list"); //info.addStringPermission("user:edit");
return info;
}
}
|
--運行ShiroTest測試類,結果爲:
|
答:Spring提供了一個Filter代理類,可讓Spring容器代理Filter的操做,DelegatingFilterProxy。實現了在過濾裏面能夠調用Spring容器的對象,把咱們原來配置在web.xml的過濾器配置在Spring配置文件裏面。
在org.apache.shiro.web.filter.mgt.DefaultFilter枚舉裏面定義可使用的過濾器以及對於的枚舉值
|
anon : 匿名過濾器,該過濾器的做用就是用於配置不須要受權就能夠直接訪問的路徑。
authc: 校驗過濾器,該過濾器的做用就是用於必須通過校驗才能夠訪問的路徑(url)
logout: 註銷過濾器, 該過濾器的做用就是用於註銷退出登陸。
perms:權限驗證過濾器,用於權限驗證的
<filter> <filter-name>securityFilterBean</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- 將生命週期交給Spring代理 --> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>securityFilterBean</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置攔截器調用的對象 注意事項:bean的名字必需要和web.xml的DelegatingFilterProxy的過濾的name屬性一一對應。 --> <bean name="securityFilterBean" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 指定跳轉到校驗不成功的路徑 ,不指定會跳轉上一次訪問菜單頁面 --> <!-- <property name="unauthorizedUrl" value="/undo"></property> --> <!-- 指定依賴的securityManager對象 --> <property name="securityManager" ref="securityManager"></property> <!-- 指定 登陸請求的路徑 --> <property name="loginUrl" value="/admin/loginAdmin"></property> <!-- 登陸成功跳轉的路徑 --> <property name="successUrl" value="/index"></property> <!-- 用於指定執行的過濾器鏈 ,配置多個過濾器鏈接對的url --> <property name="filterChainDefinitions"> <value> <!-- 指定url與過濾器的關係 --> <!-- 全部的路徑都要攔截 --> /admin/toLogin = anon /** = authc </value> </property>
</bean>
<!-- 2.指定securityManager的對象 --> <bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm" />
</bean>
<!-- 3.建立一個Realm對象 --> <bean name="myRealm" class="cn.gzsxt.realm.MyRealm"></bean>
<!-- 4.Spring容器對shiro生命週期的管理 ,基於註解權限攔截須要配置--> <bean name="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
</beans> |
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> 主頁 <a href="${pageContext.request.contextPath }/logout">退出</a><br/> <shiro:hasPermission name="user:list"> <!-- 若是有user:list權限才顯示 用戶列表 --> <a href="#"> 用戶列表</a><br/> </shiro:hasPermission> <shiro:hasPermission name="user:add"> <!-- 若是有user:add權限才顯示 用戶增長 --> <a href="#"> 用戶增長</a><br/> </shiro:hasPermission> <shiro:hasPermission name="user:edit"> <!-- 若是有user:edit權限才顯示 用戶編輯 --> <a href="#"> 用戶編輯</a><br/> </shiro:hasPermission> <shiro:hasPermission name="user:delete"> <!-- 若是有user:delete權限才顯示 用戶刪除 --> <a href="#"> 用戶刪除</a><br/> </shiro:hasPermission> </body> </html> |
<filter> <filter-name>securityFilterBean</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- 將生命週期交給Spring代理 --> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>securityFilterBean</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
package cn.gzsxt.config;
import java.util.LinkedHashMap; import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import cn.gzsxt.realm.ShiroRealm;
@Configuration public class ShiroConfig {
//1.配置securityFilterBean對象 @Bean(name="securityFilter") public Object getShiroFilterFactory() { ShiroFilterFactoryBean factory=new ShiroFilterFactoryBean();
//設置屬性 factory.setLoginUrl("/login"); factory.setSuccessUrl("/index"); factory.setSecurityManager(this.getSecurityManager()); //基於配置的權限過濾器,順序執行,因此使用LinkedHashMap Map<String, String> filterChainDefinition=new LinkedHashMap<>(); filterChainDefinition.put("/**", "authc"); factory.setFilterChainDefinitionMap(filterChainDefinition);
Object securityFilter=null; try { securityFilter = factory.getObject(); } catch (Exception e) { e.printStackTrace(); } return securityFilter; }
//2.得到securityManager對象 @Bean(name="securityManager") public DefaultWebSecurityManager getSecurityManager() { DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager(); //設置須要的realm對象 securityManager.setRealm(this.getRealm()); return securityManager; }
//3.配置realm對象 @Bean public ShiroRealm getRealm(){ ShiroRealm realm=new ShiroRealm(); return realm; } } |