1 Realm简介
1.1 Realm作用
shiro最终是通过Realm获取安全数据的(如用户、角色、权限),也就是说认证或者授权都会通过Realm进行数据操作
1.2 Realm接口
1.2.1 源代码
1.2.2 方法说明
》getName:返回一个唯一的 Realm 名字 》supports:判断此 Realm 是否支持此 Token 》getAuthenticationInfo:根据 Token 获取认证信息,该方法就是用来实现认证逻辑的(从Realm的实现类org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo中可以看出)1.3 AuthenticationToken
》层级关系
》关系图
》开发时一般将用户名和密码封装成一个UsernamePasswordToken对象
1.4 注意
》supports需要对Token类型进行判断,判断实参类型是否满足条件;这里指定的是AuthenticationToken类型(任何Token类型都可以传入,因为AuthenticationToken是一个父接口),所以在实现类中只需要判断实参是否是指定的Token类型即可(实际开发时传入的实参一般都是UsernamePasswordToken类型,所以在supports方法中只需要判定实参是否是这个类型即可)。
》实现认证的逻辑就是写在org.apache.shiro.realm.Realm#getAuthenticationInfo这个方法中的(从Realm的实现类org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo中可以看出)1.5 简易自定义Realm
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 单元测试类
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())); }}
2 Realm源码分析
Realm接口主要是认证的主接口,但是它的一些实现类既可以进行认证也可以进行授权逻辑
2.1 Realm相关类层次图
todo: 贴图
2.2 Realm相关类说明
》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;2.3 AuthenticatingRealm详解
》getAuthenticationInfo:该方法是Realm中getAuthenticationInfo的实现,该方法是实现认证逻辑的;该方法是一个final方法,所以AuthenticatingRealm的子类不能重写该方法;
》doGetAuthenticationInfo:getAuthenticationInfo方法调用doGetAuthenticationInfo实现认证逻辑,该方法是一个protected方法,专门暴露给子类进行重写的,而且是子类实现认证逻辑必须重写的方法。2.4 Realm主要实现类
2.4.1 IniRealm
》用户相关信息存储在一个ini文件中2.4.2 PropertiesRealm》用户相关信息存储在一个properties文件中2.4.3 JdbcRealm》用户信息存储在数据库中2.5 IniRealm使用教程
2.5.1 继承关系图
2.5.2 创建一个maven工程并引入shiro、junit相关依赖
org.apache.shiro shiro-core 1.4.0 junit junit 4.12
2.5.3 在resources目录下创建ini文件用于存放用户信息
》项目目录结构
》
https://blog.csdn.net/u011781521/article/details/74892074
》ini文件内容
[users]fury=111111,role1,role2zeus=222222,role1[roles]role1=user:delete,user:update,user:create,user:readrole2=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())); ArrayListroleList = new ArrayList (); roleList.add("role1"); System.out.println("是否有role1角色:" + Arrays.toString(subject.hasRoles(roleList))); System.out.println("是否有role1角色:" + subject.hasRole("role1")); subject.checkPermission("user:create"); }}
2.6 PropertiesRealm使用教程
参见2.5 + 百度
2.7 JdbcRealm使用教程
》继承关系图
》引入shiro、junit、mysql、druid依赖
org.apache.shiro shiro-core 1.4.0 junit junit 4.12 mysql mysql-connector-java 5.1.45 com.alibaba druid 1.1.6
》根据JdbcRealm源码创建相关表的SQL
/*Navicat MySQL Data TransferSource Server : mysql5.4Source Server Version : 50540Source Host : localhost:3306Source Database : shiroTarget Server Type : MYSQLTarget Server Version : 50540File Encoding : 65001Date: 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())); }}