shiro 框架使用学习

简介

  • Shiro安全框架是Apache提供的一个强大灵活的安全框架
  • Shiro安全框架提供了认证、授权、企业会话管理、加密、缓存管理相关的功能,使用Shiro可以非常方便的完成项目的权限管理模块开发

Shiro的整体架构

在这里插入图片描述
1、Subject
​ Subject即主体(可以把当前用户理解为主体),外部应用与Subject进行交互,Subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在Shiro中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过Subject进行认证授,而Subject是通过SecurityManager安全管理器进行认证授权

2、Security Manager
​ SecurityManager即安全管理器,对全部的Subject进行安全管理,它是Shiro的核心,负责对所有的Subject进行安全管理。通过SecurityManager可以完成Subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

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

4、Authenticator
​ Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,Shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

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

6、realm
​ Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库,那么realm就需要从数据库获取用户身份信息。

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

8、SessionDAO
​ SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

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

shiro 使用

1.、shiro的配置类

/*** shiro权限管理的配置*/
@Configuration
public class ShiroConfig {/*** 安全管理器*/@Beanpublic DefaultWebSecurityManager securityManager(ShiroDatabaseRealm shiroDatabaseRealm,RememberMeManager rememberMeManager,CacheManager cacheManager,SessionManager sessionManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(shiroDatabaseRealm);securityManager.setCacheManager(cacheManager);securityManager.setRememberMeManager(rememberMeManager);securityManager.setSessionManager(sessionManager);return securityManager;}/*** 会话管理器*/@Beanpublic SessionManager sessionManager() {return new ServletContainerSessionManager();}/*** 缓存管理器*/@Beanpublic CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {EhCacheManager ehCacheManager = new EhCacheManager();ehCacheManager.setCacheManager(ehcache.getObject());return ehCacheManager;}/*** rememberMe管理器*/@Beanpublic CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {CookieRememberMeManager manager = new CookieRememberMeManager();manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));manager.setCookie(rememberMeCookie);return manager;}/*** 记住密码Cookie*/@Beanpublic SimpleCookie rememberMeCookie() {SimpleCookie simpleCookie = new SimpleCookie("rememberMe");simpleCookie.setHttpOnly(true);simpleCookie.setMaxAge(7 * 24 * 60 * 60);//7天return simpleCookie;}/*** Shiro的过滤器链*/@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);/*** 默认的登陆访问url*/shiroFilter.setLoginUrl("/login");/*** 登陆成功后跳转的url*/shiroFilter.setSuccessUrl("/");/*** 没有权限跳转的url*/shiroFilter.setUnauthorizedUrl("/global/error");/*** 自定义过滤器*/HashMap<String, Filter> myFilters = new HashMap<>();myFilters.put("user", new ShiroUserFilter());shiroFilter.setFilters(myFilters);/*** 配置shiro拦截器链** anon  不需要认证* authc 需要认证* user  验证通过或RememberMe登录的都可以** 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的** 顺序从上到下,优先级依次降低** api开头的接口,走rest api鉴权,不走shiro鉴权**/Map<String, String> hashMap = new LinkedHashMap<>();//NONE_PERMISSION_RES是一个集合里边存储了所有不需要过滤的路径,包括登录路径,错误路径等,将里边所有路径标志为anon,即访问不需要权限。for (String nonePermissionRe : NONE_PERMISSION_RES) {hashMap.put(nonePermissionRe, "anon");}//  剩下的路径  走自定义过滤器hashMap.put("/**", "user");shiroFilter.setFilterChainDefinitionMap(hashMap);return shiroFilter;}/*** Shiro生命周期处理器:* 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)* 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)*/@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 启用shrio授权注解拦截方式,AOP式方法级权限检查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}
}

2.在login controller中进行登录操作

在login controller中需要使用一个UsernamePasswordToken类型的token来存储用户名、密码、和remeberme ,token相当于一个令牌。
SecurityUtils.getSubject()得到subject, subject是一个非常重要的对象,包含了登录、注销、获取当前用户、检查角色、权限等操作的方法。
调用subject.login()方法将token作为参数会进入Shiro的安全管理器,并调用 Realm 中的 doGetAuthenticationInfo 方法。

 @RequestMapping(value = "/login", method = RequestMethod.POST)public String loginVali(String username, String password, String remember) {Subject subject = SecurityUtils.getSubject();// 1) TODO 准备 Token 数据UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray());// 2) TODO 开启“记住我”功能if(remember!=null&&remember.equals("on")){token.setRememberMe(true);}else {token.setRememberMe(false);}// 3) TODO 利用 subject 进行登录subject.login(token);// 4) TODO 记录登录日志 (暂时不做)return REDIRECT + "/";}

3.实现Realm类

需要实现Realmedia中的三个方法

  • doGetAuthenticationInfo()方法 ,在该方法中需要完成认证信息的准备。
  • setCredentialsMatcher()方法,需要在该方法中重新实现一个CredentialsMatcher()类中的接口用来提供验证密码的正确性,然后将新的CredentialsMatcher对象当作参数调用父类的setCredentialsMatcher()方法。
  • doGetAuthorizationInfo()方法,该方法中需要进行授权信息准备,准备好用户的权限信息,将来使用AOP进行权限过滤时会用到。

下边是自己实现的ShiroDatabaseRealm类

  • doGetAuthenticationInfo()方法中返回了一个SimpleAuthenticationInfo对象SimpleAuthenticationInfo 表示认证信息
    • principal 表示用户信息(一般就是一个用户对象,里面会包含角色信息)
    • credentail 表示密码信息(从数据库获取,一般是加密后的)
    • realmName表示当前的Realm的名字
  • SimpleAuthenticationInfo第一个参数传递了一个ShiorUser对象而不是直接的User对象是因为在ShiorUser对象中多了一些存储权限路径等信息的属性。
  • doGetAuthorizationInfo()方法返回一个SimpleAuthorizationInfo 对象,使用setRoles设置其角色集合,setStringPermissions设置权限集合(url访问列表)
@Component
@DependsOn({"userService", "deptService", "roleService"})
@Slf4j
public class ShiroDatabaseRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;@Autowiredprivate DeptService deptService;@Autowiredprivate RoleService roleService;// TODO 1. 完成 认证信息 准备@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// a. TODO 此 token 就是登录时封装的 token 对象UsernamePasswordToken token1=(UsernamePasswordToken)token;// b. TODO 可以利用 userService 中的方法获取 用户对象User user = userService.selectByAccount(token1.getUsername());// c. TODO 记得密码验证不需要在这里做,你要做的判断有两件事:// d. TODO 第一,判断 数据库中该用户是否存在,不存在,则抛出 shiro 的 UnknownAccountException 异常if(user==null) {throw new UnknownAccountException();}// e. TODO 第二,判断 该用户是否被冻结,如被冻结,则抛出 shiro 的 LockedAccountException 异常if(user.getStatus()=="FREEZE"){throw new LockedAccountException();}// f. TODO 将数据库用户信息 User 对象转换 为 ShiroUser 对象ShiroUser shiroUser=toShiroUser(user);// g. TODO 将认证信息:ShiroUser 对象、数据库密码、realm 名称(通过 getName() 得到) 封装至 SimpleAuthenticationInfo 并返回SimpleAuthenticationInfo root = new SimpleAuthenticationInfo(shiroUser, user.getPassword(), this.getName());/*注意* 验证密码的操作,是 shiro 框架完成的,不需要主动调用,只需要提供密码验证的 CredentialsMatcher 对象* 验证成功,进入 successUrl 验证失败进入 loginUrl* 成功后会将 SimpleAuthenticationInfo 中的 principal 信息存入 session, 以后可以通过 Subject 对象获得* 成功后还会做一些 RemeberMe cookie 的生成并返回操作,也无需我们干预*/return root;}/* ShiroUser 的作用1. 用来保存认证信息中的用户数据,即 principal,将来存入 session,以便在登录期间使用2. 其中除了用户数据,还包括了用户的角色信息,其属性都是根据需要自定义的3. 为什么不直接用 User ? 是因为认证过程中的很多属性(包括将来页面要显示的属性) User 对象中没有,因此用 ShiroUser 来保存更多的信息*/private ShiroUser toShiroUser(User user) {ShiroUser shiroUser = new ShiroUser();shiroUser.setId(user.getUserId());shiroUser.setAccount(user.getAccount());shiroUser.setDeptId(user.getDeptId());shiroUser.setDeptName(deptService.selectName(user.getDeptId()));shiroUser.setName(user.getName());shiroUser.setEmail(user.getEmail());shiroUser.setAvatar(user.getAvatar());String[] split = user.getRoleId().split(",");List<Long> roleIds = new ArrayList<>();List<String> roleNames = new ArrayList<>();for (String s : split) {Long roleId = Long.valueOf(s);roleIds.add(roleId);String roleName = roleService.selectName(roleId);roleNames.add(roleName);}shiroUser.setRoleList(roleIds);shiroUser.setRoleNames(roleNames);log.debug("==============> user {} roles: {}", user.getName(), shiroUser.getRoleNames());return shiroUser;}// TODO 2. 提供密码验证 CredentialsMatcher@Overridepublic void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {CredentialsMatcher matcher = new CredentialsMatcher() {@Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {// TODO token 是表单提交过来的数据, info 是在 doGetAuthenticationInfo 步骤返回的认证信息UsernamePasswordToken token1=(UsernamePasswordToken)token;char[] password = token1.getPassword();String pass_user = new String(password);String pass_databases = info.getCredentials().toString();// TODO 使用 BCrypt 算法验证密码的正确性,返回值即表示验证是否通过return  BCrypt.checkpw(pass_user, pass_databases);// 验证不通过会由框架抛出 IncorrectCredentialsException 异常}};super.setCredentialsMatcher(matcher);}// TODO 3. 完成 授权信息 准备// 当需要进行权限验证时,会调用 doGetAuthorizationInfo 获得当前用户(已认证)的权限信息,只会执行一次,放入缓存当中@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();// a. TODO 获取用户的所有 url 访问权限,可以通过 roleService.selectPermissionURL 获取一个角色的 url 访问列表// 注意,一个用户可能会有多个角色LinkedHashSet<String> set = new LinkedHashSet<>();for (Long roleId : shiroUser.getRoleList()) {List<String> strings = roleService.selectPermissionURL(roleId);set.addAll(strings);}// b. TODO 准备一个 SimpleAuthorizationInfo 对象,应设置其角色集合,权限集合(url访问列表),并返回/* 注意,这些集合中都是字符串表示的角色和权限后续可以通过 Subject 对象中的相关方法来使用这里准备的数据,如:* subject.hasRole(角色名) 判断用户是否有某个角色* subject.isPermitted(权限名) 判断用户是否有某个权限* 更多方法,参考 shiro 的 Subject 接口说明*/LinkedHashSet<String> roleNames = new LinkedHashSet<>();roleNames.addAll(shiroUser.getRoleNames());SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setRoles(roleNames);info.setStringPermissions(set);return info;}
}

4. 在AOP中进行权限过滤

在需要进行权限过滤的controller方法上加@Permission注解。
若该方法必须某个角色才能访问则@Permission(“角色名”);

  • 得到subject
    Subject subject = SecurityUtils.getSubject();
  • 判断是否含有所有角色
    subject.hasAllRoles()
  • 判断是否含有URL
    subject.isPermitted()

/*** 权限检查的aop*/
// 0. TODO 打开 @Aspect 注解
@Aspect
@Component
@Order(200)
@Slf4j
public class ShiroPermissionAop {// 1. TODO 控制器内需要权限控制的方法上都加了 @Permission 自定义注解,添加合适的切点@Around("@annotation(cn.stylefeng.guns.core.common.annotion.Permission)")public Object doPermission(ProceedingJoinPoint pjp) throws Throwable {// a. 获取当前的请求路径(已实现)String requestURI = getRequestURI();// b. 获取当前的控制器方法对象(已实现)Method method = getMethod(pjp);// c. TODO 拿到方法的 Permission 注解,做进一步判断Permission annotation= method.getAnnotation(Permission.class);String[] roleNames = annotation.value();// d. TODO 分支1,如果 Permission 上有角色,调用 checkRoles 进一步判断if(annotation.value().length>0){boolean pass= checkRoles(roleNames);;if(!pass){throw new NoPermissionException("没有权限");}}// e. TODO 分支2,如果没有角色,那么进行所有路径匹配检查,调用 checkPermission 进一步判断else{boolean pass= checkPermission(requestURI);if(!pass){throw new NoPermissionException("没有权限");}}// f. TODO 以上分支,检查通过,使用 pjp 放行, 不通过抛出 NoPermissionException// 注意放行的话应该 将 pjp 执行目标方法的结果返回return pjp.proceed();}private Method getMethod(ProceedingJoinPoint point) {MethodSignature ms = (MethodSignature) point.getSignature();return ms.getMethod();}private String getRequestURI() {HttpServletRequest request = HttpContext.getRequest();String requestURI = request.getRequestURI().replaceFirst(ConfigListener.getConf().get("contextPath"), "");log.debug("===============> current uri: {}", requestURI);return requestURI;}public boolean checkRoles(Object[] permissions) {String[] roleNames = (String[]) permissions;Subject subject = SecurityUtils.getSubject();return subject.hasAllRoles(Arrays.asList(roleNames));}private boolean checkPermission(String requestURI) {Subject subject = SecurityUtils.getSubject();return  subject.isPermitted(requestURI);}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序将后端返回的图片文件流解析显示导页面

说明 由于请求接口后端返回的图片格式不是一个完整的url,也不是其他直接能显示的图片格式&#xff0c;是一张图片 后端根据模板与二维码生成图片,返回二进制数据 返回为文件流的格式,用wx.request请求的时候&#xff0c;就自动解码成为了下面这样的数据数据格式,这样的数据没…

idea文件比对

idea文件比对 1.项目内的文件比对2.项目间的文件比对3. 剪切板对比4. 版本历史(不同分支和不同commit)对比 1.项目内的文件比对 在项目中选择好需要比对的文件(类)&#xff0c;然后选择Compare Files Mac下的快捷键是Commandd&#xff0c; 这样的比对像是git冲突解决一样 …

STM32 GPIO 描述

一、GPIO功能描述 每个GPIO端口有两个32位配置寄存器(GPIOx_CRL&#xff0c;GPIOx_CRH) &#xff0c;两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR) &#xff0c;一个32位置位/复位寄存器(GPIOx_BSRR)&#xff0c;一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR…

Flutter:安装依赖报错doesn‘t support null safety

项目中需要引用http依赖&#xff0c;在pubspec.yaml文件中添加如下信息&#xff1a; 当同步时&#xff0c;报错信息如下&#xff1a; [myflutter] flutter pub upgrade Resolving dependencies... The current Dart SDK version is 3.1.3. Because myflutter depends on http &…

【leetcode】19.删除链表的倒数第 N 个结点

题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 示例 3&a…

校园安防监控系统升级改造方案:如何实现设备利旧上云与AI视频识别感知?

一、背景与需求分析 随着现代安防监控科技的兴起和在各行各业的广泛应用&#xff0c;监控摄像头成为众所周知的产品&#xff0c;也为人类的工作生活提供了很大的便利。由于科技的发达&#xff0c;监控摄像头的升级换代也日益频繁。每年都有不计其数的摄像头被拆掉闲置&#xf…

R语言爬虫代码模版:技术原理与实践应用

目录 一、爬虫技术原理 二、R语言爬虫代码模板 三、实践应用与拓展 四、注意事项 总结 随着互联网的发展&#xff0c;网络爬虫已经成为获取网络数据的重要手段。R语言作为一门强大的数据分析工具&#xff0c;结合爬虫技术&#xff0c;可以让我们轻松地获取并分析网络数据。…

【网络】五中IO模型介绍 + 多路转接中select和poll服务器的简单编写

高级IO 前言正式开始前面的IO函数简单过一遍什么叫做低效的IO钓鱼的例子同步IO和异步IO五种IO模型阻塞IO非阻塞IO信号驱动多路转接异步IO 小结 代码演示非阻塞IO多路转接select介绍简易select服务器timeout 为 nullptrtimeout 为 {0, 0}timeout 为 {5, 0}调用accept select编写…

ACmix:卷积和self-attention的结合,YOLOv5改进之ACmix

目录 一、ACmix理论部分 二、代码 三、YOLOv5改进 ACC3 一、ACmix理论部分 论文地址:2111.14556.pdf (arxi

【Database System Concept 7th】Chapter 24 Advanced Indexing Techniques 读书笔记

Chapter 24 Advanced Indexing Techniques 24.5 Hash Indices24.5.1 Static Hashing24.5.2 Dynamic Hashing24.5.2.1 Data Structure24.5.2.2 Queries and Updates 24.5 Hash Indices 24.5.1 Static Hashing 这一部分就不介绍了&#xff0c;在14.5中已经介绍过了。 24.5.2 D…

javascript中的new原理及实现

在js中&#xff0c;我们通过new运算符来创建一个对象&#xff0c;它是一个高频的操作。我们一般只是去用它&#xff0c;而很少关注它是如何实现的&#xff0c;它的工作机制是什么。 1 简介 本文介绍new的功能&#xff0c;用法&#xff0c;补充介绍了不加new也同样创建对象的方…

部署kubevirt教程

前提条件 已安装&#xff1a;kubernetes集群、kubectl、docker apt install -y qemu-kvm libvirt virt-install bridge-utils 【所有节点全部安装】 virt-host-validate qemu部署kubevirt 下载kubevirt-cr.yaml和kubevirt-operator.yaml 先执行&#xff1a; Kubectl apply …

047_第三代软件开发-日志分离

第三代软件开发-日志分离 文章目录 第三代软件开发-日志分离项目介绍日志分离用法 关键字&#xff1a; Qt、 Qml、 log、 日志、 分离 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&#xff09;和 C 的强…

LLM之幻觉(一):大语言模型幻觉解决方案综述

论文题目&#xff1a;《Cognitive Mirage: A Review of Hallucinations in Large Language Models》 ​论文链接&#xff1a;https://arxiv.org/abs/2309.06794v1 论文代码&#xff1a;https://github.com/hongbinye/cognitive-mirage-hallucinations-in-llms 一、幻觉介绍 …

编写软件产品使用说明书的重要技巧分享

编写软件产品使用说明书是确保用户能够准确了解和使用你的软件的重要一步。一份清晰、详尽的软件产品使用说明书不仅可以提高用户满意度&#xff0c;也能减少用户的疑惑和困惑。然而&#xff0c;要编写一份优秀的软件产品使用说明书并不容易。接下来就跟大家分享一些我的经验和…

《学懂java》:java基础篇

他们都告诉你&#xff0c;必须要做什么&#xff0c;却没告诉你为什么。 ##《 欢迎访问我的网站,ai工具箱&#xff0c;https://4398ai.com里面有免费的chatgpt网站&#xff0c;和很多免费的编程资源的干货》 首先说一下接口&#xff0c;抽象&#xff08;abstract&#xff09;&a…

vue实现商品列表,组件抽离

1.需求说明 my-tag 标签组件封装 ​ (1) 双击显示输入框&#xff0c;输入框获取焦点 ​ (2) 失去焦点&#xff0c;隐藏输入框 ​ (3) 回显标签信息 ​ (4) 内容修改&#xff0c;回车 → 修改标签信息 my-table 表格组件封装 ​ (1) 动态传递表格数据渲染 ​ (2) 表头支…

15 款 PDF 编辑器帮助轻松编辑、合并PDF文档

PDF 编辑器在当今的数字环境中至关重要&#xff0c;因为 PDF 已成为共享和存储信息的首选格式。只需几分钟&#xff0c;可靠的 PDF 编辑器即可让用户能够根据其特定需求修改、定制和定制文档。在本文中&#xff0c;我们全面汇编了 15 款最佳免费 PDF 编辑器&#xff0c;让您可以…

Javaweb之HTML,CSS的详细解析

2.4 表格标签 场景&#xff1a;在网页中以表格&#xff08;行、列&#xff09;形式整齐展示数据&#xff0c;我们在一些管理类的系统中&#xff0c;会看到数据通常都是以表格的形式呈现出来的&#xff0c;比如&#xff1a;班级表、学生表、课程表、成绩表等等。 标签&#xff…

目标跟踪(DeepSORT)

本文首先将介绍在目标跟踪任务中常用的匈牙利算法&#xff08;Hungarian Algorithm&#xff09;和卡尔曼滤波&#xff08;Kalman Filter&#xff09;&#xff0c;然后介绍经典算法DeepSORT的工作流程以及对相关源码进行解析。 目前主流的目标跟踪算法都是基于Tracking-by-Detec…