安全认证框架Shiro入门学习(shiro概述和shiro入门小案例);后续整合SpringBoot,应用程序安全;

news2024/11/25 21:23:59

权限概述

什么是权限

  • 什么是权限

    权限管理,一般指根据系统设置的安全策略或者安全规则,用户可以访问而且只能访问自己被授权的资源,不多不少。权限管理几乎出现在任何系统里面,只要有用户和密码的系统。

  • 权限管理再系统中一般分为

    访问权限

    一般表示你能做什么样的操作,或者能够访问那些资源。例如:给张三赋予“店铺主管”角色,“店铺主管”具有“查询员工”、“添加员工”、“修改员工”和“删除员工”权限。此时张三能够进入系统,则可以进行这些操作
    

    数据权限

    一般表示某些数据你是否属于你,或者属于你可以操作范围。例如:张三是"店铺主管"角色,他可以看他手下客服人员所有的服务的买家订单信息,他的手下只能看自己负责的订单信息
    

认证概念

  • 认证:

    ​ 身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和密码,看其是否与系统中存储的该用户的用户名和密码一致,来判断用户身份是否正确。例如:密码登录,手机短信验证、三方授权等

  • 认证流程

    在这里插入图片描述

  • 关键对象

    上边的流程图中需要理解以下关键对象

    Subject:主体:访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;

    Principal:身份信息是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)

    credential:凭证信息:是只有主体自己知道的安全信息,如密码、证书等。

授权概念

  • 什么是授权

    授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后,系统会为其分配对应的权限,当访问资源时,会校验其是否有访问此资源的权限

    用户对象user:当前操作的用户、程序

    资源对象resource:当前被访问的对象

    角色对象role :一组 “权限操作许可权” 的集合

    权限对象permission:权限操作许可权

  • 授权流程

    在这里插入图片描述

Shrio概述

shiro简介

  • 什么是shiro

    Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

  • shiro的特点

    Shiro 是一个强大而灵活的开源安全框架,能够非常清晰的处理认证、授权、管理会话以及密码加密。如下是它所具有的特点:

    • 易于理解的 Java Security API;
    • 简单的身份认证(登录),支持多种数据源(LDAP,JDBC 等);
    • 对角色的简单的签权(访问控制),也支持细粒度的鉴权
    • 支持一级缓存,以提升应用程序的性能
    • 内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境
    • 异构客户端会话访问
    • 非常简单的加密 API
    • 不跟任何的框架或者容器捆绑,可以独立运行

核心组件

Shiro架构图

在这里插入图片描述

  1. Subject

    Subject主体,外部应用与subject进行交互,subject将用户作为当前操作的主体,这个主体:可以是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

  2. SecurityManager

    SecurityManager权限管理器,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口

  3. Authenticator

    Authenticator即认证器,对用户登录时进行身份认证

  4. Authorizer

    Authorizer授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

  5. Realm==(数据库读取+认证功能+授权功能实现)==

    比如:

    ​ 如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

    注意

    ​ 不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

  6. SessionManager

    SessionManager会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

  7. SessionDAO

    SessionDAO即会话dao,是对session会话操作的一套接口

    比如:

    ​ 可以通过jdbc将会话存储到数据库

    ​ 也可以把session存储到缓存服务器

  8. CacheManager

    CacheManager缓存管理,将用户权限数据存储在缓存,这样可以提高性能

  9. Cryptography

    Cryptography密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能

Shiro入门

身份认证

身份认证基本流程

在这里插入图片描述

  1. Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息
  2. 使用Subject门面获取到封装着用户的数据的标识token
  3. Subject把标识token交给SecurityManager,在SecurityManager安全中心中,SecurityManager把标识token委托给认证器Authenticator进行身份验证。认证器的作用一般是用来指定如何验证,它规定本次认证用到哪些Realm
  4. 认证器Authenticator将传入的标识token,与数据源Realm对比,验证token是否合法

代码实现身份认证:

  1. 导入pom.xml文件坐标

    <!--shiro-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    
  2. 编写配置文件shiro-test.ini

    [users]
    #模拟从数据库查询用户
    #数据格式 用户名=密码
    liuls=654321
    wangls=123456
    
  3. 编写代码

    1. 根据配置文件创建SecurityManagerFactory
    2. 通过工厂获取SecurityManager
    3. 将SecurityManager绑定到当前运行环境
    4. 从当前运行环境中构造subject
    5. 构造shiro登录的数据
    6. 主体登陆
    @Test
    public void testLogin() {
      //1.根据配置文件创建SecurityManagerFactory
      Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-test-1.ini");
      //2.通过工厂获取SecurityManager
      SecurityManager securityManager = factory.getInstance();
      //3.将SecurityManager绑定到当前运行环境
      SecurityUtils.setSecurityManager(securityManager);
      //4.从当前运行环境中构造subject
      Subject subject = SecurityUtils.getSubject();
      //5.构造shiro登录的数据
      String username = "liuls";
      String password = "65432";
      UsernamePasswordToken token = new UsernamePasswordToken(username,password);
      //6.主体登陆
      subject.login(token);
      //7.验证用户是否登录成功
      System.out.println("用户是否登录成功="+subject.isAuthenticated());
    }
    

    此处构造shiro登录的数据若和配置文件中写的相同,是否登陆成功显示true,否则false

★★★Realm★★★

在这里插入图片描述

Reaml:顶级接口

CachingRealm:带有缓存的Reaml

AuthenticatingRealm:继承CachingRealm,支持认证;能缓存能认证

AuthorizingRealm:继承AuthenticatingRealm,支持授权;能缓存能认证能授权

Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源

自定义realms对象–>继承AuthorizingRealm–>重新方法:

doGetAuthorizationInfo:授权;获取到用户的授权数据(用户的权限数据)

doGetAuthenticationInfo:认证;根据用户名密码登录,将用户数据保存(安全数据)

逻辑:用户输入账号密码,构造令牌token,通过realm查找,realm中一个方法做认证一个方法做授权

代码实现:

  1. pom文件:

    <!--shiro-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    
  2. MyRealm:

    创建MyRealm继承AuthorizingRealm;

    doGetAuthorizationInfo方法:做授权

    doGetAuthoricationInfo方法:做认证

    public class MyRealm extends AuthorizingRealm {
      //授权:授权的主要目的就是根据认证数据获取到用户的权限信息
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权方法");
        //1.获取安全数据  username,用户id
        String username = (String) principalCollection.getPrimaryPrincipal();
        //2.通过认证传递的安全数据,去数据库查询角色以及权限,实现授权
        List<String> perms = new ArrayList<String>();
        perms.add("user:save");
        perms.add("user:update");
        perms.add("user:delete");
        perms.add("user:find");
        List<String> roles = new ArrayList<String>();
        roles.add("role1");
        roles.add("role2");
        // 构造返回值
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 设置角色和权限集合
        info.addStringPermissions(perms);
        info.addRoles(roles);
        return info;
      }
      //认证:认证的主要目的,比较用户名和密码是否与数据库中的一致
      //将安全数据存入到shiro进行保管
      //参数:authenticationToken登录构造的usernamepasswordtoken
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证方法");
        //1.构造uptoken
        // subject.loogin传递过来的参数是UsernamePasswordToken类型,
        // 是AuthenticationToken的一个子类,向下类型转化一下拿到构造的用户令牌
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; 	 		//2.获取输入的用户名密码
        String username = token.getUsername();
        String password = new String(token.getPassword());
    		//3.根据用户名查询数据库,正式系统查询
        //4.比较密码和数据库中的密码是否一致
        if("123456".equals(password) && username.equals("dongjianxiang")){
          //5.如果成功,向shiro存入安全数据
          SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password,"MyRealm");
          return info;
        }else{
          //6.失败,抛出异常或返回null
          throw new RuntimeException("账号密码错误");
        }
      }
    }
    
  3. 配置文件shiro-test.ini:

    配置加载MyReaml

    [main]
    #声明Realm
    myClass=com.dong.shiro.MyRealm
    #注册realm到securityManager中
    securityManager.realms=$myClass
    
  4. 测试类:

    @Test
    public void testLogin() {
      //1.根据配置文件创建SecurityManagerFactory
      Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-test.ini");
      //2.通过工厂获取SecurityManager
      SecurityManager securityManager = factory.getInstance();
      //3.将SecurityManager绑定到当前运行环境
      SecurityUtils.setSecurityManager(securityManager);
      Subject subject = SecurityUtils.getSubject();
      String username = "liuls";
      String password = "654321";
      UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    
      //执行login-->realm域中的认证方法
      subject.login(token);
      System.out.println(subject.isAuthenticated());
      //鉴权
      System.out.println(subject.hasRole("role1"));//true
      System.out.println(subject.hasRole("role2"));//true
      System.out.println(subject.isPermitted("user:save"));//true
      System.out.println(subject.isPermitted("user:update"));//true
      System.out.println(subject.isPermitted("user:find"));//true
    }
    

编码与解码

  1. Shiro提供了base64和16进制字符串编码/解码的API支持,方便一些编码解码操作
  2. Shiro内部的一些数据的【存储/表示】都使用了base64和16进制字符串

代码实现:

  1. 导入pom.xml文件

    <!--shiro-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    
  2. 实现编码与解码

    import org.apache.shiro.codec.Base64;
    import org.apache.shiro.codec.Hex;
    public class EncodesUtil {
        // 16进制编码
        public static String encodeHex(byte[] input){
            return Hex.encodeToString(input);
        }
        // 16进制解码
        public static byte[] decodeHex(String input){
            return Hex.decode(input);
        }
        // base64编码
        public static String encodeBase64(byte[] input){
            return Base64.encodeToString(input);
        }
        // base64解码
        public static byte[] decodeBase64(String input){
            return Base64.decode(input);
        }
    }
    
  3. 测试

    public class ClientTest {
        // 16进制编码测试
        @Test
        public void testHex(){
            String val = "apache";
            String falg = EncodesUtil.encodeHex(val.getBytes());
            String valHandler = new String(EncodesUtil.decodeHex(falg));
            System.out.println("编码内容"+falg);
            System.out.println("解码内容"+valHandler);
            System.out.println("比较结果:"+val.equals(valHandler));
        }
        // base64 编码测试
        @Test
        public void testBase64(){
            String val= "shiro";
            String falg = EncodesUtil.encodeBase64(val.getBytes());
            String valHandler = new String(EncodesUtil.decodeBase64(falg));
            System.out.println("编码内容"+falg);
            System.out.println("解码内容"+valHandler);
            System.out.println("比较结果:"+val.equals(valHandler));
        }
    }
    

    16进制和base64编码再解码后的内容与编码前的值相同,比较结果返回true

散列算法

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

Md2Hash、Md5Hash、Sha1Hash、Sha256Hash、Sha384Hash、Sha512Hash

散列算法主要用于密码的加密和解密

散列算法密码加密

代码实现:

  1. pom文件到坐标

    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.3</version>
    </dependency>
    
  2. 加密工具类:

    public class DigestsUtil {
    
        private static final String SHA1="SHA-1";  // 加密的方式(加密算法)
    
        private static final Integer COUNTS=369;   // 加密的次数
        
      	// 获取salt混淆字符串(固定方法)
        public static String generateSalt(){
              SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
              return randomNumberGenerator.nextBytes().toHex();
          }
      
    		// SimpleHash方法生成密文密码,需要四个参数,加密方式,输入的明文密码
      	// salt混淆字符串,加密次数
        public static String show(String input ,String salt){
            return new SimpleHash(SHA1,input,salt,COUNTS).toString();
        }
    	
      	// 模拟存数据,以便测试查看
        public static Map<String,String> entryptPassword(String passwordPlain){
            Map<String,String> map = new HashMap<String, String>();
            String salt = generateSalt();
            String password = show(passwordPlain,salt);
            map.put("salt",salt);
            map.put("password(明文):",passwordPlain);
            map.put("password(密文)",password);
            return map;
        }
    }
    
  3. 测试:

    public class Test01 {
        @Test
        public void testDigestsUtil(){
            Map<String, String> map = DigestsUtil.entryptPassword("123666");
            System.out.println("结果:"+map.toString());
        }
    }
    

    结果:{password(密文)=8ec65316412c6b8725730bf6ac8c73a1cf27f3ce, salt=b8317a553b2b64ce5c78144d1bacaf7f, password(明文):=123666}

加密解密整合案例

实现步骤:

  1. pom文件:

    <!--shiro-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    
  2. 加密工具类:

    模拟演示,数据写死

    public class DigestsUtil {
      public static final String SHA1= "SHA-1";
      public static final Integer COUNTS = 369;
    
      // 获取salt字符串
      public static String generateSalt(){
        SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
        return randomNumberGenerator.nextBytes().toHex();
      }
    
      //  加密得到密文密码
      public static String show(String input,String salt){
        return new SimpleHash(SHA1,input,salt,COUNTS).toString();
      }
    
      // 生成用户信息(账号,密文密码,salt混淆字符串)
      public static Map<String,String> entryptPassword(String passwordPlain){
        HashMap<String, String> map = new HashMap<String, String>();
        String salt = generateSalt();
        String password = show(passwordPlain, salt);
        map.put("name","刘老板");
        map.put("salt",salt);
        map.put("password",password);
        return map;
      }
    }
    
  3. MyRealm:

    public class MyRealm extends AuthorizingRealm {
      //告诉shiro使用的散列算法
      public MyRealm() {
        //指定密码匹配方式sha1
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
        //指定密码迭代此时
        hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);
        //使用父层方法是匹配方式生效
        setCredentialsMatcher(hashedCredentialsMatcher);
      }
        
      // 认证方法
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取登录名
        //1.构造uptoken
        UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
        //2.获取输入的用户名密码
        String username = upToken.getUsername();
        //3.通过用户名称查询数据库相关信息
        SecurityService securityService = new SecurityServiceImpl();//创建业务对象
        //4.调用业务方法(按照用户名称查询用户相关信息)
        Map<String, String> map = securityService.findPasswordByLoginName(username);
        if(map == null){
          throw  new UnknownAccountException("账户不存在");
        }
        String salt = map.get("salt");
        String password = map.get("password");//匿名密码
        //参数1:安全数据   参数2:密码   参数3:混淆字符串  参数4:realm名称
        return new SimpleAuthenticationInfo(username,password, ByteSource.Util.bytes(salt),"myRealm");
        // 参数1:安全数据写什么,未来授权方法就能拿到什么
        // 参数2:现在认证需要给shiro密文密码
        // 参数3:shiro需要知道混淆字符串
        
      }
      
      // 授权方法
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    }
    
  4. 配置Realm

    [main]
    # 配置realm
    myRealm=com.dong.realm.MyRealm
    securityManager.rea lms=$myRealm
    
  5. 模拟一个认证业务

    接口:

    public interface IService {
      public Map<String,String> findPasswordByLoginName(String loginName);
    }
    

    实现类:

    public class ServiceImp implements IService {
      public Map<String, String> findPasswordByLoginName(String loginName) {
        if(loginName.equals("刘老板")){
          Map<String, String> map = DigestsUtil.entryptPassword("123456");
          return map;
        }
        return  null;
      }
    }
    
  6. juint测试:

    public class Test01 {
        @Test
        public void shiroLogin(){
            // 导入ini配置创建工厂
            IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-test-1.ini");
            // 工厂构建安全管理器
            SecurityManager instance = factory.getInstance();
            // 使用工具生效安全管理器
            SecurityUtils.setSecurityManager(instance);
            // 使用工具获取subject主体
            Subject subject = SecurityUtils.getSubject();
            // 构建账号密码
            UsernamePasswordToken token = new UsernamePasswordToken("刘老板", "123456");
            // 使用subject主体登录
            subject.login(token);
    
            // 打印登录信息
            System.out.println("登陆结果:"+subject.isAuthenticated());
        }
    }
    

    加密数据时数据写死了用户叫"刘老板",密码123456,如果登录信息匹配则登录结果为true,否则false

身份授权

在这里插入图片描述

  1. 首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager
  2. SecurityManager接着会委托给内部组件Authorizer
  3. Authorizer再将其请求委托给我们的Realm去做;Realm才是真正干活的
  4. Realm将用户请求的参数封装成权限对象。再从我们重写的doGetAuthorizationInfo方法中获取从数据库中查询到的权限集合
  5. Realm将用户传入的权限对象,与从数据库中查出来的权限对象,进行一一对比。如果用户传入的权限对象在从数据库中查出来的权限对象中,则返回true,否则返回false

进行授权操作的前提:用户必须通过认证。

代码实现:

  1. pom文件导坐标

    <!--shiro-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    
  2. 配置类shiro-test.ini

    [users]
    #用户信息
    huichenyi=123456,role2
    dongjianxiang=654321,role1,role2
    
    [roles]
    # 角色
    # 角色名=权限列表
    role1=user:save,user:update,user:delete
    role2=user:find
    
  3. juint单元测试:

    public class ShiroTest02 {
      @Test
      public void testShiro2(){
        //1.根据配置文件创建SecurityManagerFactory
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-test-2.ini");
        //2.通过工厂获取SecurityManager
        SecurityManager securityManager = factory.getInstance();
        //3.将SecurityManager绑定到当前运行环境
        SecurityUtils.setSecurityManager(securityManager);
    
        Subject subject = SecurityUtils.getSubject();
        String username = "liuls";
        String password = "654321";
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        //登录成功之后,完成授权
        subject.login(token);
        //开始鉴权
        System.out.println(subject.hasRole("role1"));//false
        System.out.println(subject.hasRole("role2"));//true
        System.out.println(subject.isPermitted("user:save"));//false
        System.out.println(subject.isPermitted("user:find"));//true
    }
    

    先授权,再鉴权

    shiro鉴权逻辑:登录成功后,先不授权鉴权,当访问资源,需要权限时,shiro会先查权限授权,再鉴权,判断是否具有访问权

Shiro默认过滤器

Shiro内置了很多默认的过滤器,比如身份验证、授权等相关的。默认过滤器可以参考

org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举过滤器

  • 认证相关过滤器

在这里插入图片描述

  • 授权相关

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1186579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小米6安装Ubuntu Touch系统也不是很难嘛

序言 这个文章是用来解说,小米6如何安装Ubuntu Touch系统 正文 安装这个系统需要注意的几点 1.手机必须已经解BL锁 2.没了 安装步骤 先双击打开压缩包查看,按照第一步第二步来进行执行,下面是解压图片 第一步 1.打开第一个文件夹 复制刷入rec的命令.txt里面的内容,然后打开红…

pytorch(小土堆)深度学习

第五节课讲项目的创建和对比 第六节&#xff1a;Dataset,Dataloader Dataset提供一种方式区获取数据及其label(如何获取每一个数据及其label&#xff0c;告诉我们总共有多少的数据) Dataloader为后面的网络提供不同的数据形式 第七节&#xff1a;Dataset类代码实战 显示图片 f…

挑战100天 AI In LeetCode Day05(热题+面试经典150题)

挑战100天 AI In LeetCode Day05&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-72.1 题目2.2 题解 三、面试经典 150 题-73.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&am…

开源的全能维护 U 盘工具:Ventoy

开源的全能维护 U 盘工具&#xff1a;Ventoy 本篇文章聊聊迄今为止&#xff0c;我用着最舒服的一款开源 U 盘启动工具&#xff0c;Ventoy。 写在前面 好久不见&#xff0c;接下来计划写一个比较连续的内容&#xff0c;就先从最小的处着手吧。 经过长久的折腾&#xff0c;除…

Docker本地镜像发布到阿里云或私有库

本地镜像发布到阿里云流程 &#xff1a; 1.自己生成个要传的镜像 2.将本地镜像推送到阿里云: 阿里云开发者平台:开放云原生应用-云原生&#xff08;Cloud Native&#xff09;-云原生介绍 - 阿里云 2.1.创建仓库镜像&#xff1a; 2.1.1 选择控制台&#xff0c;进入容器镜像服…

Makefile 总述

目录 一、Makefile 里有什么&#xff1f; 1、显式规则 2、隐晦规则 3、变量的定义 4、文件指示 5、注释 二、Makefile 的文件名 三、引用其它的 Makefile 四、环境变量 MAKEFILES 五、make 的工作方式 一、Makefile 里有什么&#xff1f; Makefile 里主要包含了五个东…

Ps:图层蒙版的基本操作

点击图层蒙版缩览图选中图层蒙版之后&#xff0c;方可进行图层蒙版的操作。 反相蒙版 Invert 将图层蒙版上的白色转换为黑色&#xff0c;黑色转换为白色。 方法一&#xff1a; Ps菜单&#xff1a;图像/调整/反相 Adjustments/Invert 方法二&#xff1a; 快捷键&#xff1a;Ctrl…

window10单机部署hbase-2.5.5-hadoop3

一、介绍 hbase是什么&#xff0c;Hbase是一个分布式&#xff0c;可扩展&#xff0c;支持海量数据存储的noSQL数据库 二、下载hbase https://mirrors.tuna.tsinghua.edu.cn/apache/hbase/2.5.6/ 三、配置hbase环境变量 三、修改hbase配置文件 在hbase-env.cmd添加如下配置…

【算法-链表2】反转链表 和 两两交换链表节点

今天&#xff0c;带来链表相关算法的讲解。文中不足错漏之处望请斧正&#xff01; 理论基础点这里 反转链表 1. 思路 链表操作的本质是修改连接关系&#xff0c;本题我们需要反转链表&#xff0c;也就是每次都让当前节点的next指向自己的上一个。而题目给的是单链表&#xf…

Linux tail命令:显示文件结尾的内容

tail 命令和 head 命令正好相反&#xff0c;它用来查看文件末尾的数据&#xff0c;其基本格式如下&#xff1a; [rootlocalhost ~]# tail [选项] 文件名 此命令常用的选项及含义 【例 1】查看 /etc/passwd 文件最后 3 行的数据内容。 [rootlocalhost ~]# tail -n 3 /etc/passwd…

jmeter接口自动化部署jenkins教程

首先&#xff0c;保证本地安装并部署了jenkins&#xff0c;jmeter&#xff0c;xslproc 我搭建的自动化测试框架是jmeterjenkinsxslproc ---注意&#xff1a;原理是&#xff0c;jmeter自生成的报告jtl文件&#xff0c;通过xslproc工具&#xff0c;再结合jmeter自带的模板修改&…

Linux - 进程程序替换 - C/C++ 如何实现与各个语言之间的相互调用 - 替换环境变量

前言 我们之前利用 fork&#xff08;&#xff09;函数来创建子进程&#xff0c;这种方式是 父子进程 共用一个代码&#xff0c;只是在代码当中使用了 if-else 语句来分流&#xff0c;达到父子进程运行不同的代码块的目的。但是其实本质上&#xff0c;还是父子共用一个代码和数…

C进阶-编译环境与预处理

本章重点&#xff1a; 程序的翻译环境 程序的执行环境 详解&#xff1a;C语言程序的编译链接 预定义符号介绍 预处理指令#define 宏和函数的对比 预处理操作符#和##的介绍 命令定义 预处理指令#include 预处理指令#undef 条件编译 1. 程序的翻译环境和执行环境 在ANSI C的任何一…

基层医院、民营医院、二级医院his系统源码,云计算技术B/S架构

在我国&#xff0c;基层医院和民营医院在总体数量上占据很大比例&#xff0c;但信息化水平普遍偏低。造成这一现状的原因有很多&#xff0c;如对信息化的重视度不够、缺乏足够的资金投入等&#xff0c;严重局限了自己在市场上对系统的选择面&#xff0c;而且难以保证有效的维护…

PDF Expert for mac(苹果电脑专业pdf编辑器)兼容12系统

PDF Expert是macOS平台上的一款优秀的PDF阅读和编辑工具&#xff0c;由Readdle公司开发。它不仅拥有方便、易用的界面&#xff0c;还具备诸多功能&#xff0c;比如编辑PDF文件、添加批注、填写表格、签署文件、合并文档等。安装:PDF Expert for Mac(PDF编辑阅读转换器)v3.5.2中…

Springer LaTeX 模板,及使用Texworks编译参考文献不显示问题

模板下载地址&#xff1a;Manuscripts with mathematical content can also be submitted in LaTeX. We recommend using Springer Nature’s LaTeX template. 下载的压缩包中包含以下文件&#xff1a; 使用Texworks打开.tex文件&#xff0c;生成的PDF有参考文献&#xff0c;但…

一个界面现代美观,色彩年轻化的Vue3+SpringBoot3前后端分离中后台管理脚手架

&#x1f4da; 在线文档 | ✨ 提交需求 | &#x1f680; 演示地址&#xff08;账号/密码&#xff1a;admin/admin123&#xff09; 简介 ContiNew Admin &#xff08;Continue New Admin&#xff09;中后台管理框架/脚手架&#xff0c;持续以最新流行技术栈构建&#xff0c;拥…

PCL点云处理(008)-euc_cluster

欧式聚类是一种基于距离的聚类算法&#xff0c;可以将点云中距离较近的点聚集在一起&#xff0c;形成一个簇。 在PCL库中&#xff0c;欧式聚类的实现原理是将点云中的每个点看作一个向量&#xff0c;然后计算这些向量之间的欧式距离。欧式距离是指两个向量之间的距离&#xff0…

视频电影和字幕如何合并?

我们在看一些国外的电影或者电视剧有时是没有字幕文件的&#xff0c;而对于普通人来说&#xff0c;没有字幕意味着我们无法看懂电影的剧情&#xff0c;好不容易获得的视频资源没有意义了&#xff0c;这种情况该怎么办呢&#xff1f; 其实这种情况完全不用怕&#xff0c;要知道…

【应用前沿】360QPaaS 精彩亮相首届中国航空制造设备博览会 | 数智航空

近日&#xff0c;首届“中国航空制造设备博览会”&#xff08;CAEE2023&#xff09;在宁波国际会展中心顺利召开&#xff0c;本届大会以“数智产融 开放发展”为主题&#xff0c;以“新技术、新产品、新服务、新企业”为定位&#xff0c;以特色化、专业化、品牌化、高端化为方向…