jeecg库login登录过程分析笔记
jeecg库(版本jeecg-boot-v3.5.1last)实现了用户登录功能,二开时为了借鉴jeecg用户登录的方法,跑了一遍登录方法:
org.jeecg.modules.system.controller.LoginController#login
定义这个方法的类的路径是:
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java
截图:

login方法代码:
@ApiOperation("登录接口")@RequestMapping(value = "/login", method = RequestMethod.POST)public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel){Result<JSONObject> result = new Result<JSONObject>();String username = sysLoginModel.getUsername();String password = sysLoginModel.getPassword();//update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户if(isLoginFailOvertimes(username)){return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!");}//update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户//update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题//前端密码加密,后端进行密码解密//password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword().replaceAll("%2B", "\\+")).trim();//密码解密//update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题//update-begin-author:taoyan date:20190828 for:校验验证码String captcha = sysLoginModel.getCaptcha();if(captcha==null){result.error500("验证码无效");return result;}String lowerCaseCaptcha = captcha.toLowerCase();//update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906// 加入密钥作为混淆,避免简单的拼接,被外部利用,用户自定义该密钥即可String origin = lowerCaseCaptcha+sysLoginModel.getCheckKey()+jeecgBaseConfig.getSignatureSecret();String realKey = Md5Util.md5Encode(origin, "utf-8");//update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906Object checkCode = redisUtil.get(realKey);//当进入登录页时,有一定几率出现验证码错误 #1714if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode);result.error500("验证码错误");// 改成特殊的code 便于前端判断result.setCode(HttpStatus.PRECONDITION_FAILED.value());return result;}//update-end-author:taoyan date:20190828 for:校验验证码//1. 校验用户是否有效//update-begin-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为falseLambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(SysUser::getUsername,username);SysUser sysUser = sysUserService.getOne(queryWrapper);//update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为falseresult = sysUserService.checkUserIsEffective(sysUser);if(!result.isSuccess()) {return result;}//2. 校验用户名或密码是否正确String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());String syspassword = sysUser.getPassword();if (!syspassword.equals(userpassword)) {//update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户addLoginFailOvertimes(username);//update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户result.error500("用户名或密码错误");return result;}//用户登录信息userInfo(sysUser, result);//update-begin--Author:liusq Date:20210126 for:登录成功,删除redis中的验证码redisUtil.del(realKey);//update-begin--Author:liusq Date:20210126 for:登录成功,删除redis中的验证码redisUtil.del(CommonConstant.LOGIN_FAIL + username);LoginUser loginUser = new LoginUser();BeanUtils.copyProperties(sysUser, loginUser);baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser);//update-end--Author:wangshuai Date:20200714 for:登录日志没有记录人员return result;}
看起来非常多,但学过源码分析课就知道,大部分都是准备过程,关键代码不多。
(源码分析课我看的马士兵教育连鹏举老师的)马士兵全套Spring源码深度解析:AOP、IOC、Bean生命周期、循环依赖、事务、SpringBoot自动装配等_哔哩哔哩_bilibili
前三行功能是,创建返回结果空对象,取用户端传递的用户名和密码(原始密码,未加密):
Result<JSONObject> result = new Result<JSONObject>();
String username = sysLoginModel.getUsername();
String password = sysLoginModel.getPassword();
紧接着一个判断语句,看用户是否频繁登录,这里产生了一个支线任务,为了避免本文过于冗长,只走正常登录相关的主线,有精力再研究支线:
if(isLoginFailOvertimes(username)){return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!");
}
接下来取图片验证码,captcha是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称,判断来登录的是不是爬虫程序。包含一个if(captcha==null)支线。
String captcha = sysLoginModel.getCaptcha();
if(captcha==null){result.error500("验证码无效");return result;
}
String lowerCaseCaptcha = captcha.toLowerCase();
下面是判断用户填写验证码是否正确的过程,代码复杂是因为做了加密处理,然后从redis中取验证码的原始字符串,因为这段代码没有生成验证码的过程,我大胆猜测下,图片验证码是根据redis中的checkCode生成的。后面还有一个验证码错误的支线,有精力可以研究。
String origin =lowerCaseCaptcha+sysLoginModel.getCheckKey()+jeecgBaseConfig.getSignatureSecret();
String realKey = Md5Util.md5Encode(origin, "utf-8");
Object checkCode = redisUtil.get(realKey);
if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode);
result.error500("验证码错误"); // 改成特殊的code 便于前端判断 result.setCode(HttpStatus.PRECONDITION_FAILED.value());
return result; }
login方法代码前一半分析完了,很粗略但正合适,因为这部分只是登录的准备工作,以及一些支线任务,内容很简单,不是login方法的关键点。
下面是核心功能:1. 校验用户是否有效
使用了查询数据库,其中queryWrapper.eq(SysUser::getUsername,username)使用了lambda表达式写法,详见:
MybatisPlus:中QueryWrapper<>().lambda使用(条件查询)_.lambda().eq-CSDN博客
更多关于MybatisPlus的知识可以看:
黑马程序员最新MybatisPlus全套视频教程,4小时快速精通mybatis-plus框架_哔哩哔哩_bilibiliLambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getUsername,username);
SysUser sysUser = sysUserService.getOne(queryWrapper);result = sysUserService.checkUserIsEffective(sysUser);
if(!result.isSuccess()) {return result;
}
sysUserService.checkUserIsEffective(sysUser)是确认用户有效性的方法,可以瞟一眼。
org.jeecg.modules.system.service.impl.SysUserServiceImpl#checkUserIsEffective源码:
@Overridepublic Result<?> checkUserIsEffective(SysUser sysUser) {Result<?> result = new Result<Object>();//情况1:根据用户信息查询,该用户不存在if (sysUser == null) {result.error500("该用户不存在,请注册");baseCommonService.addLog("用户登录失败,用户不存在!", CommonConstant.LOG_TYPE_1, null);return result;}//情况2:根据用户信息查询,该用户已注销//update-begin---author:王帅 Date:20200601 for:if条件永远为falsebug------------if (CommonConstant.DEL_FLAG_1.equals(sysUser.getDelFlag())) {//update-end---author:王帅 Date:20200601 for:if条件永远为falsebug------------baseCommonService.addLog("用户登录失败,用户名:" + sysUser.getUsername() + "已注销!", CommonConstant.LOG_TYPE_1, null);result.error500("该用户已注销");return result;}//情况3:根据用户信息查询,该用户已冻结if (CommonConstant.USER_FREEZE.equals(sysUser.getStatus())) {baseCommonService.addLog("用户登录失败,用户名:" + sysUser.getUsername() + "已冻结!", CommonConstant.LOG_TYPE_1, null);result.error500("该用户已冻结");return result;}return result;}
都是简单的逻辑判断,判断过程不需要数据库交互。
回到login方法,核心功能2. 校验用户名或密码是否正确,注意从数据库获取的密码是加密后的,前台提交的密码也需要使用相同的MD5加密方式加密,才能验证是否与数据库存储的密码相等。
String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
String syspassword = sysUser.getPassword();
if (!syspassword.equals(userpassword)) {//平台用户登录失败锁定用户addLoginFailOvertimes(username);//平台用户登录失败锁定用户result.error500("用户名或密码错误");return result;
}
最后一段执行登录过程,在前面验证都通过后,才完成登录认证过程,包括将登录用户的token存入redis,以便后续请求直接比对token,userInfo(sysUser, result);中执行的就是向redis存入token和其他用户信息的操作。
//用户登录信息
userInfo(sysUser, result);
//登录成功,删除redis中的验证码
redisUtil.del(realKey);
//登录成功,删除redis中的验证码
redisUtil.del(CommonConstant.LOGIN_FAIL + username);
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser);//登录日志没有记录人员
return result;
userInfo(sysUser, result)引用包名:
org.jeecg.modules.system.controller.LoginController#userInfo
private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result) {String username = sysUser.getUsername();String syspassword = sysUser.getPassword();// 获取用户部门信息JSONObject obj = new JSONObject(new LinkedHashMap<>());//1.生成tokenString token = JwtUtil.sign(username, syspassword);// 设置token缓存有效时间redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);obj.put("token", token);//2.设置登录租户Result<JSONObject> loginTenantError = sysUserService.setLoginTenant(sysUser, obj, username,result);if (loginTenantError != null) {return loginTenantError;}//3.设置登录用户信息obj.put("userInfo", sysUser);//4.设置登录部门List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());obj.put("departs", departs);if (departs == null || departs.size() == 0) {obj.put("multi_depart", 0);} else if (departs.size() == 1) {sysUserService.updateUserDepart(username, departs.get(0).getOrgCode(),null);obj.put("multi_depart", 1);} else {//查询当前是否有登录部门// update-begin--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去SysUser sysUserById = sysUserService.getById(sysUser.getId());if(oConvertUtils.isEmpty(sysUserById.getOrgCode())){sysUserService.updateUserDepart(username, departs.get(0).getOrgCode(),null);}// update-end--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去obj.put("multi_depart", 2);}obj.put("sysAllDictItems", sysDictService.queryAllDictItems());result.setResult(obj);result.success("登录成功");return result;}
userInfo方法,通过用户名和密码(已MD5加密)生成token,并存入redis,同时设置有效期(Token有效期为24小时,Token在reids中缓存时间乘以2),正常登录流程到这里就结束了。
接下来的2.设置登录租户代码sysUserService.setLoginTenant(sysUser, obj, username,result);,小心假设是有关鉴权的,
後面的3.设置登录用户信息和4.设置登录部门,這兩部分實現用戶多部門切換功能的,跟登錄無關。把用戶當前登錄的部門記錄到sys_user表中。
org.jeecg.modules.system.service.impl.SysUserServiceImpl#setLoginTenant源码:
@Overridepublic Result<JSONObject> setLoginTenant(SysUser sysUser, JSONObject obj, String username, Result<JSONObject> result){// update-begin--Author:sunjianlei Date:20210802 for:获取用户租户信息//用户有哪些租户List<SysTenant> tenantList = null;//update-begin---author:wangshuai ---date:20221223 for:[QQYUN-3371]租户逻辑改造,改成关系表------------List<Integer> tenantIdList = relationMapper.getTenantIdsNoStatus(sysUser.getId());if (null!=tenantIdList && tenantIdList.size()>0) {//update-end---author:wangshuai ---date:20221223 for:[QQYUN-3371]租户逻辑改造,改成关系表--------------//-------------------------------------------------------------------------------------//查询有效的租户集合LambdaQueryWrapper<SysTenant> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.in(SysTenant::getId, tenantIdList);queryWrapper.eq(SysTenant::getStatus, Integer.valueOf(CommonConstant.STATUS_1));tenantList = sysTenantMapper.selectList(queryWrapper);//-------------------------------------------------------------------------------------if (tenantList.size() == 0) {return result.error500("与该用户关联的租户均已被冻结,无法登录!");} else {obj.put("tenantList", tenantList);}}// update-end--Author:sunjianlei Date:20210802 for:获取用户租户信息//登录会话租户ID,有效性重置if (tenantList != null && tenantList.size() > 0) {if (tenantList.size() == 1) {sysUser.setLoginTenantId(tenantList.get(0).getId());} else {List<SysTenant> listAfterFilter = tenantList.stream().filter(s -> s.getId().equals(sysUser.getLoginTenantId())).collect(Collectors.toList());if (listAfterFilter == null || listAfterFilter.size() == 0) {//如果上次登录租户ID,在用户拥有的租户集合里面没有了,则随机取用户拥有的第一个租户IDsysUser.setLoginTenantId(tenantList.get(0).getId());}}} else {//无租户的时候,设置为 0sysUser.setLoginTenantId(0);}//设置用户登录缓存租户this.updateUserDepart(username, null,sysUser.getLoginTenantId());log.info(" 登录接口用户的租户ID = {}", sysUser.getLoginTenantId());if(sysUser.getLoginTenantId()!=null){//登录的时候需要手工设置下会话中的租户ID,不然登录接口无法通过租户隔离查询到数据TenantContext.setTenant(sysUser.getLoginTenantId()+"");}return null;}
代码中的操作都是数据库操作,这里需要留个伏笔,鉴权信息存入数据库后,将来如何调取,前台如何按权限显示不同的信息,需要进一步查找shiro的配置。
至此登录逻辑已执行完毕,回到主程序中:
redisUtil.del(realKey);
//登录成功,删除redis中的验证码
redisUtil.del(CommonConstant.LOGIN_FAIL + username);
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser);//登录日志没有记录人员
return result;
剩余的操作是,删除redis验证码等,注意new LoginUser()并不用于登录,仅仅为了记录日志时,对象类型统一,因为已经登录的用户是通过shiro的subject方法获得用户信息的,获取的用户对象正是LoginUser类型。接下来将sysUser中的信息复制到loginUser中,通过baseCommonService.addLog添加到日志中。
整个登录过程分析完毕。登录后,用户再次访问系统时,会进入shiro的认证和授权过程,这部分内容也很重要,留待后续分析。
理解OAuth 2.0 - 阮一峰的网络日志 (ruanyifeng.com)
相关文章:
jeecg库login登录过程分析笔记
jeecg库(版本jeecg-boot-v3.5.1last)实现了用户登录功能,二开时为了借鉴jeecg用户登录的方法,跑了一遍登录方法: org.jeecg.modules.system.controller.LoginController#login 定义这个方法的类的路径是:…...
echarts仪表盘vue
<div class"ybptx" ref"btryzb"></div>mounted() {this.getBtData();},getBtData() {var chart this.$echarts.init(this.$refs.btryzb);var data_czzf 88;var option {series: [{name: 内层数据刻度,type: gauge,radius: 80%,min: 0,max: 1…...
管道和重定向分号-连接符
本文介绍shell脚本常用命令连接符:管道符( | )、重定向( < 、>、>>、2> 、&> )、分号( ; ) 本文内容同微信公众号【凡登】,关注不迷路,学习上高速,欢迎关注共同学习。 1、管道 进程的通信方式之一…...
WSL VScode连接文件后无法修改(修改报错)
权限问题 usrname:用户名 dirpath:要修改的文件夹路径 sudo chown -R usrname /dirpath...
迷你Ceph集群搭建(超低配设备)
我的博客原文链接:https://blog.gcc.ac.cn/post/2023/%E8%BF%B7%E4%BD%A0ceph%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA/ 环境 机器列表: IP角色说明10.0.0.15osdARMv7,512M内存,32G存储,百兆网口10.0.0.16clientARM64…...
Python数据挖掘项目实战——自动售货机销售数据分析
摘要:本案例将主要结合自动售货机的实际情况,对销售的历史数据进行处理,利用pyecharts库、Matplotlib库进行可视化分析,并对未来4周商品的销售额进行预测,从而为企业制定相应的自动售货机市场需求分析及销售建议提供参…...
TortoiseGit使用教程
文章目录 一. 创建仓库二. Clone仓库三. 查看修改记录四. 版本回溯五. 创建分支六. 切换分支七. 合并分支八. 删除分支九. TortoiseGit配置1. 常规配置2. 配置远程仓库账户密码3. 配置远程仓库 一. 创建仓库 在需要创建仓库的文件上右键→Git Create repository here… 创建仓…...
如何测量GNSS信号和高斯噪声功率及载波比?
引言 本文将介绍如何测量德思特Safran GSG-7或GSG-8 GNSS模拟器的输出信号功率。此外,还展示了如何为此类测量正确配置德思特Safran Skydel仿真引擎以及如何设置射频设备,从而使用频谱分析仪准确测量信号的射频功率。 什么是载波噪声密度C/N0 GNSS接收…...
动态壁纸软件iWall mac中文特色
iWall for mac是一款动态壁纸软件,它可以使用任何格式的漂亮视频(无须转换),音频(可视化功能),图片,动画,Flash,gif,swf,程序,网页,网站做为您的动态壁纸&…...
xtrabackup全备 增备
版本针对mysql8.0版本 官方下载地址 https://www.percona.com/downloads 自行选择下载方式 yum安装方式 1、下载上传服务器 安装软件 [rootmaster mysql]# ll percona-xtrabackup-80-8.0.33-28.1.el7.x86_64.rpm -rw-r--r--. 1 root root 44541856 Oct 10 13:25 percona-x…...
【广州华锐互动】灭火器使用VR教学系统应用于高校消防演练有什么好处?
在科技发展的大潮中,虚拟现实(VR)技术以其独特的沉浸式体验赢得了各个领域的青睐,其中包括教育和培训。在高校消防演练中,VR也成为了一种新的消防教育方式。 由广州华锐互动开发的VR消防演练系统,就包含了校…...
Pymol做B因子图
分子动力学模拟结束后,获得蛋白的平均结构, 比如获得的平均结构为WT-average.pdb 然后将平均结构导入到Pymol 中,可以得到B因子图。 gmx rmsf -f md_0_100_noPBC.xtc -s md_0_100.tpr -o rmsf-per-residue.xvg -ox average.pdb -oq bfactors…...
EKF例程 matlab
% 不含IMU误差方程的EKF滤波典型程序,适用于多次滤波的第二级 % author:Evand % date: 2023-09-20 % Ver1 clear;clc;close all; global T %% initial T 0.1; %采样率 t [T:T:100]; Q 0.1diag([1,1,1]);wsqrt(Q)randn(size(Q,1),length(t)); R 1diag([1,1,1]);v…...
【C语言】atoi函数的模拟
atoi对于初学者来说大概率是一个陌生的函数 但不要害怕,我们可以通过各种网站去查询 例如: cplusplus就是一个很好的查询网站 目录 函数介绍模拟实现需要注意的点 函数介绍 我们发现这是一个将字符串转换为整形数字的函数 例如: int main()…...
JAXB 使用记录 bean转xml xml转bean 数组 继承 CDATA(转义问题)
JAXB 使用记录 部分内容引自 https://blog.csdn.net/gengzhy/article/details/127564536 基础介绍 JAXBContext类:是应用的入口,用于管理XML/Java绑定信息 Marshaller接口:将Java对象序列化为XML数据 Unmarshaller接口:将XML数…...
Linux Centos安装Sql Server数据库,结合cpolar内网穿透实现公网访问
目录 前言 1. 安装sql server 2. 局域网测试连接 3. 安装cpolar内网穿透 4. 将sqlserver映射到公网 5. 公网远程连接 6.固定连接公网地址 7.使用固定公网地址连接 前言 简单几步实现在Linux centos环境下安装部署sql server数据库,并结合cpolar内网穿透工具…...
Vulnhub系列靶机---Raven: 2
文章目录 信息收集主机发现端口扫描目录扫描用户枚举 漏洞发现漏洞利用UDF脚本MySQL提权SUID提权 靶机文档:Raven: 2 下载地址:Download (Mirror) 信息收集 靶机MAC地址:00:0C:29:15:7F:17 主机发现 sudo nmap -sn 192.168.8.0/24sudo arp…...
计算机视觉与深度学习 | 视觉惯性SLAM的基础理论
===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 视觉惯性SLAM的基础理论 引言三维空间刚体的运动表示旋转矩阵(Rotatio…...
[电源选项]没有系统散热方式,没有被动散热选项
背景 笔记本的风扇声音太大,想改成被动散热方式,又不想影响性能。 于是我打开了控制面板\所有控制面板项\电源选项,点更改计划设置-> 更改高级电源设置。 想把散热方式改成被动散热。发现win11中好像没有这个选项了! 如何…...
房产中介租房小程序系统开发搭建
随着移动互联网的发展,租房小程序已经成为许多房产中介公司转型线上的重要工具。通过租房小程序,房产中介公司可以方便地展示房源信息、吸引租户、达成交易。那么,如何通过乔拓云网开发租房小程序呢?下面是详细的开发指南。 1.进入…...
从零构建大模型?斯坦福CS336爆火课程带你闯关,附超全学习资源包!
文章介绍了斯坦福大学CS336《从零开始构建语言模型》课程,该课程借鉴操作系统课程理念,带领学生体验语言模型创建的各个环节,包括数据收集、模型构建、训练和评估。课程内容实践性强,需要较多学习开发时间,适合有一定基…...
Open Interpreter连接LM Studio:双引擎部署实战教程
Open Interpreter连接LM Studio:双引擎部署实战教程 1. 开篇:为什么需要本地AI编程助手? 想象一下这样的场景:你手头有一个2GB的CSV数据文件需要分析处理,但云端AI工具有文件大小限制;或者你正在处理敏感…...
从数据到洞察:如何利用2024版建筑高度SHP数据,5步完成城市热岛效应初步分析
从数据到洞察:如何利用2024版建筑高度SHP数据,5步完成城市热岛效应初步分析 城市热岛效应是城市化进程中普遍存在的环境问题,表现为城市中心区域温度明显高于周边郊区的现象。这种现象不仅影响居民的生活质量,还会加剧能源消耗和空…...
RVC与VITS技术对比:检索式vs端到端语音转换的适用场景分析
RVC与VITS技术对比:检索式vs端到端语音转换的适用场景分析 1. 引言 你有没有想过,为什么有些AI翻唱听起来特别像原唱,而有些则感觉“味儿”不太对?或者,为什么有些语音转换工具训练起来飞快,但效果时好时…...
突破硬件限制的跨显卡AI增强方案:OptiScaler游戏画质优化全解析
突破硬件限制的跨显卡AI增强方案:OptiScaler游戏画质优化全解析 【免费下载链接】OptiScaler DLSS replacement for AMD/Intel/Nvidia cards with multiple upscalers (XeSS/FSR2/DLSS) 项目地址: https://gitcode.com/GitHub_Trending/op/OptiScaler OptiSc…...
低资源部署DeepSeek-R1:苹果A17实测120 tokens/s推理速度
低资源部署DeepSeek-R1:苹果A17实测120 tokens/s推理速度 1. 模型概述 DeepSeek-R1-Distill-Qwen-1.5B是DeepSeek团队基于80万条R1推理链样本对Qwen-1.5B进行知识蒸馏得到的轻量级模型。这款"小钢炮"模型仅1.5B参数却能达到7B级模型的推理能力ÿ…...
延时补偿预测器
Active flux基于扰动观测器补偿仿真模型: (1)1.5周期延时补偿 (2)相电压补偿 (2)扰动观测器补偿最近在调试电机控制项目的时候,总遇到Active Flux观测器输出波形抖动的问题。工程师们…...
STM32F103C8T6实战:在最小系统板上运行轻量级TranslateGemma
STM32F103C8T6实战:在最小系统板上运行轻量级TranslateGemma 1. 引言 你有没有想过,在一块只有拇指大小的开发板上运行AI翻译模型?STM32F103C8T6最小系统板,这个通常用来控制LED灯、读取传感器的小家伙,现在居然能跑…...
嵌入式NTP客户端高精度时间同步实现
1. NTP客户端库深度解析:嵌入式系统中的高精度时间同步实现1.1 项目背景与工程痛点NTP(Network Time Protocol)是嵌入式设备实现网络时间同步的核心协议。在工业控制、数据采集、日志记录等场景中,毫秒级甚至亚毫秒级的时间精度直…...
TouchGal Galgame社区终极指南:一站式游戏资源管理与交流平台
TouchGal Galgame社区终极指南:一站式游戏资源管理与交流平台 【免费下载链接】kun-touchgal-next TouchGAL是立足于分享快乐的一站式Galgame文化社区, 为Gal爱好者提供一片净土! 项目地址: https://gitcode.com/gh_mirrors/ku/kun-touchgal-next 还在为寻找…...
