Shiro整合SpringBoot,实战下的应用场景
文章目录
- 前言
- 一、springBoot+shiro环境准备
- 1.数据库
- 2.ssmp环境搭建
- 3.实体类
- 4.三层搭建
- 5.初始化测试数据
- 二、Shiro过滤器
- 1.Shiro认证过滤器
- 2.Shiro授权过滤器
- 三、springBoot+shiro身份认证
- 1.创建Realm,重写认证方法doGetAuthenticationInfo
- 2.创建shiro配置类
- 3.Postman测试
- 四、springBoot+shiro授权,鉴权
- 1.重写授权方法doGetAuthenticationInfo
- 2.访问程序资源(鉴权)
- 3.全局异常处理器
- 4.Postman测试
前言
整合springBoot+shiro流程:
- 环境准备
- 身份认证
2.1 密码加密
2.2 非法请求控制 - 授权,鉴权
一、springBoot+shiro环境准备
项目目录:

1.数据库
认证框架五表设计:
准备user用户表、user_role用户角色关系表、role角色表、 role_permission角色权限关系表、permission权限表;
user用户表:

user_role用户角色关系表:

role角色表:

role_permission角色权限关系表:

permission权限表:

2.ssmp环境搭建
2.1准备依赖
<!--web启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--test启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--Mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version><scope>runtime</scope></dependency><!--Mybatisplus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><!--lombok作用于实体类--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--shiro相关坐标--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version></dependency>
2.2 配置Yaml文件
#数据源配置
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rooturl: jdbc:mysql://localhost:3306/shiro?serverTimezone=GMTpassword: 12345678
#Mybatisplus配置
mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.实体类
User用户类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_user")
public class User implements Serializable {@TableId(value = "id",type = IdType.NONE)private String id;@TableField(value = "username")private String username;@TableField(value = "password")private String password;@TableField(value = "salt")private String salt;@TableField(exist = false)private Set<Role> roles;
}
role角色类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_role")
public class Role implements Serializable {private String id;private String name;private String code;private String description;private Set<Permission> permissions;
}
Permission 权限类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_permission")
public class Permission implements Serializable {private String id;private String name;private String code;private String escription;
}
4.三层搭建
DAO层: 因为是五表构造,需要处理关联关系,所以在这里使用级联查询的方式实现:
UserMapper :
@Mapper
public interface UserMapper extends BaseMapper<User> {@Select("select * from pe_user where username =#{username}")User findByName(String name);@Select("select * from pe_user where id =#{id}")@Results({@Result(id = true,property = "id",column = "id"),@Result(property = "username",column = "username"),@Result(property = "password",column = "password"),@Result(property = "salt",column = "salt"),@Result(property = "roles",column = "id",many = @Many(select = "com.apesource.springboot_shiro_01.dao.RoleMapper.findRoleById"))})User findUserDetailById(int id);
}
RoleMapper :
@Mapper
public interface RoleMapper {@Results({@Result(column = "id", property = "id"),@Result(column = "name", property = "name"),@Result(column = "code", property = "code"),@Result(column = "description", property = "description"),@Result(column = "id",property = "permissions",many = @Many(select = "com.apesource.springboot_shiro_01.dao.PermissionMapper.findPermissionById"))})
// @Select("select * from 角色表 where 角色id in (select 角色ID from 关系表 where 用户id = ?)")@Select("select * from pe_role where id in (select role_id from pe_user_role where user_id = #{id})")public Set<Role> findRoleById(String id);
}
PermissionMapper :
@Mapper
public interface PermissionMapper {@Select("select * from pe_permission where id in (select permission_id from pe_role_permission where role_id = #{id})")@Results({@Result(column = "id", property = "id"),@Result(column = "name", property = "name"),@Result(column = "code",property = "code"),@Result(column = "description", property = "description")})public Set<Permission> findPermissionById(long id);
}
Service层: 因为项目只实现Shrio的用户认证和授权,所以只构造User的业务层就足够了。
UserService :
@Service
public class UserService {@Autowired(required = false)UserMapper mapper;public User findByName(String name){return mapper.findByName(name);}public User findUserDetailById(int id){return mapper.findUserDetailById(id);}
}
Controller层:
@RestController
public class ShiroController {@AutowiredUserService service;/*** @RequiresPermissions() -- 访问此方法必须具备的权限* @RequiresRoles() -- 访问此方法必须具备的角色**/@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "访问个人主页成功";}//添加@RequiresPermissions("user-add")@RequestMapping(value = "/user",method = RequestMethod.POST)public String add() {return "添加用户成功";}//查询@RequiresPermissions("user-find")@RequestMapping(value = "/user/find",method = RequestMethod.GET )public String find(){return "查询成功";}//更新@RequiresPermissions("user-update")@RequestMapping(value = "/user/{id}",method = RequestMethod.PUT)public String update(@PathVariable String id) {return "更新用户成功";}//删除@RequiresPermissions("user-delete")@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)public String delete(@PathVariable String id) {return "删除用户成功";}//未登陆与未授权页面@RequestMapping(value="/autherror")public String autherror() {return "未登录";}//用户登录@RequestMapping(value="/login")public String login(User user) {try {//1.构造登录令牌UsernamePasswordToken upToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());//2.获取subjectSubject subject = SecurityUtils.getSubject();//3.调用subject进行登录subject.login(upToken);return "登录成功";}catch (Exception e) {e.printStackTrace();return "用户名或密码错误";}}
}
5.初始化测试数据
因为在实战场景下,数据库的密码是不可以明文保存的,这样对于数据的安全性是巨大的隐患,而shiro也支持数据加密的功能。
加密工具类:
public class DigestUtil {//算法方式public static final String SHA1 = "SHA-1";public static final String SHA256 = "SHA-256";//加密次数public static final Integer Counts =369;/*** @Description show* @param input 需要散列字符串* @param salt 盐字符串* @return*/public static String show(String input,String salt){return new SimpleHash(SHA1,input,salt,Counts).toString();}/*** @Description 随机获得salt字符串* @return*/public static String generateSalt(){SecureRandomNumberGenerator randomNumberGenerator =new SecureRandomNumberGenerator();return randomNumberGenerator.nextBytes().toHex();}/*** @Description 生成密码字符密文和salt密文* @param* @return*/public static Map<String,String> entryptPassword(String passwordPlain){Map<String,String> map = new HashMap<>();String salt =generateSalt();String password = show(passwordPlain,salt);map.put("salt",salt);map.put("明文password",passwordPlain);map.put("密文密码",password);return map;}
由于没有实现注册功能,所以对于数据库的密文密码通过自己操作工具类输入进去:
public static void main(String[] args) {String name ="张三丰";String password = "123123";Map<String, String> map = entryptPassword(password);System.out.println(map.toString());}
二、Shiro过滤器
Shiro内置了很多默认的过滤器,比如身份验证、授权等相关的,shiro也是通过过滤器的原理来实现认证和授权的扩展功能的。
1.Shiro认证过滤器

2.Shiro授权过滤器

三、springBoot+shiro身份认证
1.创建Realm,重写认证方法doGetAuthenticationInfo
使用@PostConstruct注解修饰的init方法就会在Spring容器的启动时自动的执行
public class MyRealm extends AuthorizingRealm {@AutowiredUserService service;/*** @Description 自定义密码比较器* bean标签 init-method属性*/@PostConstructpublic void initCredentialsMatcher() {//指定密码算法HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestUtil.SHA1);//指定迭代次数hashedCredentialsMatcher.setHashIterations(DigestUtil.Counts);//生效密码比较器setCredentialsMatcher(hashedCredentialsMatcher);}
}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.获取登录的用户名密码(token)UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;String username = token.getUsername();//2.根据用户名查询数据库User user = service.findByName(username);//3.判断用户是否存在或者密码是否一致if(user==null){throw new UnknownAccountException("账户不存在");}//4.如果一致返回安全数据//通过SimpleAuthenticationInfo校验数据//构造方法:安全数据,密码(匿名),混淆字符串(salt),realm域名return new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),"MyRealm");}
2.创建shiro配置类
shiro配置类需要配置八个步骤,比较繁琐,最重要的是shiroFilter过滤器,用于实现非法请求的处理。
@Configuration
public class ShiroConfiguration {/*** 1.创建shiro自带cookie对象*/@Beanpublic SimpleCookie sessionIdCookie(){SimpleCookie simpleCookie = new SimpleCookie();simpleCookie.setName("ShiroSession");return simpleCookie;}//2.创建realm@Beanpublic MyRealm getRealm() {return new MyRealm();}/*** 3.创建会话管理器*/@Beanpublic DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(false);sessionManager.setSessionIdCookieEnabled(true);sessionManager.setSessionIdCookie(sessionIdCookie());sessionManager.setGlobalSessionTimeout(3600000);return sessionManager;}//4.创建安全管理器@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(getRealm());securityManager.setSessionManager(sessionManager());return securityManager;}/*** 5.保证实现了Shiro内部lifecycle函数的bean执行*/@Bean(name = "lifecycleBeanPostProcessor")public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 6.开启对shiro注解的支持* AOP式方法级权限检查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验*/@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());return authorizationAttributeSourceAdvisor;}//8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制@Beanpublic ShiroFilterFactoryBean shiroFilter() {//1.创建过滤器工厂ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();//2.设置安全管理器filterFactory.setSecurityManager(defaultWebSecurityManager());//3.通用配置(跳转登录页面,为授权跳转的页面)filterFactory.setLoginUrl("/autherror");//跳转url地址//4.设置过滤器集合//key = 拦截的url地址//value = 过滤器类型Map<String,String> filterMap = new LinkedHashMap<>();//key:请求规则 value:过滤器名称filterMap.put("/login","anon");//当前请求地址可以匿名访问filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问//在过滤器工程内设置系统过滤器filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}
}
3.Postman测试
成功:

合法请求资源:

失败:

非法请求资源:

四、springBoot+shiro授权,鉴权
1.重写授权方法doGetAuthenticationInfo
授权方法:
操作的时候,判断用户是否具有响应的权限
一定先认证再授权
先认证 – 安全数据
再授权 – 根据安全数据获取用户具有的所有操作权限
//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//1.获取已认证的用户数据String id1 = (String) principalCollection.getPrimaryPrincipal();int i = Integer.parseInt(id1);User user = service.findUserDetailById(i);//2.根据用户数据获取用户的权限信息(所有角色,所有权限)Set<String> roles = new HashSet<>();//所有角色Set<String> perms = new HashSet<>();//所有权限for (Role role : user.getRoles()) {roles.add(role.getName());for (Permission perm : role.getPermissions()) {perms.add(perm.getCode());}}//将角色和权限信息返回SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(perms);info.setRoles(roles);return info;}
2.访问程序资源(鉴权)
注解实现:直接在控制器接口上加注解即可;
注解实现权限:如果权限信息不匹配,抛出异常(则使用异常处理器解决);
常用注解:
@RequiresPermissions – 访问此方法必须具备的权限
@RequiresRoles – 访问此方法必须具备的角色
@RequiresAuthentication – 表明当前用户需是经过认证的用户
@ RequiresGuest --表明该用户需为”guest”用户
@ RequiresUser – 当前用户需为已认证用户或已记住用户
具体实现在controller层的每个业务接口,这里只举例一个:
//个人主页@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "访问个人主页成功";}@RequiresPermissions("user-add")//参数:对应权限的code值@RequestMapping(value = "/user",method = RequestMethod.POST)public String add() {return "添加用户成功";}
3.全局异常处理器
专门用于处理权限不足的异常信息:
@ControllerAdvice
public class BaseExceptionHandler {@ExceptionHandler(value = AuthorizationException.class)@ResponseBodypublic String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e) {return "未授权-异常处理器实现";}
}
4.Postman测试
张三丰用户只有user-home权限,没有add权限;
1.直接访问权限方法会被拦截做登录:

2.访问有权限的资源
成功

3.访问没有权限的资源
失败,被全局异常处理器处理。

相关文章:
Shiro整合SpringBoot,实战下的应用场景
文章目录 前言一、springBootshiro环境准备1.数据库2.ssmp环境搭建3.实体类4.三层搭建5.初始化测试数据 二、Shiro过滤器1.Shiro认证过滤器2.Shiro授权过滤器 三、springBootshiro身份认证1.创建Realm,重写认证方法doGetAuthenticationInfo2.创建shiro配置类3.Postman测试 四、…...
C语言——全局变量和局部变量重名了会怎么样
前言 (1)今天在交流群里面看到这样一个问题: 为什么这个程序中下面我定义的void型函数smart在全局变量前声明了,但是在man函数中调用了smart函数,m的值打印出来还是0。 #include<stdio.h>int m; void smart(void);int main(…...
linux下vi或vim操作Found a swap file by the name的原因及解决方法--九五小庞
在linux下用vi或vim打开Test.java文件时 [rootlocalhost tmp]# vi Test.java出现了如下信息: E325: ATTENTION Found a swap file by the name ".Test.java.swp" owned by: root dated: Wed Dec 7 13:52:56 2011 file name: /var/tmp/Test.java modif…...
通过RD Client远程连接windows电脑踩坑点
通过RD Client远程连接windows电脑操作的个人踩坑点,记录下来,防止下一次还犯。 配置: win10专业版腾讯云服务器Ubuntu22.04小米平板RD client 首先是安装frp 这一部分参考的是:通过RD Client远程连接windows电脑(…...
学习node之——如何在项目中使用MySQL、前后端的身份认证
上一篇文章只写了一丢丢,这篇才是正片,look look look 一、使用mysql模块操作数据库 1、查询数据 这里连接数据库的用户和密码都是我们在安装mysql时配置的密码。每个人的users表格里面数据不同,结果也会不一样哟! // 导入mys…...
AUTOSAR从入门到精通-【应用篇】参照AUTOSAR架构的柴油车后处理集成电控系统软件设计与研究(续)
目录 3.3底层驱动模块开发 3.3.1利用S-Function编写底层驱动模块 3.3.2编写TLC文件来控制自动代码生成过程...
Linux 内核动态打印调试(dev_info、 dev_dbg )
目录 前言 1 printk消息级别 2 调整内核printk打印级别 3 dev_xxx函数简介 4 配置内核使用动态打印 5 动态调试使用方法 6 动态打印调试的基本原理 🎈个人主页🎈:linux_嵌入式大师之路的博客-CSDN博客🎉🎉&…...
深入浅出AXI协议(3)——握手过程
一、前言 在之前的文章中我们快速地浏览了一下AXI4协议中的接口信号,对此我们建议先有一个简单的认知,接下来在使用到的时候我们还会对各种信号进行一个详细的讲解,在这篇文章中我们将讲述AXI协议的握手协议。 二、握手协议概述 在前面的文章…...
Ansible学习笔记5
copy模块:(重点) copy模块用于对文件的远程拷贝(如把本地的文件拷贝到远程主机上。) 在master的主机上准备一个文件,拷贝文件到group1的所有主机上。 这个用的频率非常高,非常有用的一个模块…...
LeetCode 面试题 02.06. 回文链表
文章目录 一、题目二、C# 题解 一、题目 编写一个函数,检查输入的链表是否是回文的。 点击此处跳转题目。 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true …...
linux环境没有curl或者telnet命令解决方法与区分linux环境类型
如何区分你当前使用的 Linux 系统是 Ubuntu、CentOS 还是 Alpine,查看 /etc/os-release 文件 [rootlocalhost ~]# cat /etc/os-release NAME"CentOS Linux" VERSION"7 (Core)" ID"centos" ID_LIKE"rhel fedora" VERSION_I…...
golang channel
channel是不同协程之间异步通信的数据结构。 基本用法 1 构造 ch:make(chan int)//无缓冲 ch:make(chan int,10)//有缓冲2 读操作 val:<-ch <-ch val,ok:<-ch3 写 var data int ch<-data4 关闭 close(ch)5 多路复用 select{ case <-parent.Done():child.…...
高等职业学校物联网实训室建设方案
一、概述 1.1专业背景 物联网(Internet of Things)被称为继计算机、互联网之后世界信息产业第三次浪潮,它并非一个全新的技术领域,而是现代信息技术发展到一定阶段后出现的一种聚合性应用与技术提升,是随着传感网、通…...
Python基础学习第四天:Python注释
创建注释 注释以 # 开头,Python 将忽略它们: 实例 #This is a comment print("Hello, World!")运行实例 注释可以放在一行的末尾,Python 将忽略该行的其余部分: 实例 print("Hello, World!")…...
Puppeteer中使用Stealth.min.js库
这里需要安装npm install puppeteer-extra puppeteer-extra-plugin-stealth,然后,在启动浏览器时,Puppeteer 会自动应用 Stealth.min.js 插件的功能。 const puppeteer require(puppeteer-extra); const StealthPlugin require(puppeteer-…...
JVM ZGC垃圾收集器
ZGC垃圾收集器 ZGC(“Z”并非什么专业名词的缩写,这款收集器的名字就叫作Z Garbage Collector)是一款在JDK 11中新加入的具有实验性质[1]的低延迟垃圾收集器,是由Oracle公司研发的。 ZGC收集器是一款基于Region内存布局的&#…...
事务管理-事务进阶-propagation属性
目录 事务属性-传播行为 propagation 案例 需求 步骤 具体代码 小结 事务属性-传播行为 propagation 事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。即如果事务方法A中调用了事务方法B,…...
树多选搜索查询,搜索后选中状态仍保留
<template><div class"half-transfer"><div class"el-transfer-panel"><div><el-checkbox v-model"selectAll" change"handleSelectAll">全部</el-checkbox></div><el-input v-model&qu…...
数据结构--字典树(trie)
概念: Trie 是一种能够快速插入和查询字符串的多叉树结构。、 节点的编号各不相同,根节点编号为0,其他节点用来标识路径,还可以标记单词的插入次数,边表示字符。 tire 维护字符串的集合,支持两种操作&…...
iframe通过postMessage进行跨域通信以及在Angular中使用
写在前面 在前端开发过程中,会遇到一些需要使用iframe的场景,使用iframe关键的一个点是数据之间的传输,基于同源的要求十分苛刻,大家基本上是都是跨域的,如果跨域进行数据传输呢? 大家使用的比较多的就是p…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...
