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 26 7 11shiro 8com.bie 91.0-SNAPSHOT 104.0.0 12 13shiro-test 14 15 1617 18 31 3219 23 24 25org.apache.shiro 20shiro-core 211.2.2 2226 30junit 27junit 284.12 29
认证的步骤,如下所示:
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 23 7 8mysql 4mysql-connector-java 55.1.46 69 com.alibaba 10druid 111.1.18 12
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 MapuserMap = new HashMap (); 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 roles = getRolesByUserName(userName); 52 // 3、第三步,从数据库中或者缓存中通过用户姓名获取到权限信息 53 Set 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 getPermissionsByUserName(String userName) { 70 // 创建一个集合对象 71 Set sets = new HashSet (); 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 getRolesByUserName(String userName) { 87 // 创建一个集合对象 88 Set sets = new HashSet (); 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 MapuserMap = new HashMap (); 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 roles = getRolesByUserName(userName); 55 // 3、第三步,从数据库中或者缓存中通过用户姓名获取到权限信息 56 Set 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 getPermissionsByUserName(String userName) { 73 // 创建一个集合对象 74 Set sets = new HashSet (); 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 getRolesByUserName(String userName) { 90 // 创建一个集合对象 91 Set sets = new HashSet (); 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 }