shiro最終是經過Realm獲取安全數據的(如用戶、角色、權限),也就是說認證或者受權都會經過Realm進行數據操做java
1.2.1 源代碼mysql
1.2.2 方法說明
》getName:返回一個惟一的 Realm 名字
》supports:判斷此 Realm 是否支持此 Token
》getAuthenticationInfo:根據 Token 獲取認證信息,該方法就是用來實現認證邏輯的(從Realm的實現類org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo中能夠看出)sql
》層級關係數據庫
》關係圖apache
》開發時通常將用戶名和密碼封裝成一個UsernamePasswordToken對象api
》supports須要對Token類型進行判斷,判斷實參類型是否知足條件;這裏指定的是AuthenticationToken類型(任何Token類型均可以傳入,由於AuthenticationToken是一個父接口),因此在實現類中只須要判斷實參是不是指定的Token類型便可(實際開發時傳入的實參通常都是UsernamePasswordToken類型,因此在supports方法中只須要斷定實參是不是這個類型便可)。
》實現認證的邏輯就是寫在org.apache.shiro.realm.Realm#getAuthenticationInfo這個方法中的(從Realm的實現類org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo中能夠看出)緩存
1.5.1 思路
》實現Realm接口
》getName返回一個惟一的Realm名稱便可
》supports中斷定實參類型是不是UsernamePasswordToken類型
》getAuthenticationInfo中實現認證邏輯
1.5.2 代碼實現安全
package com.xunyji.demo03.shirotest.realm; import org.apache.shiro.authc.*; import org.apache.shiro.realm.Realm; /** * @author AltEnter * @create 2019-01-20 20:11 * @desc 自定義簡易Realm **/ public class MySimpleRealm implements Realm { public String getName() { return "mySimpleRealm"; } public boolean supports(AuthenticationToken token) { if (token instanceof UsernamePasswordToken) { return true; } return false; } public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); String password = new String((char[])token.getCredentials()); System.out.println(String.format("用戶名爲:%s, 用戶密碼爲:%s", username, password)); if (!"fury".equals(username)) { System.out.println("用戶名錯誤"); return null; } if (!"111111".equals(password)) { System.out.println("密碼錯誤"); return null; } return new SimpleAuthenticationInfo(username, password, getName()); } }
1.5.3 單元測試類maven
package com.xunyji.demo03.shirotest.realm; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.subject.Subject; import org.junit.Test; import static org.junit.Assert.*; public class MySimpleRealmTest { @Test public void test01() { MySimpleRealm mySimpleRealm = new MySimpleRealm(); DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(mySimpleRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("fury", "111111"); subject.login(token); System.out.println(String.format("認證結果:%s", subject.isAuthenticated())); } }
Realm接口主要是認證的主接口,可是它的一些實現類既能夠進行認證也能夠進行受權邏輯ide
todo: 貼圖
》CachingRealm:帶有緩存實現的Realm,至關於Realm的擴展
》AuthenticatingRealm:專門作認證的Realm,它繼承自CachingRealm
》AuthorizingRealm:專門作受權的Realm,由於它集成自AuthenticatingRealm,因此它也能夠實現認證邏輯;自定義的Realm通常都是繼承該類,而後重寫裏面的認證方法和受權方法便可。
》IniRealm:用ini文件存儲用戶信息時使用,[users]部分指定用戶名/密碼及其角色; [roles]部分指 定角色即權限信息;
》PropertiesRealm:用properties文件存儲用戶信息時使用,user.username=password,role1,role2 指定用戶 名/密碼及其角色;role.role1=permission1,permission2 指定角色及權限信息;
》JdbcRealm:用數據庫存儲用戶信息時使用,經過 sql 查詢相應的信息,如「select password from users where username = ?」獲取用戶密碼,「select password, password_salt from users where username = ?」獲取用戶密碼及鹽;「select role_name from user_roles where username = ?」 獲取用戶角色;「select permission from roles_permissions where role_name = ?」獲取角色對 應的權限信息;也能夠調用相應的 api 進行自定義 sql;
》getAuthenticationInfo:該方法是Realm中getAuthenticationInfo的實現,該方法是實現認證邏輯的;該方法是一個final方法,因此AuthenticatingRealm的子類不能重寫該方法;
》doGetAuthenticationInfo:getAuthenticationInfo方法調用doGetAuthenticationInfo實現認證邏輯,該方法是一個protected方法,專門暴露給子類進行重寫的,並且是子類實現認證邏輯必須重寫的方法。
2.4.1 IniRealm
》用戶相關信息存儲在一個ini文件中
2.4.2 PropertiesRealm
》用戶相關信息存儲在一個properties文件中
2.4.3 JdbcRealm
》用戶信息存儲在數據庫中
2.5.1 繼承關係圖
2.5.2 建立一個maven工程並引入shiro、junit相關依賴
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
2.5.3 在resources目錄下建立ini文件用於存放用戶信息
》項目目錄結構
https://blog.csdn.net/u011781521/article/details/74892074
》ini文件內容
[users]
fury=111111,role1,role2
zeus=222222,role1
[roles]
role1=user:delete,user:update,user:create,user:read
role2=car:create
》測試代碼
package com.xunyji.demo03.shirotest.realm; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.realm.text.IniRealm; import org.apache.shiro.subject.Subject; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; /** * @author AltEnter * @create 2019-01-17 22:07 * @desc IniRealm測試類 **/ public class IniRealmDemo { @Test public void iniRealmTest() { // 01 建立Realm IniRealm iniRealm = new IniRealm("classpath:shiro.ini"); // 02 建立SecurityManager並將Realm設置到SecurityManager中 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(iniRealm); // 03 將SecurityManager設置到SecurityUtils中 SecurityUtils.setSecurityManager(defaultSecurityManager); // 04 從SecurityUtils中獲取Subject Subject subject = SecurityUtils.getSubject(); // 05 將用戶名和用戶密碼封裝成一個Token UsernamePasswordToken token = new UsernamePasswordToken("zeus", "222222"); // 06 經過Subject進行登陸認證 try { subject.login(token); } catch (Exception e) { e.printStackTrace(); } // 07 經過Subject判斷登陸認證結果 System.out.println(String.format("認證結果爲:%s", subject.isAuthenticated())); ArrayList<String> roleList = new ArrayList<String>(); roleList.add("role1"); System.out.println("是否有role1角色:" + Arrays.toString(subject.hasRoles(roleList))); System.out.println("是否有role1角色:" + subject.hasRole("role1")); subject.checkPermission("user:create"); } }
參見2.5 + 百度
》繼承關係圖
》引入shiro、junit、mysql、druid依賴
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency>
》根據JdbcRealm源碼建立相關表的SQL
/* Navicat MySQL Data Transfer Source Server : mysql5.4 Source Server Version : 50540 Source Host : localhost:3306 Source Database : shiro Target Server Type : MYSQL Target Server Version : 50540 File Encoding : 65001 Date: 2019-01-17 21:07:56 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `roles_permissions` -- ---------------------------- DROP TABLE IF EXISTS `roles_permissions`; CREATE TABLE `roles_permissions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_name` varchar(255) NOT NULL, `permission` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of roles_permissions -- ---------------------------- INSERT INTO `roles_permissions` VALUES ('1', 'admin', 'user:update'); -- ---------------------------- -- Table structure for `user_roles` -- ---------------------------- DROP TABLE IF EXISTS `user_roles`; CREATE TABLE `user_roles` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `role_name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of user_roles -- ---------------------------- INSERT INTO `user_roles` VALUES ('1', 'fury', 'admin'); INSERT INTO `user_roles` VALUES ('2', 'fury', 'user'); -- ---------------------------- -- Table structure for `users` -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of users -- ---------------------------- INSERT INTO `users` VALUES ('1', 'fury', '1111');
》測試類
package com.xunyji.demo03.shirotest.realm; import com.alibaba.druid.pool.DruidDataSource; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.realm.jdbc.JdbcRealm; import org.apache.shiro.subject.Subject; import org.junit.Test; /** * @author AltEnter * @create 2019-01-20 21:07 * @desc JdbcRealm使用Demo **/ public class JdbcRealmDemo { DruidDataSource data =new DruidDataSource(); { data.setUrl("jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC"); data.setUsername("root"); data.setPassword("182838"); } @Test public void testAuthentication(){ JdbcRealm jdbcRealm = new JdbcRealm(); jdbcRealm.setDataSource(data); jdbcRealm.setPermissionsLookupEnabled(true); // 開啓權限查詢功能 String sql="select password from users where username= ?"; jdbcRealm.setAuthenticationQuery(sql); String roleSql ="select role_name from user_roles where username = ?"; jdbcRealm.setUserRolesQuery(roleSql); //1.構建securtymanager DefaultSecurityManager manager = new DefaultSecurityManager(); manager.setRealm(jdbcRealm); //2.主體提交認證請求 SecurityUtils.setSecurityManager(manager); Subject subject = SecurityUtils.getSubject(); // UsernamePasswordToken token =new UsernamePasswordToken("Mark","123456"); UsernamePasswordToken token =new UsernamePasswordToken("fury","111111"); subject.login(token); //是否定證的一個方法 boolean authenticated = subject.isAuthenticated(); System.out.println("authenticated==============="+authenticated); subject.checkRole("user"); subject.checkRole("admin"); subject.checkPermission("user:update"); } }
2.8 SimpleAccountRealm使用教程
》層次關係圖
》說明
這種類型是經過硬編碼來存儲用戶數據的
》測試代碼
package com.xunyji.demo03.shirotest.realm; import com.sun.org.apache.bcel.internal.generic.NEW; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.realm.SimpleAccountRealm; import org.apache.shiro.subject.Subject; import org.junit.Before; import org.junit.Test; /** * @author AltEnter * @create 2019-01-20 21:15 * @desc SimpleAccountRealm使用Demo類 **/ public class SimpleAccountRealmDemo { private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); @Before public void addUser() { simpleAccountRealm.addAccount("fury", "111111"); } @Test public void test01() { DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(simpleAccountRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("fury", "111111"); subject.login(token); System.out.println(String.format("認證結果爲:%s", subject.isAuthenticated())); subject.logout(); System.out.println(String.format("認證結果爲:%s", subject.isAuthenticated())); } }