zoukankan      html  css  js  c++  java
  • Shrio02 Realm作用、自定义简洁Realm、Realm实现类使用

    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相关依赖

            <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文件用于存放用户信息

    》项目目录结构

    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.6 PropertiesRealm使用教程

    参见2.5 + 百度 

    2.7 JdbcRealm使用教程

    》继承关系图

    》引入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');
    View Code

    》测试类

    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()));
    
    
        }
    }

  • 相关阅读:
    python实例
    date命令
    unbuntu禁用ipv6
    Oracle学习(一)
    深入浅出区块链笔记
    sqlserver索引
    Go学习(16):网络编程
    Go学习(15):并发与包
    Go学习(14):defer
    Go学习(13):异常
  • 原文地址:https://www.cnblogs.com/NeverCtrl-C/p/10296238.html
Copyright © 2011-2022 走看看