shiro详解,看这一篇差不多理解了shiro的基础了

本文详细介绍了Apache Shiro框架,包括其简介、功能、运行原理和核心组件。通过一个小Demo演示了Shiro的认证和授权过程,并展示了在SpringBoot中集成Shiro的步骤,包括导入依赖、编写配置文件和实现登录拦截与用户认证。文章还探讨了Realm的认证与授权功能,以及如何自定义Realm以适应数据库认证。最后,文章提供了在实际项目中进行权限管理的实践建议。
摘要由CSDN通过智能技术生成

shiro学习

简介

​ 在以往的权限管理中,我们的权限管理通常是有以下几个步骤: 1.创建用户,分配权限。 2.用户登录,权限拦截器拦截请求,识别当前用户登录信息 3.从权限表中判断是否拥有权限
​ 从以上步骤中可以提取到以下三个问题。 三个问题: > 1.如何让Shiro拦截请求。 > 在web开发中,Shiro会提供一个拦截器来对请求进行拦截。 > > 2.Shiro如何判断发起请求用户的身份? > 在web开发中,会借助session来判断,如果禁用了session,那么可能需要重写一些方法。 > > 3.如何判断权限? > Shiro使用realm来判断权限。
下面的也将以这三个问题为中心来描述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:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

(同类的比较知名的安全框架还有spring security,Shiro的优点是比较简洁,功能虽然比不上Spring Security多样,但对于安全需求不多的时候可以使用Shiro。)

运行原理

image-20200723115228779

  • Application Code:代表着应用,应用使用Subject来标识自己的身份,以及使用Subject来进行认证和授权。

  • Subject:代表着“当前用户”。这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等。所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager。我们可以把 Subject 认为是一个门面,SecurityManager 才是实际的执行者。

    包含 Principals 和 Credentials 两个信息。我们看下两者的具体含义。

    Principals:代表身份。可以是用户名、邮件、手机号码等等,用来标识一个登录主体的身份。

    Credentials:代表凭证。常见的有密码,数字证书等等。

    说白了,两者代表了需要认证的内容,最常见的便是用户名、密码了。比如用户登录时,通过 Shiro 进行身份认证,其中就包括主体认证。

  • Shiro SecurityManager:应用使用Subject来进行认证和授权,实际上执行认证和授权的是SecurityManager。安全管理器。即所有与安全有关的操作都会与 SecurityManager 交互,且它管理着所有 Subject。可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,我们可以把它看成 DispatcherServlet 前端控制器。

  • Realm:SecurityManager的认证和授权需要使用Realm,Realm负责获取用户的权限和角色等信息,再返回给SecurityManager来进行判断。

image-20200723115831108

  1. Subject:主体,可以看到主体可以是任何与应用交互的“用户”。

  2. SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher。它是 Shiro 的核心,所有具体的交互都通过 SecurityManager 进行控制。它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

  3. Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,我们可以自定义实现。其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了。

  4. Authrizer:授权器,或者访问控制器。它用来决定主体是否有权限进行相应的操作,即控制着用户能访问应用中的哪些功能。

  5. Realm:可以有1个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的。它可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等。

  6. SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 需要有人去管理它的生命周期,这个组件就是 SessionManager。而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境。

  7. SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD。我们可以自定义 SessionDAO 的实现,控制 session 存储的位置。如通过 JDBC 写到数据库或通过 jedis 写入 redis 中。另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能。

  8. CacheManager:缓存管理器。它来管理如用户、角色、权限等的缓存的。因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能。

  9. Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密的。

小demo

  • shiro.ini

    # -----------------------------------------------------------------------------
    # users用来定义用户
    [users]
    # 用户名 = 密码,角色1,角色2...
    admin = secret, admin
    guest = guest, guest
    aa = 123456, guest
    # -----------------------------------------------------------------------------
    # roles用来定义角色
    [roles]
    # 角色 = 权限 (* 代表所有权限)
    admin = *
    # 角色 = 权限 (* 代表所有权限)
    guest = see
    aa = see
    
  • shiroDemo.java

    package com.demo;
    
    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;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class ShiroDemo {
         
        private static final Logger log = LoggerFactory.getLogger(ShiroDemo.class);
        public static void main(String[] args) {
         
            //1.创建SecurityManagerFactory
            IniSecurityManagerFactory factory =
                    new IniSecurityManagerFactory("classpath:shiro.ini");
            //2.获取SecurityManager,绑定到SecurityUtils中
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            //3.获取一个用户识别信息
            Subject currentUser = SecurityUtils.getSubject();
            //4.判断是否已经身份验证
            if (!currentUser.isAuthenticated()) {
         
                // 4.1把用户名和密码封装为 UsernamePasswordToken 对象
                UsernamePasswordToken token = new UsernamePasswordToken("guest", "guest");
                // 4.2设置rememberme
                token.setRememberMe(true);
                try {
         
                    // 4.3登录.
                    currentUser.login(token);
                }
                catch (UnknownAccountException uae) {
          //用户不存在异常
                    log.info("****---->用户名不存在: " + token.getPrincipal());
                    return;
                }
                catch (IncorrectCredentialsException ice) {
         // 密码不匹配异常
                    log.info("****---->" + token.getPrincipal() + " 的密码错误!");
                    return;
                }
                catch (LockedAccountException lae) {
         // 用户被锁定
                    log.info("****---->用户 " + token.getPrincipal() + " 已被锁定");
                }
                catch (AuthenticationException ae) {
          // 其他异常,认证异常的父类
                    log.info("****---->用户" + token.getPrincipal() + " 验证发生异常");
                }
            }
    
            // 5.权限测试:
            //5.1判断用户是否有某个角色
            if (currentUser.hasRole("guest")) {
         
                log.info("****---->用户拥有角色guest!");
            } else {
         
                log.info("****---->用户没有拥有角色guest");
                return;
            }
            //5.2判断用户是否执行某个操作的权限
            if (currentUser.isPermitted("see")) {
         
                log.info("****----> 用户拥有执行此功能的权限");
            } else {
         
                log.info("****---->用户没有拥有执行此功能的权限");
            }
    
            //6.退出
            System.out.println("****---->" + currentUser.isAuthenticated());
            currentUser.logout();
            System.out.println("****---->" + currentUser.isAuthenticated());
    
        }
    }
    

    解析一下上面的代码做了什么:

    对于shiro.ini:

    1.在[users]标签下以用户名 = 密码,角色1,角色2...的格式创建了用户
    2.在[roles]标签下以角色 = 权限 (* 代表所有权限)的格式为用户分配了角色

    对于ShiroDemo.java:

    1.使用shiro.ini来获取了IniSecurityManagerFactory
    2.通过IniSecurityManagerFactory获取SecurityManager,并绑定到SecurityUtils中
    3.使用SecurityUtils获取一个用户识别信息Subject
    4.对Subject对象判断是否已经身份验证(Authenticated)
    5.将用户名和密码封装成UsernamePasswordToken对象,调用Subject对象的login方法来进行登录
    6.登录成功后,调用Subject对象的hasRole方法来判断用户是否拥有某个角色
    7.调用Subject对象的isPermitted方法来判断用户是否拥有某个行为
    8.调用Subject对象的logout方法来退出。

Realm

  • Realm是真正负责处理认证和授权的组件。
  • SecurityManager要完成认证,需要Realm返回一个AuthenticationInfo,AuthenticationInfo会携带存储起来的正确的用户认证信息,用来与用户提交的信息进行比对,如果信息不匹配,那么会认证失败。
  • SecurityManager要完成授权,需要Realm返回一个AuthorizationInfo

认证

下面的例子是以继承了AuthenticatingRealm的自定义Realm来实现自定义认证。
认证依赖于方法doGetAuthenticationInfo,需要返回一个AuthenticationInfo,通常返回一个他的子类SimpleAuthenticationInfo,构造方法的第一个参数是用户名,第二个是验证密码,第三个是当前realm的className。

package com.demo.realms;

import org.apache.shiro.authc.*;
import org.apache.shiro.realm.AuthenticatingRealm;

public class MyRealm extends AuthenticatingRealm {
   
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)  {
   
        System.out.println("MyRealm认证中---->用户:"+token.getPrincipal());
        // 可以从token中获取用户名来从数据库中查询数据
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String password="123456";// 假设这是从数据库中查询到的用户密码
        // 创建一个SimpleAuthenticationInfo,第一个参数是用户名,第二个是验证密码,第三个是当前realm的className
        // 验证密码会与用户提交的密码进行比对
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(upToken.getUsername(),password,this.getName());
        return info;
    }
}

当创建了一个Realm之后,需要告诉SecurityManager,所以在shiro.ini中配置:

# -------------------------------------------------------------------
[main]
myRealm = com.demo.realms.MyRealm
# --------由于自定义认证,所以去除users,roles------------------------

这样子就可以进行自定义认证了,在上面的用户密码中都设置了"123456",所以如果输入的密码不正确都会认证失败。但上面没有设置授权,所以代码中要去掉授权的判断.

授权

下面的例子是以继承了AuthorizingRealm的自定义Realm来实现自定义认证和自定义授权。
授权依赖于方法doGetAuthorizationInfo,需要返回一个AuthorizationInfo,通常返回一个他的子类SimpleAuthorizationInfo。构造SimpleAuthorizationInfo可以空构造,也可以传入一个Set<String> roles来构造。

package com.demo.realms
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值