Shiro
Shiro
1.权限管理概述
2.Shiro权限框架
2.1 概念
2.2 Apache Shiro 与Spring Security区别
3.Shiro认证
3.1 基于ini认证
3.2 自定义Realm --认证
4.Shiro授权
4.1 基于ini授权
4.2 自定义realm – 授权
5.项目集成shiro 认证-授权注意点
5.1 认证
5.2 授权
5.3 注解@RequiresPermissions()
5.4 标签式权限验证
6.Shiro加密
7.Shiro缓存
权限管理概述
RBAC(Role Based Access Control) :某个用户拥有什么角色,被允许做什么事情(权限)
用户登录—>分配角色---->(权限关联映射)---->鉴权(拥有什么什么权限)
熟悉框架流程
- 概念 能做什么
- 架构是怎样
- 代码怎么实现
- 项目运用
Shiro权限框架
概念: Apache Shiro 是一个强大且易用的 Java 安全框架
- 能做什么:Shiro可以帮我们完成 :认证、授权、加密、会话管理、与 Web 集成、缓存等。
- 架构是怎样的
主要认识: - Subject(用户):当前的操作用户 获取当前用户Subject currentUser = SecurityUtils.getSubject()
- SecurityManager(安全管理器):Shiro的核心,负责与其他组件进行交互,实现 subject 委托的各种功能
- Realms(数据源) :Realm会查找相关数据源,充当与安全管理间的桥梁,经过Realm找到数据源进行认证,授权等操作
- Authenticator(认证器)::用于认证,从 Realm 数据源取得数据之后进行执行认证流程处理。
- Authorizer(授权器):用户访问控制授权,决定用户是否拥有执行指定操作的权限。
- SessionManager (会话管理器):支持会话管理
- CacheManager (缓存管理器):用于缓存认证授权信息
- Cryptography(加密组件):提供了加密解密的工具包
Apache Shiro 与Spring Security区别
Shiro::用于中小型项目比较常见,简单易上手,可以支持多种环境
Shiro 可以不跟任何的框架或者容器绑定,可独立运行
Spring Security:一般多用于spring环境,中大型项目,更强大
Spring Security 则必须要有Spring环境
Shiro认证
基于ini认证
1.新建项目 --导入依赖--新建 配置文件ini2. shiro帮我们创建用户创建令牌 ,类比页面传入的账号密码密码错误-- IncorrectCredentialsException账号错误-- UnknownAccountException
导入依赖
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.3</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.5.2</version>
</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.22</version><scope>provided</scope>
</dependency>
- 编写ini,shiro默认支持的是ini配置的方式(只是演示) shiro-au.ini,真实项目使用xml
#用户的身份、凭据
[users]
zhangsan=555
xiaoluo=666
- 使用 Shiro 相关的 API 完成身份认证
@Test
public void testLogin(){//创建Shiro的安全管理器,是shiro的核心DefaultSecurityManager securityManager = new DefaultSecurityManager();//加载shiro.ini配置,得到配置中的用户信息(账号+密码)IniRealm iniRealm = new IniRealm("classpath:shiro-au.ini");securityManager.setRealm(iniRealm);//把安全管理器注入到当前的环境中SecurityUtils.setSecurityManager(securityManager);//无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断Subject subject = SecurityUtils.getSubject();System.out.println("认证状态:"+subject.isAuthenticated());//创建令牌(携带登录用户的账号和密码)UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","666");//执行登录操作(将用户的和 ini 配置中的账号密码做匹配)subject.login(token);System.out.println("认证状态:"+subject.isAuthenticated());//登出//subject.logout();//System.out.println("认证状态:"+subject.isAuthenticated());
}
自定义Realm–认证
模拟
@Getter
@Setter
public class Employee {private String username;private String password;}
思路:
//继承 AuthorizingRealm //参数的token ,subject进行登录匹配传入的token :
UsernamePasswordToken
1.获取用户名
方式一:将token强转为UsernamePasswordToken–>getUsername()
方式二:通过token.getPrincipal()2.以用户名为条件,查询mysql数据库,得到用户对象(用户名/密码)
模拟从数据库查询的对象 ------->自己new一个3.将用户对象封装成认证info对象返回
//存在两种可能
1.用户对象为null-----> return null
2.不为空 ---->封装数据 return new SimpleAuthenticationInfo(
有3个参数
1.employee , //从数据库查询出来需要进行密码匹配的用户对象
2.employee.getPassword(), //匹配对象密码
3.super.getName() //指定realm的名称 ,可自定义
)
实现:
public class Realm extends AuthorizingRealm {//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//1.获取登录用户名String username = (String) token.getPrincipal();//2.以用户名为条件查询mysql数据库,得到用户对象//模拟数据Employee employee = new Employee();employee.setUsername("xiaoluo");employee.setPassword("123");//封装成一个认证info对象//判断用户是否为空if (employee!=null){return new SimpleAuthenticationInfo(employee,employee.getPassword(),super.getName());}return null;}
}
@Test
public void testLogin(){//创建Shiro的安全管理器,是shiro的核心DefaultSecurityManager securityManager = new DefaultSecurityManager();//自定义Realm,查出用户信息Realm realm = new Realm();securityManager.setRealm(realm);//把安全管理器注入到当前的环境中SecurityUtils.setSecurityManager(securityManager);//无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断Subject subject = SecurityUtils.getSubject();System.out.println("认证状态:"+subject.isAuthenticated());//创建令牌(携带登录用户的账号和密码)UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","123");//执行登录操作(将用户的和 ini 配置中的账号密码做匹配)subject.login(token);System.out.println("认证状态:"+subject.isAuthenticated());//登出//subject.logout();//System.out.println("认证状态:"+subject.isAuthenticated());
}
Shiro授权
基于ini授权
- 登录
- 分配
- 鉴权
权限表达式 资源:操作
admin=: //超级管理员
emp=employee:*
//判断是否有某个角色
subject.hasRole("role");
//用户拥有所有指定角色返回true
subject.hasAllRoles(Arrays.aList("role1","role2"));
//判断用户是否有指定角色,将结果返回,封装到boolean数组中
boolean[] booleans=subject.hasRoles(Arrays.aList("role1","role2"));
//check开头的是没有返回值,当没有权限时就会抛出异常
subject.checkRole("role");
//判断用户是否有某个权限
subject.isPermitted("权限表达式")
boolean[] booleans=subject.isPermitted("权限表达式1","权限表达式2")
自定义realm — 授权
思路:
1.获取当前登录用户的id/name
//获取当前登录用户对象 ,登录传过来的第一个参数
方式一 :principals.getPrimaryPrincipal();
方式二:SecurityUtils.getSubject().getPrincipal();
2.以用户id作为条件查询mysql数据,查询该用户拥有角色/权限
//假装查数据
//List roles= roleService.queryByEmployee(Employee.getId()); >//List
//List roles=
permissionService.queryByEmployee(Employee.getId());
= permissionService.queryByEmployee(Employee.getId());
3.将角色与权限封装到授权info对象中
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles) //添加角色
info.addStringPermission(permission);//添加权限
实现:
//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//获取当前用户对象Employee employee = (Employee)principalCollection.getPrimaryPrincipal();//以用户id查询数据库,判断该角色/用户是否拥有权限 模拟List<String> roles= Arrays.asList("seller");List<String> permissions= Arrays.asList("customer:list","customer:save");//封装到info对象SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addRoles(roles);info.addStringPermissions(permissions);return info;}
public class ShiroDemo {@Testpublic void testLogin(){//创建Shiro的安全管理器,是shiro的核心DefaultSecurityManager securityManager = new DefaultSecurityManager();//加载shiro.ini配置,得到配置中的用户信息(账号+密码)IniRealm iniRealm = new IniRealm("classpath:shiro-author.ini");//自定义Realm,查出用户信息Realm realm = new Realm();securityManager.setRealm(realm);//把安全管理器注入到当前的环境中SecurityUtils.setSecurityManager(securityManager);//无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断Subject subject = SecurityUtils.getSubject();System.out.println("认证状态:"+subject.isAuthenticated());//创建令牌(携带登录用户的账号和密码)UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","12");//执行登录操作(将用户的和 ini 配置中的账号密码做匹配)subject.login(token);System.out.println("认证状态:"+subject.isAuthenticated());//登出//subject.logout();//System.out.println("认证状态:"+subject.isAuthenticated());//判断用户是否有某个角色System.out.println("hr:"+subject.hasRole("hr"));System.out.println("seller:"+subject.hasRole("seller"));//是否同时拥有多个角色System.out.println("是否同时拥有role1和role2:"+subject.hasAllRoles(Arrays.asList("hr", "seller")));boolean[] booleans = subject.hasRoles(Arrays.asList("hr", "seller"));//System.out.println(booleans);//check开头的是没有返回值的,当没有权限时就会抛出异常subject.checkRole("seller");//判断用户是否有某个权限System.out.println("user:delete:"+subject.isPermitted("user:delete"));subject.checkPermission("customer:list");}
}
//如果用户是超级管理员 //设置超级管理员角色info.addRole("admin")//设置超级管理员权限
info.addStringPermission("*:*");
项目集成shiro 认证-授权注意点
认证
1.添加对应依赖
2.配置代理过滤器 为什么不用配置拦截器? Shiro是选择使用filter过滤器来进行拦截的,因为Shiro不依赖Spring容器,所以当没有springmvc时意味着不能用拦截器,但过滤器则不同,只要是web项目都可以使用
3.创建shiro.xml
4.配置shiro过滤器
5.配置安全管理器 DefaultWebSecurityManager
6.修改LoginController
7.配置自定义Realm
8.将自定义Realm交给容器管理
shiro中的过滤器
- anon: 匿名处理过滤器,即不需要登录即可访问;一般用于静态资源过滤;/static/**=anon
- authc: 表示需要认证(登录)才能使用;(放最后) /**=authc
- logout: 注销过滤器 /logout=logout
- roles: 角色授权过滤器,验证用户是否拥有资源角色;/employee/input=perms[“user:update”]
授权
- 没有权限的异常 :org.apache.shiro.authz.UnauthorizedException
shiro注解鉴权操作方式: 类比RBAC:
1.自定义权限注解
2.将注解贴在请求映射方法上面
3.将注解标注的权限表达式加载到数据库中
4.将这些表达式根据用户角色进行权限分配
5.当用户登录之后,访问某个请求映射方法时,先经过权限拦截器,进行鉴权操作
1.获取当前登录用户权限表达式集合
2.获取当前请求映射方法头顶上权限表达式
3.判断用户权限表达式集合中是否包含该表达式
Shiro 权限验证三种方式
- 编程式
- 注解式
- 页面标签式
1.编程式Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("hr")) {//有权限
} else {//无权限
}
2.注解式
@RequiresRoles("admin")
@RequiresPermissions("user:create")
public void hello() {//有权限
}
3.页面标签式<@shiro.hasPermission name="employee:list"><!-- 有权限 -->
</@shiro.hasRole>
授权步骤
1.贴注解
2.开启 Shiro 注解扫描器
3.查询数据库真实数据 自定义realm
注解@RequiresPermissions()
- value属性: 这个属性是一个数组
- Logical.AND: 必须同时拥有value配置所有权限才允许访问
- Logical.OR:只需要拥有value配置所有权限中一个即可允许访问
约定:权限表达式,第一值权限表达式,第二值为权限名称
@RequiresPermissions(value={“employee:list”,“员工列表”},logical=Logical.OR)
逻辑为OR,value满足其中一个条件成立
标签式权限验证
拓展FreeMarker标签
默认的freemarker是不支持shiro标签,所以需要做功能拓展
public class ShiroFreeMarkerConfig extends FreeMarkerConfigurer {@Overridepublic void afterPropertiesSet() throws IOException, TemplateException {//继承之前的属性配置,这不能省super.afterPropertiesSet();Configuration cfg = this.getConfiguration();cfg.setSharedVariable("shiro", new ShiroTags());//注册shiro 标签}
}
让之前的配置文件中的FreeMarkerConfigurer修改为自定义的ShiroFreeMarkerConfig类
<!-- 注册 FreeMarker 配置类 -->
<bean class="cn.k.shiro.ShiroFreeMarkerConfig"><!-- 配置 FreeMarker 的文件编码 --><property name="defaultEncoding" value="UTF-8" /><!-- 配置 FreeMarker 寻找模板的路径 --><property name="templateLoaderPath" value="/WEB-INF/views/" /><property name="freemarkerSettings"><props><!-- 兼容模式 ,配了后不需要另外处理空值问题,时间格式除外 --><prop key="classic_compatible">true</prop></props></property>
</bean>
使用shiro标签
- authenticated 标签:已认证通过的用户。
<@shiro.authenticated> </@shiro.authenticated>
- notAuthenticated 标签:未认证通过的用户。
<@shiro.notAuthenticated></@shiro.notAuthenticated>
- principal 标签 :输出当前用户信息,通常为登录帐号信息
<@shiro.principal property="name" /> //对应name属性
- hasRole 标签:验证当前用户是否拥有该角色
<@shiro.hasRole name="admin">我是管理员</@shiro.hasRole>
- hasAnyRoles 标签:验证当前用户是否拥有这些角色中的任何一个,角色之间逗号分隔
<@shiro.hasAnyRoles name="admin,user,hr">Hello admin</@shiro.hasAnyRoles>
- hasPermission 标签:验证当前用户是否拥有该权限
<@shiro.hasPermission name="department:delete">删除</@shiro.hasPermission>
Shiro加密
MD5
//参数1 :原文,参数2:盐 ,参数3:散列次数(加密次数)
Md5Hash hash=new Md5Hash("1","kent",3);
盐一般要求是固定长度的字符串,且每个用户的盐不同。
一般盐的选择的是用户的唯一数据(账号名等),盐是要求不能改变的,不然下次加密结果就对应不上了
Shiro缓存
当我们登录时,授权信息是要从数据库中查询的,如果每次刷新刷新都需要获取你到底有没有权限,对性能影响不好,用户登录后,授权信息一般很少改动,所以,我们可以将第一次授权后,将信息存在缓存中,下次直接再缓存中获取,就很好的避免了多次访问数据库
Shiro没有实现自己的缓存机制,只提供了支持缓存的API接口,这里使用的是EhCache
集成EhCache
第三方EhCache
1.配置缓存管理器并引用缓存管理器
<!--安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><!--注册自定义数据源--><property name="realm" ref="employeeRealm"/><!--注册缓存管理器--><property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <!-- 设置配置文件 --><property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>2.添加缓存配置文件 shiro-ehcache.xml<ehcache><defaultCachemaxElementsInMemory="1000"eternal="false"timeToIdleSeconds="600"timeToLiveSeconds="600"memoryStoreEvictionPolicy="LRU"></defaultCache>
</ehcache>
配置缓存文件属性说明
- maxElementsInMemory: 缓存对象最大个数。
- eternal:对象是否永久有效 ,一般为false
- timeToIdleSeconds: 对象空闲时间
- timeToLiveSeconds:对象存活时间
- memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,Ehcache 将会根据指定的策略去清理内存。
- LFU(较少使用,意思是一直以来最少被使用的,缓存的元素有一个hit 属性(命中率),hit 值最小的将会被清出缓存)默认
拓展 统一全局异常
@ControllerAdvice 控制器功能增强注解
1.在进入请求映射方法之前做功能增强,经典用法:date日期格式化
2.在进入请求映射方法之后做功能增强,经典用法:统一异常处理
3.处理异常的方法,方法需要贴ExceptionHandler注解
配置文件
配置代理过滤器
<filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
配置shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--引入自定义Realm--><bean id="employeeRealm" class="cn.k.shiro.EmployeeRealm"></bean><!--配置安全管理器--><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="realm" ref="employeeRealm"/><!--注册缓存管理器--><property name="cacheManager" ref="cacheManager"/></bean><!--配置shiro过滤器--><bean id="shiroFilter"class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><!--引用指定的安全管理器--><property name="securityManager" ref="securityManager"/><!--shiro默认的登录地址是/login.jsp 现在要指定我们自己的登录页面地址--><property name="loginUrl" value="/login.html"/><!--路径对应的规则--><property name="filterChainDefinitions"><value>/userLogin=anon/css/**=anon/js/**=anon/img/**=anon/upload/**=anon/userLogout=logout/**=authc</value></property></bean><!--开启 Shiro 注解扫描器--><!-- <aop:config/> 会扫描配置文件中的所有advisor,并为其创建代理 --><aop:config/><!-- Pointcut advisor通知器, 会匹配所有加了shiro权限注解的方法 --><bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"><property name="securityManager" ref="securityManager"/></bean><!-- 缓存管理器 --><bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"><!-- 设置配置文件 --><property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/></bean></beans>
关联mvc.xml
<import resource="classpath:shiro.xml"/>
loginController
@RequestMapping("/userLogin")@ResponseBodypublic JsonResult login(String username, String password){try {UsernamePasswordToken token = new UsernamePasswordToken(username, password);SecurityUtils.getSubject().login(token);return new JsonResult();} catch (UnknownAccountException e) {return new JsonResult(false, "账号不存在");} catch (IncorrectCredentialsException e) {return new JsonResult(false, "密码错误");} catch (Exception e) {e.printStackTrace();return new JsonResult(false, "登录异常,请联系管理员");}}
相关文章:

Shiro
Shiro 1.权限管理概述 2.Shiro权限框架 2.1 概念 2.2 Apache Shiro 与Spring Security区别 3.Shiro认证 3.1 基于ini认证 3.2 自定义Realm --认证 4.Shiro授权 4.1 基于ini授权 4.2 自定义realm – 授权 5.项目集成shiro 认证-授权注意点 5.1 认证…...

使用nginx进行负载均衡配置详细说明
使用nginx进行负载均衡 1. nginx负载均衡介绍 nginx应用场景之一就是负载均衡。在访问量较多的时候,可以通过负载均衡,将多个请求分摊到多台服务器上,相当于把一台服务器需要承担的负载量交给多台服务器处理,进而提高系统的吞吐…...

N皇后问题
#include<iostream> #include<string> #include<vector> using namespace std; #define MAX 20//最大20个皇后 int n ;//实际皇后个数 int sum ;//答案个数 vector<vector<int>> attack(MAX, vector<int>(MAX, 0));//标记攻击位置 vector&…...

强化学习DQN之俄罗斯方块
强化学习DQN之俄罗斯方块强化学习DQN之俄罗斯方块算法流程文件目录结构模型结构游戏环境训练代码测试代码结果展示强化学习DQN之俄罗斯方块 算法流程 本项目目的是训练一个基于深度强化学习的俄罗斯方块。具体来说,这个代码通过以下步骤实现训练: 首先…...

1.3总线:并行总线、串行总线、单工、半双工、全双工、总线宽度、总线带宽、总线的分类、数据总线、地址总线、控制总线
1.3总线:并行总线、串行总线、单工、半双工、全双工、总线宽度、总线带宽、总线的分类、数据总线、地址总线、控制总线总线并行总线、串行总线单工、半双工、全双工总线宽度总线带宽总线的分类数据总线(Data Bus,DB)地址总线&…...

Linux驱动开发—设备树开发详解
设备树开发详解 设备树概念 Device Tree是一种描述硬件的数据结构,以便于操作系统的内核可以管理和使用这些硬件,包括CPU或CPU,内存,总线和其他一些外设。 Linux内核从3.x版本之后开始支持使用设备树,可以实现驱动代…...

深入浅出C++ ——继承
文章目录一、继承的相关概念1. 继承的概念2. 继承格式3. 继承方式4. 访问限定符5. 继承基类成员访问方式的变化二、基类和派生类对象赋值转换三、继承中的作用域四、派生类的默认成员函数五、继承与友元六、继承与静态成员七、菱形继承及菱形虚拟继承1. 单继承2. 多继承3. 菱形…...

设计模式C++实现20: 桥接模式(Bridge)
部分内容参考大话设计模式第22章;本实验通过C语言实现。 一 基本原理 意图:将抽象部分和实现部分分离,使它们都可以独立变化。 上下文:某些类型由于自身的逻辑,具有两个或多个维度的变化。如何应对“多维度的变化”…...

Android中的Rxjava
要使用Rxjava首先要导入两个包,其中rxandroid是rxjava在android中的扩展 implementation io.reactivex:rxandroid:1.2.1implementation io.reactivex:rxjava:1.2.0observer 是一个观察者接口,泛型T为观察者观察数据的类型,里面只有三个方法&a…...

【RocketMQ】源码详解:消息储存服务加载、文件恢复、异常恢复
消息储存服务加载 入口:org.apache.rocketmq.store.DefaultMessageStore#load 在创建brokerContriller时会调用初始化方法初始化brokerController,在初始化方法中会进行消息储存服务的加载 this.messageStore.load(); 加载方法主要是加载一些必要的参数和数据,如配…...

数字IC设计工程师是做什么的?
随着我国半导体产业的发展,近几年的新入行的从业人员,除了微电子相关专业的,还有就是物理、机械、数学、计算机等专业,很多人对这一高薪行业充满了好奇,那么数字IC设计工程师到底是做什么的? 首先来看看数…...

【040】134. 加油站[简单模拟 + 逻辑转化]
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和 cost &am…...

Python用selenium实现自动登录和下单的脚本
前言 学python对selenium应该不陌生吧 Selenium 是最广泛使用的开源 Web UI(用户界面)自动化测试套件之一。Selenium 支持的语言包括C#,Java,Perl,PHP,Python 和 Ruby。目前,Selenium Web 驱动…...

(02)Cartographer源码无死角解析-(55) 2D后端优化→AppendNode()、class MapById、 PoseGraphData、
讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解:https://blog.csdn.net/weixin_43013761/article/details/127350885 文末正下方中心提供了本…...

如何在jmeter中把响应中的数据提取出来并引用
jmeter做接口测试过程中,经常遇到请求需要用到token的时候,我们可以把返回token的接口用后置处理器提取出来,但是在这种情况下,只能适用于当前的线程组,其他线程组无法引用到提取的token变量值,所以必须要生…...

2023环翠区编程挑战赛中学组题解
T1. 出栈序列 题目描述 栈是一种“先进后出”的数据结构,对于一个序列1,2,...,n1,2, ...,n1,2,...,n,其入栈顺序是1,2,...n1,2, ...n1,2,...n,但每个元素出栈的时机可以自由选择。 例如111入栈、111出栈,222入栈、333入栈、333…...

手撸一个Switch开关组件
一、前言 手撸系列又来了,这次咱们来撸一个Switch开关组件,废话不多说,咱们立刻发车。 二、使用效果 三、实现分析 首先我们先不想它的这个交互效果,我们就实现“不合格”时的一个静态页面,静态页面大致如下&#x…...

2023年1月冰箱品牌销量排行:销量环比增长26%,销售额36亿+
鲸参谋电商大数据2023年1月京东平台“冰箱”销售数据出炉! 根据鲸参谋平台电商数据显示,2023年1月份,在京东平台上,冰箱的销量将近130万件,环比增长26%,同比下滑8%;销售额达36亿,环比…...

DSP CCS 开发问题总结及解决办法
文章目录 问题汇总 1. CCS编译器的Project菜单栏工程导入选项丢失,怎么解决! 1.1启动CCS后发现导入工程菜单栏丢失,无法导入工程文件。 1.2方法一 工程选项的导入工程文件丢失,如果要重新获得相应的选项,就需要删除当前…...

Vue3.x+Element Plus仿制Acro Design简洁模式分页器组件
Vue3.xElement Plus仿制Acro Design简洁模式分页器组件 开发中难免会遇到宽度很窄的列表需要使用分页器的情况,这时若使用Element Plus组件的分页器会导致分页器内容超出展示的区域,而Element Plus组件中目前没有Acro Design那样小巧的分页器(…...

经典文献阅读之--VoxelMap(体素激光里程计)
0. 简介 作为激光里程计,常用的方法一般是特征点法或者体素法,最近Mars实验室发表了一篇文章《Efficient and Probabilistic Adaptive Voxel Mapping for Accurate Online LiDAR Odometry》,同时还开源了代码在Github上。文中为雷达里程计提…...

.NET6中使用GRPC详细描述
Supported languages | gRPC,官网。至于原理就不说了,可以百度原理之后,然后再结合代码,事半功倍,就能很好理解GRPC了。 目录 一、简单使用 二、实际应用 一、简单使用 1.使用vs2022创建一个grpc程序,…...

ML@矩阵微积分基础
文章目录矩阵微积分Matrix calculus记法简单Jacobi Matrix分子记法分母记法一般形式的Jacobi MatrixTypes of matrix derivative向量求导向量对标量求导标量对向量求导向量对向量求导矩阵求导矩阵对标量求导(切矩阵)标量对矩阵求导记法向量求导 向量对标量求导标量对向量求导向…...

华为OD机试真题Python实现【优秀学员统计】真题+解题思路+代码(20222023)
优秀学员统计 题目 公司某部门软件教导团正在组织新员工每日打卡学习活动,他们开展这项学习活动已经一个月了,所以想统计下这个月优秀的打卡员工。每个员工会对应一个 id,每天的打卡记录记录当天打卡员工的 id 集合,一共 30 天。 请你实现代码帮助统计出打卡次数 top5 的…...

docsify在线文档支持pdf查看
目录 步骤一:添加插件 步骤二:添加pdf地址 步骤三:成果展示 docsify是一个在github上很好用的文档转换网页的工具,但是大部分情况我们都是使用的markdown文件。最近想把pdf文档也能支持在这上面展示,研究后总结一下…...

ES6中Set类型的基本使用
在ES6之前,存储数据的结构主要有两种:数组、对象。 在ES6中新增了另外两种数据结构(存放数据的方式):Set、Map,以及他们的另外形式WeakSet、WeakMap。 Set的基本使用 Set是一个新增的数据结构,…...

【VUE3.0_CSS功能】
CSS功能组件css作用域深度选择器(标签名空格:deep(标签名))插槽选择器(:soltted(标签名))全局选择器(:global(类名))动态CSS(v-bind)useCSSModule拓展知识:deep的写法组件…...

微机原理复习总结6:汇编语言程序设计
本篇博客主要分享几道汇编语言例题编写一完整的程序,从键盘输入一组字符,直到输入“0”为止,当输入是小写字母时,则修改为大写字母,输入的字符存放在string为首址的存储单元中。data segment ;数据段定义 st…...

计算机网络 部分原理和过程
下面是一台计算机 Ping 和不在同一 IP 网络上的另一台计算机的全过程: 该计算机首先确定要 Ping 的目标 IP 地址,并检查该 IP 地址是否与本地 IP 地址在同一 IP 网络上。如果目标 IP 地址与本地 IP 地址不在同一 IP 网络上,则需要通过路由器…...

C++实现链表
C实现链表 众所周知,C/C语言实现的链表是由一个一个的结点构成,每个结点分为数据域和指针域,指针域中存储了其后继结点的地址,通过地址来访问下一个结点。 链表是一系列节点串联形成的数据结构,链表存储有序的元素集合…...