Shiro安全框架入門學習
Shiro官方網址:http://shiro.apache.org/
1、Shiro安全框架簡介,什麼是Shiro。
1)、Apache的強大靈活的開源安全框架。
2)、認證、授權、企業會話管理、安全加密。
2、Shiro和Spring Security的比較。
1)、Shiro更加簡單和靈活,可以脫離Spring,粒度較粗。
2)、Spring Security更加複雜和笨重,不可脫離Spring,粒度更細。
3、Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易於理解的API,您可以快速、輕鬆地獲得任何應用程式,從最小的移動應用程式到最大的網路和企業應用程式。
Authenticator:認證器,管理著登陸和登出。
Authorizer:授權器,管理主題擁有那些許可權。
Session Manager:Session管理器。
Session DAO:提供了Session的增加、修改、刪除、查詢操作。
Cache Manager:快取管理器,可以快取角色資料、許可權資料。
Pluggable Realms:可以理解為Shiro和資料庫之前的橋樑,shiro獲取角色資訊、許可權資訊都是通過Realms來獲取的。
4、三個核心元件:Subject, SecurityManager 和 Realms。
1)、Subject:即“當前操作使用者”。但是,在Shiro中,Subject這一概念並不僅僅指人,也可以是第三方程序、後臺帳戶(Daemon Account)或其他類似事物。它僅僅意味著“當前跟軟體互動的東西”。Subject代表了當前使用者的安全操作,SecurityManager則管理所有使用者的安全操作。
2)、SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通過SecurityManager來管理內部元件例項,並通過它來提供安全管理的各種服務。
3)、Realm: Realm充當了Shiro與應用安全資料間的“橋樑”或者“聯結器”。也就是說,當對使用者執行認證(登入)和授權(訪問控制)驗證時,Shiro會從應用配置的Realm中查詢使用者及其許可權資訊。從這個意義上講,Realm實質上是一個安全相關的DAO:它封裝了資料來源的連線細節,並在需要時將相關資料提供給Shiro。當配置Shiro時,你必須至少指定一個Realm,用於認證和(或)授權。配置多個Realm是可以的,但是至少需要一個。
Shiro內建了可以連線大量安全資料來源(又名目錄)的Realm,如LDAP、關係資料庫(JDBC)、類似INI的文字配置資源以及屬性檔案等。如果預設的Realm不能滿足需求,你還可以插入代表自定義資料來源的自己的Realm實現。
5、Shiro的認證、認證過程。
Shiro中的SecurityManager是用來提供安全服務的,如果做Shiro認證的時候,首先要建立SecurityManager物件的,構建SecurityManager環境。然後是由主體Subject提交認證請求,主體提交認證請求到SecurityManager認證,SecurityManager是使用的Authenticator做認證的,Authenticator做認證的時候通過Realm來獲取認證的資料,進而由Realm做最終的認證。
6、簡單測試Shiro的認證、認證過程。首先引入shiro的依賴包,pom.xml如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 5 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 6 <parent> 7 <artifactId>shiro</artifactId> 8 <groupId>com.bie</groupId> 9 <version>1.0-SNAPSHOT</version> 10 </parent> 11 <modelVersion>4.0.0</modelVersion> 12 13 <artifactId>shiro-test</artifactId> 14 15 <!-- 首先引入shiro的包 --> 16 <dependencies> 17 <!-- shiro的核心包 --> 18 <dependency> 19 <groupId>org.apache.shiro</groupId> 20 <artifactId>shiro-core</artifactId> 21 <version>1.2.2</version> 22 </dependency> 23 24 <!-- 單元測試junit --> 25 <dependency> 26 <groupId>junit</groupId> 27 <artifactId>junit</artifactId> 28 <version>4.12</version> 29 </dependency> 30 </dependencies> 31 32 </project>
認證的步驟,如下所示:
1 package com.bie.test; 2 3 import org.apache.shiro.SecurityUtils; 4 import org.apache.shiro.authc.UsernamePasswordToken; 5 import org.apache.shiro.mgt.DefaultSecurityManager; 6 import org.apache.shiro.realm.SimpleAccountRealm; 7 import org.apache.shiro.subject.Subject; 8 import org.junit.Before; 9 import org.junit.Test; 10 11 /** 12 * shiro認證測試 13 * 14 * @ProjectName: shiro 15 * @Package: com.bie.test 16 * @ClassName: AuthenticationTest 17 * @Author: biehl 18 * @Description: ${description} 19 * @Date: 2020/8/6 11:19 20 * @Version: 1.0 21 */ 22 public class AuthenticationTest { 23 24 // 建立一個簡單賬戶Realm 25 private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); 26 27 /** 28 * 測試執行之前執行該方法 29 */ 30 @Before 31 public void add() { 32 simpleAccountRealm.addAccount("admin", "123456"); 33 } 34 35 @Test 36 public void testAuthentication() { 37 // 1、認證第一步,構建SecurityManager環境 38 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); 39 // 將simpleAccountRealm設定到SecurityManager環境中 40 defaultSecurityManager.setRealm(simpleAccountRealm); 41 42 // 2、第二步,主體提交認證請求,使用SecurityUtils獲取到主體 43 // 設定SecurityManager環境 44 SecurityUtils.setSecurityManager(defaultSecurityManager); 45 Subject subject = SecurityUtils.getSubject(); 46 47 // 3、第三步,提交認證 48 UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); 49 subject.login(token); 50 51 // 4、第四步,Realm驗證 52 boolean authenticated = subject.isAuthenticated(); 53 System.out.println("isAuthenticated是否進行了認證:" + authenticated); 54 55 // 5、第五步,登陸之後可以進行退出 56 subject.logout(); 57 boolean authenticated2 = subject.isAuthenticated(); 58 System.out.println("isAuthenticated是否進行了認證:" + authenticated2); 59 60 } 61 62 }
專案結構和執行結果,如下所示:
7、Shiro的授權過程,如下所示:
實現程式碼,如下所示:
1 package com.bie.test; 2 3 import org.apache.shiro.SecurityUtils; 4 import org.apache.shiro.authc.UsernamePasswordToken; 5 import org.apache.shiro.mgt.DefaultSecurityManager; 6 import org.apache.shiro.realm.SimpleAccountRealm; 7 import org.apache.shiro.subject.Subject; 8 import org.junit.Before; 9 import org.junit.Test; 10 11 /** 12 * shiro認證測試 13 * 14 * @ProjectName: shiro 15 * @Package: com.bie.test 16 * @ClassName: AuthenticationTest 17 * @Author: biehl 18 * @Description: ${description} 19 * @Date: 2020/8/6 11:19 20 * @Version: 1.0 21 */ 22 public class AuthenticationTest { 23 24 // 建立一個簡單賬戶Realm 25 private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); 26 27 /** 28 * 測試執行之前執行該方法 29 */ 30 @Before 31 public void add() { 32 // 設定賬號密碼 33 // simpleAccountRealm.addAccount("admin", "123456"); 34 // 設定賬號密碼,角色(既是管理員角色、也是user普通使用者角色) 35 simpleAccountRealm.addAccount("admin", "123456", "admin", "user"); 36 } 37 38 @Test 39 public void testAuthentication() { 40 // 1、認證第一步,構建SecurityManager環境 41 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); 42 // 將simpleAccountRealm設定到SecurityManager環境中 43 defaultSecurityManager.setRealm(simpleAccountRealm); 44 45 // 2、第二步,主體提交認證請求,使用SecurityUtils獲取到主體 46 // 設定SecurityManager環境 47 SecurityUtils.setSecurityManager(defaultSecurityManager); 48 Subject subject = SecurityUtils.getSubject(); 49 50 // 3、第三步,提交認證 51 UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); 52 subject.login(token); 53 54 // 4、第四步,Realm驗證 55 boolean authenticated = subject.isAuthenticated(); 56 System.out.println("isAuthenticated是否進行了認證:" + authenticated); 57 58 // 5、第五步,認證結束開始檢查使用者是否具備此角色 59 subject.checkRole("admin"); 60 System.out.println("admin使用者具備該admin角色!"); 61 // checkRoles檢查當前的主體s是否具備引數裡面所有的角色資料 62 subject.checkRoles("admin", "user"); 63 System.out.println("admin使用者具備該admin、user角色!"); 64 } 65 66 }
8、Shiro內建的Realm,比如 IniRealm、JdbcRealm。
8.1、Shiro內建的IniRealm,可以配合.ini配置檔案來設定賬號,設定角色和賬號的繫結,設定許可權等功能。
1 package com.bie.realm; 2 3 import org.apache.shiro.SecurityUtils; 4 import org.apache.shiro.authc.UsernamePasswordToken; 5 import org.apache.shiro.mgt.DefaultSecurityManager; 6 import org.apache.shiro.realm.text.IniRealm; 7 import org.apache.shiro.subject.Subject; 8 import org.junit.Test; 9 10 /** 11 * @ProjectName: shiro 12 * @Package: com.bie.realm 13 * @ClassName: IniRealmTest 14 * @Author: biehl 15 * @Description: ${description} 16 * @Date: 2020/8/6 11:53 17 * @Version: 1.0 18 */ 19 public class IniRealmTest { 20 21 @Test 22 public void testAuthentication() { 23 // 0、建立一個IniRealm 24 IniRealm iniRealm = new IniRealm("classpath:user.ini"); 25 26 // 1、認證第一步,構建SecurityManager環境 27 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); 28 defaultSecurityManager.setRealm(iniRealm); 29 30 // 2、第二步,主體提交認證請求,使用SecurityUtils獲取到主體 31 // 設定SecurityManager環境 32 SecurityUtils.setSecurityManager(defaultSecurityManager); 33 Subject subject = SecurityUtils.getSubject(); 34 35 // 3、第三步,提交認證 36 UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); 37 subject.login(token); 38 39 // 4、第四步,Realm驗證 40 boolean authenticated = subject.isAuthenticated(); 41 System.out.println("isAuthenticated是否進行了認證:" + authenticated); 42 43 // 5、檢查角色,檢查管理員的角色,給使用者admin配備角色admin 44 subject.checkRole("admin"); 45 System.out.println("檢查管理員admin!"); 46 47 // 6、檢查許可權,檢查admin角色是否擁有使用者刪除的許可權 48 subject.checkPermission("user:delete"); 49 System.out.println("檢查管理員擁有使用者刪除user:delete的許可權!"); 50 51 subject.checkPermission("user:update"); 52 subject.checkPermission("user:select"); 53 subject.checkPermission("user:insert"); 54 System.out.println("檢查管理員擁有使用者刪除user:delete,user:update,user:select,user:insert的許可權!"); 55 } 56 57 }
user.ini配置檔案,如下所示:
1 [users] 2 admin=123456,admin 3 [roles] 4 admin=user:delete,user:update,user:select,user:insert
8.2、Shiro內建的JdbcRealm,如果使用預設的資料表結構,可以直接設定賬號,設定角色和賬號的繫結,設定許可權等功能。
1 <!-- mysql驅動包 --> 2 <dependency> 3 <groupId>mysql</groupId> 4 <artifactId>mysql-connector-java</artifactId> 5 <version>5.1.46</version> 6 </dependency> 7 <!-- 資料來源 --> 8 <dependency> 9 <groupId>com.alibaba</groupId> 10 <artifactId>druid</artifactId> 11 <version>1.1.18</version> 12 </dependency>
shiro預設使用的資料表結構,這裡新增了幾條資料,如下所示:
1 /* 2 Navicat Premium Data Transfer 3 4 Source Server : mysql_localhost 5 Source Server Type : MySQL 6 Source Server Version : 50720 7 Source Host : localhost:3306 8 Source Schema : test 9 10 Target Server Type : MySQL 11 Target Server Version : 50720 12 File Encoding : 65001 13 14 Date: 06/08/2020 14:42:35 15 */ 16 17 SET NAMES utf8mb4; 18 SET FOREIGN_KEY_CHECKS = 0; 19 20 -- ---------------------------- 21 -- Table structure for roles_permissions 22 -- ---------------------------- 23 DROP TABLE IF EXISTS `roles_permissions`; 24 CREATE TABLE `roles_permissions` ( 25 `id` int(11) NOT NULL AUTO_INCREMENT, 26 `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 27 `permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 28 PRIMARY KEY (`id`) USING BTREE 29 ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; 30 31 -- ---------------------------- 32 -- Records of roles_permissions 33 -- ---------------------------- 34 INSERT INTO `roles_permissions` VALUES (1, 'admin', 'user:select'); 35 INSERT INTO `roles_permissions` VALUES (2, 'admin', 'user:insert'); 36 INSERT INTO `roles_permissions` VALUES (3, 'admin', 'user:delete'); 37 INSERT INTO `roles_permissions` VALUES (4, 'admin', 'user:update'); 38 39 -- ---------------------------- 40 -- Table structure for user_roles 41 -- ---------------------------- 42 DROP TABLE IF EXISTS `user_roles`; 43 CREATE TABLE `user_roles` ( 44 `id` int(11) NOT NULL AUTO_INCREMENT, 45 `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 46 `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 47 PRIMARY KEY (`id`) USING BTREE 48 ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; 49 50 -- ---------------------------- 51 -- Records of user_roles 52 -- ---------------------------- 53 INSERT INTO `user_roles` VALUES (1, 'admin', 'admin'); 54 INSERT INTO `user_roles` VALUES (2, 'admin', 'user'); 55 56 -- ---------------------------- 57 -- Table structure for users 58 -- ---------------------------- 59 DROP TABLE IF EXISTS `users`; 60 CREATE TABLE `users` ( 61 `id` int(11) NOT NULL AUTO_INCREMENT, 62 `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 63 `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 64 PRIMARY KEY (`id`) USING BTREE 65 ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; 66 67 -- ---------------------------- 68 -- Records of users 69 -- ---------------------------- 70 INSERT INTO `users` VALUES (3, 'admin', '123456'); 71 INSERT INTO `users` VALUES (4, '張三', '123456'); 72 INSERT INTO `users` VALUES (5, '李四', '123456'); 73 74 SET FOREIGN_KEY_CHECKS = 1;
JdbcRealm的案例程式碼,如下所示:
1 package com.bie.realm; 2 3 import com.alibaba.druid.pool.DruidDataSource; 4 import org.apache.shiro.SecurityUtils; 5 import org.apache.shiro.authc.UsernamePasswordToken; 6 import org.apache.shiro.mgt.DefaultSecurityManager; 7 import org.apache.shiro.realm.jdbc.JdbcRealm; 8 import org.apache.shiro.subject.Subject; 9 import org.junit.Test; 10 11 /** 12 * @ProjectName: shiro 13 * @Package: com.bie.realm 14 * @ClassName: IniRealmTest 15 * @Author: biehl 16 * @Description: ${description} 17 * @Date: 2020/8/6 11:53 18 * @Version: 1.0 19 */ 20 public class JdbcRealmTest { 21 22 // 設定資料來源 23 DruidDataSource druidDataSource = new DruidDataSource(); 24 25 { 26 druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test"); 27 druidDataSource.setUsername("root"); 28 druidDataSource.setPassword("123456"); 29 } 30 31 32 @Test 33 public void testAuthentication() { 34 // 設定JdbcRealm 35 JdbcRealm jdbcRealm = new JdbcRealm(); 36 // 設定jdbc的資料來源 37 jdbcRealm.setDataSource(druidDataSource); 38 // 設定許可權的開關,預設是false,設定為true才可以查詢許可權資料的。 39 jdbcRealm.setPermissionsLookupEnabled(true); 40 41 // 1、認證第一步,構建SecurityManager環境 42 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); 43 // 設定Realm 44 defaultSecurityManager.setRealm(jdbcRealm); 45 46 // 2、第二步,主體提交認證請求,使用SecurityUtils獲取到主體 47 // 設定SecurityManager環境 48 SecurityUtils.setSecurityManager(defaultSecurityManager); 49 Subject subject = SecurityUtils.getSubject(); 50 51 // 3、第三步,提交認證 52 UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); 53 subject.login(token); 54 55 // 4、第四步,Realm驗證 56 boolean authenticated = subject.isAuthenticated(); 57 System.out.println("isAuthenticated是否進行了認證:" + authenticated); 58 59 // 5、檢查角色,檢查管理員的角色,給使用者admin配備角色admin 60 subject.checkRole("admin"); 61 System.out.println("檢查管理員admin!"); 62 subject.checkRoles("admin", "user"); 63 System.out.println("檢查管理員admin、普通使用者角色user!"); 64 65 // 6、檢查許可權,檢查admin角色是否擁有使用者刪除的許可權 66 subject.checkPermission("user:delete"); 67 System.out.println("檢查管理員擁有使用者刪除user:delete的許可權!"); 68 69 subject.checkPermission("user:update"); 70 subject.checkPermission("user:select"); 71 subject.checkPermission("user:insert"); 72 System.out.println("檢查管理員擁有使用者刪除user:delete,user:update,user:select,user:insert的許可權!"); 73 74 } 75 76 }
如果使用自定義使用者資訊表、角色資訊表、角色許可權表,例項程式碼,如下所示:
1 package com.bie.realm; 2 3 import com.alibaba.druid.pool.DruidDataSource; 4 import org.apache.shiro.SecurityUtils; 5 import org.apache.shiro.authc.UsernamePasswordToken; 6 import org.apache.shiro.mgt.DefaultSecurityManager; 7 import org.apache.shiro.realm.jdbc.JdbcRealm; 8 import org.apache.shiro.subject.Subject; 9 import org.junit.Test; 10 11 /** 12 * @ProjectName: shiro 13 * @Package: com.bie.realm 14 * @ClassName: IniRealmTest 15 * @Author: biehl 16 * @Description: ${description} 17 * @Date: 2020/8/6 11:53 18 * @Version: 1.0 19 */ 20 public class JdbcRealmTest { 21 22 // 設定資料來源 23 DruidDataSource druidDataSource = new DruidDataSource(); 24 25 { 26 druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test"); 27 druidDataSource.setUsername("root"); 28 druidDataSource.setPassword("123456"); 29 } 30 31 32 @Test 33 public void testAuthentication() { 34 // 設定JdbcRealm 35 JdbcRealm jdbcRealm = new JdbcRealm(); 36 // 設定jdbc的資料來源 37 jdbcRealm.setDataSource(druidDataSource); 38 // 設定許可權的開關,預設是false,設定為true才可以查詢許可權資料的。 39 jdbcRealm.setPermissionsLookupEnabled(true); 40 41 // 如果自定義資料表結構的時候 42 String userSql = "SELECT password from users_test WHERE name = ? "; 43 // 將認證的sql語句使用自己的sql語句 44 jdbcRealm.setAuthenticationQuery(userSql); 45 46 // 使用者角色的自定義表 47 String userRoleSql = "select role_name from user_roles_test where name = ? "; 48 jdbcRealm.setUserRolesQuery(userRoleSql); 49 50 // 角色許可權自定義表 51 String rolePermissionSql = "select permission from roles_permissions_test where name = ? "; 52 jdbcRealm.setPermissionsQuery(rolePermissionSql); 53 54 // 1、認證第一步,構建SecurityManager環境 55 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); 56 // 設定Realm 57 defaultSecurityManager.setRealm(jdbcRealm); 58 59 // 2、第二步,主體提交認證請求,使用SecurityUtils獲取到主體 60 // 設定SecurityManager環境 61 SecurityUtils.setSecurityManager(defaultSecurityManager); 62 Subject subject = SecurityUtils.getSubject(); 63 64 // 3、第三步,提交認證 65 UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); 66 subject.login(token); 67 68 69 // 4、第四步,Realm驗證 70 boolean authenticated = subject.isAuthenticated(); 71 System.out.println("isAuthenticated是否進行了認證:" + authenticated); 72 73 // 5、檢查角色,檢查管理員的角色,給使用者admin配備角色admin 74 subject.checkRole("admin"); 75 System.out.println("檢查管理員admin!"); 76 subject.checkRoles("admin", "user"); 77 System.out.println("檢查管理員admin、普通使用者角色user!"); 78 79 // 6、檢查許可權,檢查admin角色是否擁有使用者刪除的許可權 80 subject.checkPermission("user:delete"); 81 System.out.println("檢查管理員擁有使用者刪除user:delete的許可權!"); 82 83 subject.checkPermission("user:update"); 84 subject.checkPermission("user:select"); 85 subject.checkPermission("user:insert"); 86 System.out.println("檢查管理員擁有使用者刪除user:delete,user:update,user:select,user:insert的許可權!"); 87 88 } 89 90 }
8.3、Shiro自定義Realm,如下所示:
1 package com.bie.shiro; 2 3 import com.alibaba.druid.util.StringUtils; 4 import org.apache.shiro.authc.AuthenticationException; 5 import org.apache.shiro.authc.AuthenticationInfo; 6 import org.apache.shiro.authc.AuthenticationToken; 7 import org.apache.shiro.authc.SimpleAuthenticationInfo; 8 import org.apache.shiro.authz.AuthorizationInfo; 9 import org.apache.shiro.authz.SimpleAuthorizationInfo; 10 import org.apache.shiro.realm.AuthorizingRealm; 11 import org.apache.shiro.subject.PrincipalCollection; 12 13 import java.util.HashMap; 14 import java.util.HashSet; 15 import java.util.Map; 16 import java.util.Set; 17 18 /** 19 * 自定義Realm 20 * 21 * @ProjectName: shiro 22 * @Package: com.bie.shiro 23 * @ClassName: CustomRealm 24 * @Author: biehl 25 * @Description: ${description} 26 * @Date: 2020/8/6 14:58 27 * @Version: 1.0 28 */ 29 public class CustomRealm extends AuthorizingRealm { 30 31 // 模擬資料表的登陸 32 private Map<String, String> userMap = new HashMap<String, String>(); 33 34 { 35 userMap.put("admin", "123456"); 36 37 // 設定RealName名稱 38 super.setName("customRealm"); 39 } 40 41 /** 42 * 授權使用 43 * 44 * @param principals 45 * @return 46 */ 47 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 48 // 1、第一步,從認證資訊中獲取到使用者資訊。 49 String userName = (String) principals.getPrimaryPrincipal(); 50 // 2、第二步,從資料庫中或者快取中通過使用者姓名獲取到角色資訊 51 Set<String> roles = getRolesByUserName(userName); 52 // 3、第三步,從資料庫中或者快取中通過使用者姓名獲取到許可權資訊 53 Set<String> permissions = getPermissionsByUserName(userName); 54 // 4、第四步,將獲取到角色資訊和許可權資訊返回 55 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); 56 // 設定許可權 57 simpleAuthorizationInfo.setStringPermissions(permissions); 58 // 設定角色 59 simpleAuthorizationInfo.setRoles(roles); 60 return simpleAuthorizationInfo; 61 } 62 63 /** 64 * 通過使用者名稱獲取到許可權資訊 65 * 66 * @param userName 67 * @return 68 */ 69 private Set<String> getPermissionsByUserName(String userName) { 70 // 建立一個集合物件 71 Set<String> sets = new HashSet<String>(); 72 // 設定許可權名稱 73 sets.add("user:select"); 74 sets.add("user:insert"); 75 sets.add("user:delete"); 76 sets.add("user:update"); 77 return sets; 78 } 79 80 /** 81 * 模擬通過使用者姓名獲取到角色資訊 82 * 83 * @param userName 84 * @return 85 */ 86 private Set<String> getRolesByUserName(String userName) { 87 // 建立一個集合物件 88 Set<String> sets = new HashSet<String>(); 89 // 設定角色名稱 90 sets.add("admin"); 91 sets.add("user"); 92 return sets; 93 } 94 95 /** 96 * 認證使用 97 * 98 * @param token 主體傳過來的認證資訊 99 * @return 100 * @throws AuthenticationException 101 */ 102 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 103 // 1、第一步,通過主體傳過來的認證資訊獲取使用者名稱 104 String username = (String) token.getPrincipal(); 105 // 2、通過使用者名稱到資料庫中獲取憑證 106 String password = getPasswordByUserName(username); 107 // 判斷獲取到的密碼是否存在,不存在直接返回null 108 if (StringUtils.isEmpty(password)) { 109 return null; 110 } 111 // 如果物件存在 112 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("admin", password, "customRealm"); 113 return authenticationInfo; 114 } 115 116 /** 117 * 通過使用者姓名獲取到使用者的密碼 118 * 119 * @param username 120 * @return 121 */ 122 private String getPasswordByUserName(String username) { 123 // 正常情況下,需要讀取資料庫,這裡進行模擬 124 return this.userMap.get(username); 125 } 126 127 }
自定義Realm的單元測試,如下所示:
1 package com.bie.shiro; 2 3 import org.apache.shiro.SecurityUtils; 4 import org.apache.shiro.authc.UsernamePasswordToken; 5 import org.apache.shiro.mgt.DefaultSecurityManager; 6 import org.apache.shiro.subject.Subject; 7 import org.junit.Test; 8 9 /** 10 * @ProjectName: shiro 11 * @Package: com.bie.shiro 12 * @ClassName: CustomRealmTest 13 * @Author: biehl 14 * @Description: ${description} 15 * @Date: 2020/8/6 15:10 16 * @Version: 1.0 17 */ 18 public class CustomRealmTest { 19 20 @Test 21 public void testAuthentication() { 22 // 0、建立一個自定義Realm:CustomRealm 23 CustomRealm customRealm = new CustomRealm(); 24 25 // 1、認證第一步,構建SecurityManager環境 26 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); 27 defaultSecurityManager.setRealm(customRealm); 28 29 // 2、第二步,主體提交認證請求,使用SecurityUtils獲取到主體 30 // 設定SecurityManager環境 31 SecurityUtils.setSecurityManager(defaultSecurityManager); 32 Subject subject = SecurityUtils.getSubject(); 33 34 // 3、第三步,提交認證 35 UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); 36 subject.login(token); 37 38 // 4、第四步,Realm驗證 39 boolean authenticated = subject.isAuthenticated(); 40 System.out.println("isAuthenticated是否進行了認證:" + authenticated); 41 42 // 5、檢查角色,檢查管理員的角色,給使用者admin配備角色admin 43 subject.checkRole("admin"); 44 System.out.println("檢查管理員admin!"); 45 46 // 6、檢查許可權,檢查admin角色是否擁有使用者刪除的許可權 47 subject.checkPermission("user:delete"); 48 System.out.println("檢查管理員擁有使用者刪除user:delete的許可權!"); 49 50 subject.checkPermission("user:update"); 51 subject.checkPermission("user:select"); 52 subject.checkPermission("user:insert"); 53 System.out.println("檢查管理員擁有使用者刪除user:delete,user:update,user:select,user:insert的許可權!"); 54 } 55 56 }
9、Shiro加密,Shiro雜湊內建。
1)、HashedCredentialsMatcher。
2)、自定義Realm中使用雜湊。
3)、鹽的使用,單純的加密並不能滿足加密的要求,還需要進行加鹽slat,讓密碼更加難以識破。
1 package com.bie.shiro; 2 3 import com.alibaba.druid.util.StringUtils; 4 import org.apache.shiro.authc.AuthenticationException; 5 import org.apache.shiro.authc.AuthenticationInfo; 6 import org.apache.shiro.authc.AuthenticationToken; 7 import org.apache.shiro.authc.SimpleAuthenticationInfo; 8 import org.apache.shiro.authz.AuthorizationInfo; 9 import org.apache.shiro.authz.SimpleAuthorizationInfo; 10 import org.apache.shiro.crypto.hash.Md5Hash; 11 import org.apache.shiro.realm.AuthorizingRealm; 12 import org.apache.shiro.subject.PrincipalCollection; 13 import org.apache.shiro.util.ByteSource; 14 15 import java.util.HashMap; 16 import java.util.HashSet; 17 import java.util.Map; 18 import java.util.Set; 19 20 /** 21 * 自定義Realm 22 * 23 * @ProjectName: shiro 24 * @Package: com.bie.shiro 25 * @ClassName: CustomRealm 26 * @Author: biehl 27 * @Description: ${description} 28 * @Date: 2020/8/6 14:58 29 * @Version: 1.0 30 */ 31 public class CustomRealm extends AuthorizingRealm { 32 33 // 模擬資料表的登陸 34 private Map<String, String> userMap = new HashMap<String, String>(); 35 36 { 37 // userMap.put("admin", "123456"); 38 userMap.put("admin", "496edd8064892864b76c5fd3a732544b"); 39 40 // 設定RealName名稱 41 super.setName("customRealm"); 42 } 43 44 /** 45 * 授權使用 46 * 47 * @param principals 48 * @return 49 */ 50 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 51 // 1、第一步,從認證資訊中獲取到使用者資訊。 52 String userName = (String) principals.getPrimaryPrincipal(); 53 // 2、第二步,從資料庫中或者快取中通過使用者姓名獲取到角色資訊 54 Set<String> roles = getRolesByUserName(userName); 55 // 3、第三步,從資料庫中或者快取中通過使用者姓名獲取到許可權資訊 56 Set<String> permissions = getPermissionsByUserName(userName); 57 // 4、第四步,將獲取到角色資訊和許可權資訊返回 58 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); 59 // 設定許可權 60 simpleAuthorizationInfo.setStringPermissions(permissions); 61 // 設定角色 62 simpleAuthorizationInfo.setRoles(roles); 63 return simpleAuthorizationInfo; 64 } 65 66 /** 67 * 通過使用者名稱獲取到許可權資訊 68 * 69 * @param userName 70 * @return 71 */ 72 private Set<String> getPermissionsByUserName(String userName) { 73 // 建立一個集合物件 74 Set<String> sets = new HashSet<String>(); 75 // 設定許可權名稱 76 sets.add("user:select"); 77 sets.add("user:insert"); 78 sets.add("user:delete"); 79 sets.add("user:update"); 80 return sets; 81 } 82 83 /** 84 * 模擬通過使用者姓名獲取到角色資訊 85 * 86 * @param userName 87 * @return 88 */ 89 private Set<String> getRolesByUserName(String userName) { 90 // 建立一個集合物件 91 Set<String> sets = new HashSet<String>(); 92 // 設定角色名稱 93 sets.add("admin"); 94 sets.add("user"); 95 return sets; 96 } 97 98 /** 99 * 認證使用 100 * 101 * @param token 主體傳過來的認證資訊 102 * @return 103 * @throws AuthenticationException 104 */ 105 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 106 // 1、第一步,通過主體傳過來的認證資訊獲取使用者名稱 107 String username = (String) token.getPrincipal(); 108 // 2、通過使用者名稱到資料庫中獲取憑證 109 // 如果使用了shiro的HashedCredentialsMatcher加密,那麼這裡儲存的是加密後的密文 110 String password = getPasswordByUserName(username); 111 // 判斷獲取到的密碼是否存在,不存在直接返回null 112 if (StringUtils.isEmpty(password)) { 113 return null; 114 } 115 // 如果物件存在 116 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("admin", password, "customRealm"); 117 118 // 如果加了鹽salt,認證返回的時候需要見鹽salt設定進去的 119 authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("654321")); 120 // 將加了鹽的認證使用者返回 121 return authenticationInfo; 122 } 123 124 /** 125 * 通過使用者姓名獲取到使用者的密碼 126 * 127 * @param username 128 * @return 129 */ 130 private String getPasswordByUserName(String username) { 131 // 正常情況下,需要讀取資料庫,這裡進行模擬 132 return this.userMap.get(username); 133 } 134 135 public static void main(String[] args) { 136 // 加鹽salt讓密碼更加難以識破 137 Md5Hash md5Hash = new Md5Hash("123456","654321"); 138 System.out.println(md5Hash); 139 } 140 141 }
自定義Realm的單元測試,如下所示:
1 package com.bie.shiro; 2 3 import org.apache.shiro.SecurityUtils; 4 import org.apache.shiro.authc.UsernamePasswordToken; 5 import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 6 import org.apache.shiro.mgt.DefaultSecurityManager; 7 import org.apache.shiro.subject.Subject; 8 import org.junit.Test; 9 10 /** 11 * @ProjectName: shiro 12 * @Package: com.bie.shiro 13 * @ClassName: CustomRealmTest 14 * @Author: biehl 15 * @Description: ${description} 16 * @Date: 2020/8/6 15:10 17 * @Version: 1.0 18 */ 19 public class CustomRealmTest { 20 21 @Test 22 public void testAuthentication() { 23 // 0、建立一個自定義Realm:CustomRealm 24 CustomRealm customRealm = new CustomRealm(); 25 26 // shiro加密 27 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); 28 // 設定加密方式為md5 29 hashedCredentialsMatcher.setHashAlgorithmName("md5"); 30 // 設定加密的次數 31 hashedCredentialsMatcher.setHashIterations(1); 32 33 // 設定自定義Realm的HashedCredentialsMatcher物件 34 customRealm.setCredentialsMatcher(hashedCredentialsMatcher); 35 36 37 // 1、認證第一步,構建SecurityManager環境 38 DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); 39 defaultSecurityManager.setRealm(customRealm); 40 41 // 2、第二步,主體提交認證請求,使用SecurityUtils獲取到主體 42 // 設定SecurityManager環境 43 SecurityUtils.setSecurityManager(defaultSecurityManager); 44 Subject subject = SecurityUtils.getSubject(); 45 46 // 3、第三步,提交認證 47 UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); 48 subject.login(token); 49 50 // 4、第四步,Realm驗證 51 boolean authenticated = subject.isAuthenticated(); 52 System.out.println("isAuthenticated是否進行了認證:" + authenticated); 53 54 // 5、檢查角色,檢查管理員的角色,給使用者admin配備角色admin 55 subject.checkRole("admin"); 56 System.out.println("檢查管理員admin!"); 57 58 // 6、檢查許可權,檢查admin角色是否擁有使用者刪除的許可權 59 subject.checkPermission("user:delete"); 60 System.out.println("檢查管理員擁有使用者刪除user:delete的許可權!"); 61 62 subject.checkPermission("user:update"); 63 subject.checkPermission("user:select"); 64 subject.checkPermission("user:insert"); 65 System.out.println("檢查管理員擁有使用者刪除user:delete,user:update,user:select,user:insert的許可權!"); 66 } 67 68 }