• 10.1 Shiro(简介)


    一、Shiro介绍

    Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大灵活的开源安全框架,提供认证、授权、会话管理以及密码加密等功能,由Java语言开发。

    Shiro可以做到:

    1. 验证用户身份
    2. 对用户进行访问控制(配置文件中配置拦截器,对请求地址进行拦截)
    3. 判断某个用户是否被赋予某个特定角色
    4. 判断某个用户是否被允许执行某些操作
    5. 可以在各种环境下使用Session API,即使不在web或EJB容器中
    6. 对认证、访问控制火灾会话生命周期中的事件进行响应处理

    二、Shiro功能

    2.1Apache Shiro 框架提供了很多功能,下图展示了Shiro的着重点

    Shiro 主要面向 Shiro开发团队所谓的“应用安全的四大基础”——认证,授权,会话管理与密码加密

    • **认证:**或“登录”,用以验证用户身份。
    • **授权:**访问控制,比如决定谁可以访问某些资源
    • **会话管理:**管理用户相关的Session,即使是在非Web或EJB应用中。
    • **加密:**可以非常方便地使用(各种算法)加密算法来保证数据的安全。

    2.2 Shiro中的几个重要概念

    Shiro 的框架主要有三个顶级概念:Subject,SecurityManager 和 Realms

    **Subject:**实际是安全邻域里的“当前执行用户”。Subject 可以是一个人,或者是第三方服务,守护进程账户,定时任务等等——可以是基本上任何与软件交互的事务。Subject的实例都会(也是必须)绑定一个 SecurityManager,对 Subject的操作会转换为 Subject 与 SecurityManager的交互。

    **SecurityManager:**Shiro架构核心,协调内部组件运作。配置完成后,开发者只需要使用 Subject。

    **Realm:**在 Shiro 和你的安全数据之间扮演“桥梁”或“连接器”的角色。用于用户账号的认证和授权。

    *进行认证或授权时,Shiro会从应用配置的一个或多个Realms中来查找。这个意义上Realm其实就是安全(操作)特有的DAO:它封装了数据源的细节,并在Shiro需要时提供相关数据。配置Shiro时,必须至少得有一个Realm以用于认证(和/或)授权。在SecurityManager中可以配置Realm,至少得有一个。Shiro自带一些开箱即用的Realms用于连接像是LDAP,关系数据库(JDBC),文本配置源(比如INI)以及属性文件等等这样的安全数据来源。如果默认的不能满足你的需要,你也可以自己实现一个Realm。

    2.3 Shiro 的核心架构

    **Subject:**安全视角下与软件交互的实体(用户、第三方等等)

    SecurityManager:安全管理器协调内部各组件的作用;同时管理 Shiro 的所有用户,对用户进行相关的安全操作

    Authenticator:认证器 结合Realm完成认证操作

    Authorizer:授权器结合Realm完成授权操作

    CacheManager:缓存管理器 缓存权限数据

    Realm:域

    SessionManager:会话管理器 管理Session

    CrypotoGraphy:加密用于密码加密


    三、Shiro 认证

    3.1即身份验证:

    在 Shiro 中,用户需要提供 principalsCredentials给 Shiro 进行验证。

    principals :身份(用户名/手机号等)

    Credentials:证明/凭证(密码)

    认证需要用到两个相关的概念是SubjectRealm,分别是主体及验证主体的数据源。

    【认证流程】
    主体调用Subject.login()该方法后,由Realm查询数据源进行校验,把校验的结果给
    Authenticator,由认证器分析完成认证操作:
    如果身份错误,抛出UnknownAccountException;
    如果凭证错误,抛出IncorrectCredentialException

    认证时 Realm有三种方式:下面的 Realm 都是测试,未查询数据库,用 ini 文件配置代替数据源

    1. 默认 Realm 无法支持加密功能
    2. 自定义 Realm
    3. 密码加密,凭证匹配器

    3.2环境搭建(用于JavaEE工程,进行测试;Maven-Web工程则只需要导入Shiro相关的依赖进行了,包含了这些依赖)

    <%--在pomp文件中导入依赖--%>
    
        commons-logging
        commons-logging
        1.1.3
    
    
        org.apache.shiro
        shiro-core
        1.4.1
    
    
        junit
        junit
        4.9
        provided
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.3默认 Realm 无需创建Realm类

    此处使用 ini 配置文件,通过 [users] 指定了两个主体

    配置文件 shiro-authenticate.ini 需要在 idea 中下载该插件(类似油桶,下载后要重启软件)

    [users]
    zhangsan=123
    lisi=lisi
    
    • 1
    • 2
    • 3

    测试代码

    //测试默认的Realm完成认证
        @Test
        public void test01(){
            //读取配置文件,生成工厂对象
            Factory factory =
                    new IniSecurityManagerFactory("classpath:autenticate/shiro-authenticate.ini");
            //从工厂中取出SecurityManager:单例
            SecurityManager securityManager = factory.getInstance();
    
            //把securityManager设置环境变量中 等同于把securityManager放入到了一个工具类中,方便获取securityManager中
            //各个组件对象的
            SecurityUtils.setSecurityManager(securityManager);
    
            //获取Sebject主体
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","1111");
            try {
                subject.login(token);
            } catch (UnknownAccountException e) {
                System.out.println("身份错误");
            }catch (IncorrectCredentialsException e1){
                System.out.println("凭证错误");
            }
        }
    
    • 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

    3.4自定义 Realm

    自定义 Realm 需要实现或者继承框架中的 realm 类

    配置文件 shiro-realm.ini

    #声明一个realm,获取Realm路径
    myRealm= com.bjpowernode.shiro.MyRealm
    #指定securityManager的realms实现,即将Realm注入到安全管理器里
    securityManager.realms=$myRealm
    
    • 1
    • 2
    • 3
    • 4

    自定义Realm:

    自定义Realm认证流程:先校验身份, 身份没有问题后,再校验凭证

    /**
     * AuthenticatingRealm:认证的Realm
     * AuthorizingRealm:授权的Realm
     */
    public class MyRealm extends AuthorizingRealm {
    
        //认证的功能
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //先校验身份
            //获取用户输入的身份
            String principal = (String) authenticationToken.getPrincipal();
    
            //拿着用户输入的身份,去数据库查询,过程.... where username=principal
            String correctPrincipal = "abc";
    
            if(correctPrincipal == null){
                throw new UnknownAccountException();
            }
    
            //再校验凭证
            //从查询出来的身份中取出凭证
            String credential = "admin";
            SimpleAuthenticationInfo simpleAuthenticationInfo =
                    new SimpleAuthenticationInfo(principal, credential, "myRealm");
            return simpleAuthenticationInfo;
        }
    
        //授权
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            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

    测试代码

     //自定义的Realm来完成认证
        @Test
        public void test02(){
            //读取配置文件,生成工厂对象
            Factory factory =
                    new IniSecurityManagerFactory("classpath:autenticate/shiro-realm.ini");
            //从工厂中取出SecurityManager:单例
            SecurityManager securityManager = factory.getInstance();
    
            //把securityManager设置环境变量中 等同于把securityManager放入到了一个工具类中,方便获取securityManager中
            //各个组件对象的
            SecurityUtils.setSecurityManager(securityManager);
    
            //获取Sebject主体
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","admin");
            subject.login(token);
    
            //判断用户是否认证成功
            boolean authenticated = subject.isAuthenticated();
    
            System.out.println(authenticated);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.5自定义 realm,加密完成验证

    散列算法

    一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如 MD5、SHA 等。一般进行散列时最好提供一个 salt(盐),比如加密密码 “admin”,产生的散列值是 21232f297a57a5a743894a0e4a801fc3”,可以到一些 md5 解密网站很容易的通过散列值得到密码 “admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和 ID(即盐);这样散列的对象是 “密码 + 用户名 +ID”,这样生成的散列值相对来说更难破解。

    配置文件 shiro-md5.ini

    #凭证匹配器  整合Spring框架,实际就是个bean对象
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #散列算法  加密的方法MD5
    credentialsMatcher.hashAlgorithmName=md5
    #散列次数  即加密次数
    credentialsMatcher.hashIterations=1
    #声明一个realm
    md5Realm= com.bjpowernode.shiro.Md5Reaml
    #将散列设置到当前reaml中  把凭证匹配器注入到Realm中
    md5Realm.credentialsMatcher=$credentialsMatcher
    #指定securityManager的realms实现
    securityManager.realms=$md5Realm
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    自定义Realm

    public class Md5Realm extends AuthorizingRealm {
    
        //认证
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
            //先校验身份
            //获取用户输入的身份
            String principal = (String) authenticationToken.getPrincipal();
    
            //拿着用户输入的身份,去数据库查询,过程.... where username=principal
            String correctPrincipal = "";
    
            if(correctPrincipal == null){
                throw new UnknownAccountException();
            }
    
            //再校验凭证
            //从查询出来的身份中取出凭证,加密之后的密码
            String credential = "f6fdffe48c908deb0f4c3bd36c032e72";
            String salt = "admin";
            SimpleAuthenticationInfo simpleAuthenticationInfo =
                    new SimpleAuthenticationInfo
                            (principal, credential, ByteSource.Util.bytes(salt),"myRealm");
            return simpleAuthenticationInfo;
        }
    
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            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

    测试代码

      //自定义的Realm来完成加密认证
        @Test
        public void test04(){
            //读取配置文件,生成工厂对象
            Factory factory =
                    new IniSecurityManagerFactory("classpath:autenticate/shiro-md5.ini");
            //从工厂中取出SecurityManager:单例
            SecurityManager securityManager = factory.getInstance();
    
            //把securityManager设置环境变量中 等同于把securityManager放入到了一个工具类中,方便获取securityManager中
            //各个组件对象的
            SecurityUtils.setSecurityManager(securityManager);
    
            //获取Sebject主体
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123");
            subject.login(token);
    
            //判断用户是否认证成功
            boolean authenticated = subject.isAuthenticated();
    
            System.out.println(authenticated);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    四、Shiro 授权

    4.1授权概念

    授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

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

    • 资源 在应用中用户可以访问的URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

    • 权限 安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面 查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)打印文档等。如上可以看出,权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)

    • 粗粒度:小红以部门经理登陆到系统中,能操作用户模块的所有资源

    4.2授权的三种方式

    1、编程式:通过写 If/else 授权代码块完成

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) {
        //有权限
    } else {
        //无权限
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、注解式:通过在执行的 Java 方法上放置相应的注解完成

    @RequiresPermissions("admin")
    public void hello() {
        //有权限
    }
    
    • 1
    • 2
    • 3
    • 4

    3、JSP/GSP 标签:在 JSP/GSP 页面通过相应的标签完成

    
    
    
    
    • 1
    • 2
    • 3

    编码式:

    授权代码:

     //授权
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //先根据用户的身份查询用户的所有权限
            Object primaryPrincipal = principalCollection.getPrimaryPrincipal();
    
           //查询数据库省略 List
         //   List permissions = new ArrayList();
            //添加权限标识符
          //  permissions.add("user:*");
    
            //定义一个集合,存储用户对应的角色
            List roles = new ArrayList();
            roles.add("role1");
            roles.add("role2");
    
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addRoles(roles);
            return simpleAuthorizationInfo;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试类

     @Test
        public void test01(){
            Factory factory =
                    new IniSecurityManagerFactory("classpath:authorize/shiro-authorize.ini");
    
            SecurityManager securityManager = factory.getInstance();
    
            SecurityUtils.setSecurityManager(securityManager);
    
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("zhangsan","123" );
    
            subject.login(usernamePasswordToken);
    
            boolean authenticated = subject.isAuthenticated();
    
            System.out.println(authenticated);
    
    
    //        subject.checkPermissions("user:query");
            boolean has = subject.hasRole("role3");
    
            System.out.println(has);
        }
    
    • 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
  • 相关阅读:
    计算机网络知识笔记
    5.系统设计的工作内容与技能工具有哪些?
    MySQL中explain的用法
    小程序社区团购demo
    中国工业地产市场竞争格局与前景战略建议报告2022-2028年新版
    JVM内存模型
    【JDK 8-集合框架】5.4 allMatch 和 anyMatch 函数
    无穷级数(数学一)
    PyOpenGL轮子文件whl格式所有版本下载
    LED灯实验
  • 原文地址:https://blog.csdn.net/m0_54853420/article/details/126325331