• shiro-第一篇-基本介绍及使用


    shiro

    概述

    shior的话,在第一次听说的时候单纯的任务它就是一个安全框架,可以对访问接口的用户进行验证等工作,类似拦截器或过滤器的东西,但是在学习后,发现远远不止这些,它的灵活性和易用性让我震惊,这么好的东西早就应该发现的呀!!!

    特性

    ​ 既然说了它这么好,让我们来看看它到底好在哪里:

    易于使用:使用 Shiro 构建系统安全框架非常简单。就算第一次接触也可以快速掌握。

    全面:Shiro 包含系统安全框架需要的功能,满足安全需求的“一站式服务”。

    灵活:Shiro 可以在任何应用程序环境中工作。虽然它可以在 Web、EJB 和 IoC 环境 中工作,但不需要依赖它们。Shiro 也没有强制要求任何规范,甚至没有很多依赖项。

    强力支持 Web:Shiro 具有出色的 Web 应用程序支持,可以基于应用程序 URL 和 Web 协议(例如 REST)创建灵活的安全策略,同时还提供一组 JSP 库来控制页面输出。

    兼容性强:Shiro 的设计模式使其易于与其他框架和应用程序集成。Shiro 与 Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin 等框架无缝集成。

    看那么多字是不是都很恶心,总结起来就一个字,好用!

    spirng security

    ​ 其实提到安全框架的话,还有一个耳熟能详的框架,就是spring系列的spring security,那这俩有什么区别呢?

    1. spring security是基于spring的,对spring有一定的依赖性,不过当前的话,大部分互联网项目都是基于spring的,所以这个也不能算缺点
    2. spring security功能比shiro更丰富一些,例如安全维护方面
    3. spring security的使用比起shiro来较为复杂

    学习方式

    ​ 在我看来学习任何东西只要掌握其原理和运行流程,其实编码也就好说了,最多也就看看源码之类的东西,知道哪个类是干嘛的,都🆗的,而学习这些东西最好的地方就是官网啦,这里是shiro的官网,里面有更加详尽的介绍及使用,如果有什么问题,里面应该都有解决方案

    learning

    基本功能及简介

    ​ 下面就来看看shiro的基本功能吧,以图的方式来展示(摘自apache shiro官网):
    在这里插入图片描述

    功能介绍:

    1. Authentication:身份认证/登录,验证用户是不是拥有相应的身份
    2. Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限
    3. Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
    4. Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储
    5. Web Support:Web 支持,可以非常容易的集成到 Web 环境
    6. Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率
    7. Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
    8. Testing:提供测试支持
    9. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
    10. Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

    架构概述

    ​ 在最高概念层面上,Shiro的架构有3个主要概念:Subject、SecurityManager和Realms。下图是这些组件如何交互的高级概述(摘自apache shiro官网):
    在这里插入图片描述

    组件介绍:

    1. Subject:这里通常指与软件交互的任何东西,通常指用户

    2. SecurityManager:shiro架构的核心,所有和安全相关的操作都会与之交互,它管理所有的subject,类似于springmvc中的DispatcherServlet

    3. Realms:充当的是shiro和应用程序安全数据之间的桥梁,当需要实际与安全相关的数据(如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro 会从为应用程序配置的一个或多个 Realm 中查找其中的许多内容

      简单来说,可以把Realm看作是一个一个的特定的dao,它封装了数据源的连接细节,并根据需要提供数据给shiro,可以配置多个,但是至少提供一个Realm

    详细架构

    ​ 下图显示了 Shiro 的核心架构概念以及每个概念的简短摘要(摘自apache shiro官网):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-moiBv4d1-1669008965948)(assets/ShiroArchitecture.png)]

    • Subject( org.apache.shiro.subject.Subject ) 当前与软件交互的实体(用户、第 3 方服务、cron 作业等)的特定于安全的“视图”。
    • SecurityManager ( org.apache.shiro.mgt.SecurityManager ) 主要是用来协调其他组件顺利工作的
    • Authenticator ( org.apache.shiro.authc.Authenticator ) 负责Subject认证,可以自定义实现
      • Authentication Strategy ( org.apache.shiro.authc.pam.AuthenticationStrategy )是身份认证策略,当定义了多个Realm,它就可以去使用指定的策略去进行认证
    • Authorizer ( org.apache.shiro.authz.Authorizer )负责访问控制,是最终决定是否允许用户做某事的机制
    • SessionManager ( org.apache.shiro.session.mgt.SessionManager )负责管理session生命周期,不仅可以用在web环境
      • SessionDAO ( org.apache.shiro.session.mgt.eis.SessionDAO ) 负责代表SessionManager执行增删改查操作,这允许任何数据插入会话管理基础结构
    • CacheManager ( org.apache.shiro.cache.CacheManager )负责创建和管理其他shiro组件使用的缓存实例的生命周期
    • Cryptography ( org.apache.shiro.crypto.* )的中文释义是密码学,很自然的可以想到,安全框架肯定会包含加密模块,在数据方面进行加密
    • Realm( org.apache.shiro.realm.Realm )就没什么好说的了,和上面的意思一样,充当的是shiro和应用程序安全数据之间的桥梁

    术语

    • 身份:验证身份是验证用户身份的过程

    • 授权:也称为访问控制,是确定用户是否被允许做某件事的过程

    • 凭证:是验证用户身份的一条信息,只有用户自己知道

    • 权限:至少在 Shiro 的解释中,权限是一种描述应用程序中原始功能的声明,仅此而已。权限是安全策略中最低级别的结构。它们仅定义应用程序可以做什么。他们没有描述“谁”能够执行这些操作。许可只是一种行为声明,仅此而已。

    • 主体(Subject):访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只 有授权 后才允许访问相应的资源。

    • 角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。

    基本使用

    登录认证

    流程:

    1. 收集用户身份和凭证,即用户名和密码
    2. 调用subject.login进行登录,如果失败则报异常AuthenticationException
    3. 创建自定义的Realm类,继承org.apache.shiro.realm.AuthenticatingRealm类,实现doGetAuthenticationInfo()方法

    身份认证

    流程:

    1. 首先调用 Subject.login(token) 进行登录,其会自动委托给 SecurityManager
    2. SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证
    3. Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现
    4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证
    5. Authenticator 会把相应的 token 传入 Realm,从 Realm 获取 身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处 可以配置多个Realm,将按照相应的顺序 及策略进行访问

    角色授权

    流程:

    1. 首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager,而 SecurityManager接着会委托给 Authorizer
    2. Authorizer是真正的授权者,如果调用如isPermitted(“user:view”),其首先会让PermissionResolver把字符串转换成相应的Permission实例
    3. 在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限
    4. Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted/hasRole 会返回 true,否则返回false表示授权失败

    实例

    • pom文件
    <dependencies>
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-coreartifactId>
            <version>1.9.0version>
        dependency>
        <dependency>
            <groupId>commons-logginggroupId>
            <artifactId>commons-loggingartifactId>
            <version>1.2version>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ​ shiro获取权限相关信息可以通过数据库获取,也可以通过shiro.ini文件获取,此处使用后者实现基本演示

    • shiro.ini
    # 提供了对根对象 securityManager 及其依赖的配置
    [main]
    md5CredentialsMatcher = org.apache.shiro.authc.credential.Md5CredentialsMatcher
    md5CredentialsMatcher.hashIterations = 3
    myrealm = gyl.top.MyRealm
    myrealm.credentialsMatcher = $md5CredentialsMatcher
    securityManager.realms = $myrealm
    # 提供了对用户/密码及其角色的配置,用户名=密码,角色 1,角色 2
    [users]
    zhangsan = 7174f64b13022acd3c56e2781e098a5f,role1,role2
    lisi = l4
    # 提供了角色及权限之间关系的配置,角色=权限 1,权限 2
    [roles]
    role1 = user:insert,user:select
    # 这里还有一种叫urls,不过这里并未配置,它主要用来对url拦截相关的配置,
    # 想更多了解shiro.ini配置文件
    # 可以参考https://blog.csdn.net/u011781521/article/details/74892074
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 自定义的Realm:这里其实使用了shiro加密的认证方式,在下面的小节中会提到
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.realm.AuthenticatingRealm;
    import org.apache.shiro.util.ByteSource;
    
    /**
     * @author GYL
     * @version V1.0
     */
    public class MyRealm extends AuthenticatingRealm {
    
        /**
         * 自定义登录认证方法,shiro的login方法底层会调用该类的认证方法进行认证
         * 需要配置自定义的realm生效,在ini文件中可以配置,或者在springboot中进行配置
         * 该方法只是获取进行对比的信息,认证逻辑还是安装shiro底层认证逻辑完成
         *
         * @param authenticationToken :获取登录信息
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            // 获取身份信息
            String principal = authenticationToken.getPrincipal().toString();
            // 获取凭证信息
            String password = authenticationToken.getCredentials().toString();
            System.out.println("认证信息:" + principal);
            System.out.println("凭证信息:" + password);
            // 访问数据库,获取数据库中存储的数据信息
            if ("zhangsan".equals(principal)) {
                // 数据库中存储着加盐三次迭代的密码
                String passwordInfo = "7174f64b13022acd3c56e2781e098a5f";
                // 创建封装校验逻辑的对象,把数据封装进去,然后返回
                return new SimpleAuthenticationInfo(
                        authenticationToken.getPrincipal(),
                        passwordInfo,
                        ByteSource.Util.bytes("salt"),
                        principal
                );
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 测试类
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    
    /**
     * @author GYL
     * @version V1.0
     */
    public class ShiroTest {
        public static void main(String[] args) {
            // 初始化获取SecurityManager
            IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager manager = factory.getInstance();
            SecurityUtils.setSecurityManager(manager);
            // 获取Subject对象
            Subject subject = SecurityUtils.getSubject();
            // 创建token对象,web应用用户名密码从页面传递
            AuthenticationToken token = new UsernamePasswordToken("zhangsan", "z3");
            // 调用login方法进行登录认证
            try {
                subject.login(token);
                System.out.println("登录成功");
                if (subject.hasRole("role1")) {
                    System.out.println("有相关角色");
                }
                if (subject.isPermitted("user:insert")) {
                    System.out.println("有相关权限");
                }
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                System.out.println("用户不存在");
            } catch (IncorrectCredentialsException e) {
                e.printStackTrace();
                System.out.println("密码错误");
            } catch (AuthenticationException ae) {
                //unexpected condition? error?
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    shiro加密

    ​ 在开发中,常会对一些敏感数据进行加密,比如用户的密码等信息

    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.crypto.hash.SimpleHash;
    
    /**
     * @author GYL
     * @version V1.0
     */
    public class ShiroMd5 {
        public static void main(String[] args) {
            // 密码明文:
            String password = "z3";
            // 使用md5加密
            Md5Hash md5Hash = new Md5Hash(password);
            System.out.println(md5Hash);
            // 带盐的md5加密,盐就是在密码明文后拼接新字符串,然后再进行加密
            Md5Hash md5Hash1 = new Md5Hash(password, "salt");
            System.out.println(md5Hash1);
            // 为了保证安全,避免被破解还可以多次迭代加密,保证数据安全
            Md5Hash md5Hash2 = new Md5Hash(password, "salt", 3);
            System.out.println(md5Hash2);
            // 使用父类实现加密
            SimpleHash simpleHash = new SimpleHash("MD5", password, "salt", 3);
            System.out.println(simpleHash);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 有关shiro的基本介绍和使用就到这里,本文章参考了尚硅谷shiro讲解及shiro官方文档

    每一点滴的进展,都是缓慢而艰苦的

  • 相关阅读:
    华资软件一面
    【考研】数据结构考点——直接插入排序
    前端Sortable拖拽实现排序
    sky walking日志采集以及注意事项
    八股文面经整理
    CLIP 基础模型介绍寄论文讲解
    CISP-PTE之SQL注入(二次注入的应用)
    SpringCloud整合Nacos
    《深入浅出.NET框架设计与实现》阅读笔记(一)
    【经验分享】运用云服务器实现挂机手机网课的操作,部分手机软件适用
  • 原文地址:https://blog.csdn.net/weixin_44351616/article/details/127963005