Centos7系统升级OpenSSH到openssh-8.*版本的方法

Centos7系统升级OpenSSH到openssh-8.*版本的方法

Centos7 系统默认源中 OpenSSH 最新版本是 OpenSSH_7.4p1,因此无法通过:yum update -y openssh命令升级到更高版本。然而 OpenSSH_7.4p1 版本已经严重落后并被爆出有多个高危漏洞,下面本文就分享一下 Centos7.* 系统升级 OpenSSH 到 openssh-8.* 版本的方法。注意:升级前请务必对系统进行快照备份,以免升级过程中出现异常无法恢复。下载rpm包(以8.8示例)wget https://www.02405.com/wp-content/uploads/2022/06/openssh8.8-c7.tar.gz tar -zxvf openssh8.8-c7.tar.gz安装方法一:rpm -Uvh *.rpm安装方法二(此方法会自动处理依赖关系,推荐使用):yum install ./*.rpm部分机器使用方法二安装会提示依赖问题,可以使用以下方法:yum update *.rpm至此,升级完成,如果之前升级过的,下面的就不用看了,直接新开SSH终端连接即可。因为 OpenSSH 升级后,/etc/ssh/sshd_config 会还原至默认状态,我们需要进行相应配置:cd /etc/ssh/ chmod 400 ssh_host_ecdsa_key ssh_host_ed25519_key ssh_host_rsa_key echo "PermitRootLogin yes" >> /etc/ssh/sshd_config echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config systemctl restart sshd注意:升级后重启SSH可能出现以下错误:It is required that your private key files are NOT accessible by others. This private key will be ignored. Unable to load host key "/etc/ssh/ssh_host_ed25519_key": bad permissions Unable to load host key: /etc/ssh/ssh_host_ed25519_key sshd: no hostkeys available -- exiting. sshd.service: control process exited, code=exited status=1 Failed to start SYSV: OpenSSH server daemon. Unit sshd.service entered failed state. sshd.service failed.解决办法:chmod 0600 /etc/ssh/ssh_host_ed25519_key service sshd restart即可解决。注意,/etc/pam.d/sshd 也文件会被覆盖,我们进行还原:先清空:>/etc/pam.d/sshd;再还原:echo '#%PAM-1.0 auth required pam_sepermit.so auth include password-auth account required pam_nologin.so account include password-auth password include password-auth # pam_selinux.so close should be the first session rule session required pam_selinux.so close session required pam_loginuid.so # pam_selinux.so open should only be followed by sessions to be executed in the user context session required pam_selinux.so open env_params session optional pam_keyinit.so force revoke session include password-auth'>/etc/pam.d/sshd至此,升级完成,先别关闭终端,直接新开一个终端,连接到服务器测试。注意:如果新开终端连接的时,root 密码报错,并且已经根据上面后续操作,那可能就是 SElinux 的问题,我们进行临时禁用:setenforce 0即可正常登录,然后修改 /etc/selinux/config 文件:sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config进行永久禁用SElinux即可。附:8.5-8.8版本编译好的rpm包文件,你也可以下载后手动上传到服务器安装,安装方法与上面相同。openssh-8.5p1-1.el7_.tar下载openssh8.6-c7下载openssh8.8-c7.tar下载

2025-12-04 269 阅读 0 评论
Spring事务失效的常见陷阱与解决方案

Spring事务失效的常见陷阱与解决方案

本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。原理Spring事务的原理是:通过AOP切面的方式实现的,也就是通过代理模式去实现事务增强。具体过程是:对包含@Transactional注解的方法进行拦截,然后重写,重新在方法里加入异常回滚的逻辑。而且,每个线程都是独立管理自己的事务,相互隔离。原理简单,使用起来也简单,也就是在方法上打上@Transactional注解,然后事务就正常生效了。也很少有人去验证异常情况下是否能真正的回滚。Spring事务让我熟悉的地方是哪哪看起来都简单,让我陌生的地方使用时的变种较多,有时候莫名其妙的不生效。源码以上原理的相关源码如下:实践出真知有时候在编码过程中,我会发现某些场景下的事务会失效,总有一些意想不到的情况和隐藏的坑等着你去发现。 我认为验证事务的最佳方法就是:记住基本原则 + 动手实践。记住基本原则能帮助你快速解决常规问题,而动手实践则能验证那些不常见或不确定的问题。几种事务不生效的用法以下是几种常见的Spring事务失效的情况,读者们一定要牢记。这不仅对日常编码非常有帮助,还能在面试时展示你的知识。private方法Spring是通过AOP代理的方式实现事务增强的,但是private方法无法被代理,所以在private方法上打@Transactional注解是不生效的。final、static修饰的方法和private方法类似,final和static修饰的方法也无法被代理,所以@Transactional注解也不生效。因为,static是属于类方法,final修饰的方法无法被重写,自然也就无法植入事务增强代码。Bean对象没有被Spring托管某个类一定要被Spring托管,那才能通过@Transactional注解去增强事务。如果只有@Transactional注解,而没有把类交给Spring托管,事务也是不生效的。类似如下情况:// 此处没有@Service注解,此类不被spring托管,及时有@Transactional也不生效 public class UserService { @Autowired private UserMapper userMapper; @Transactional public final void createAndUpdateUser() { createUser(); updateUserById(); } public void createUser() { User user = new User(); user.setId(2L); user.setName("test2"); user.setEmail("test2" + "@test.com"); userMapper.insert(user); System.out.println("create user"); } public void updateUserById() { User user = userMapper.findById(1L); user.setName("admin1"); userMapper.update(user); int i = 1 / 0; // 此处会抛出异常 System.out.println("update user"); } }异常被吞掉如果在业务代码里,通过try……catch捕获了异常,同时又没有继续抛出异常时,Spring事务也是不生效的。因为代理增强的逻辑就是要发现了异常,才能回滚事务。如果异常被方法本身吞掉了,则代理会认为没有异常,从而无法回滚。非RuntimeException异常Spring事务默认会回滚RuntimeException 及其子类,以及 Error 类型的异常。如果是其余异常,则不会回滚。源码处可见:这种非RuntimeException异常场景下,需要做2个动作从而保证事务回滚。捕获异常,然后抛出自定义异常。自行在@Transactional注解中增加@Transactional(rollbackFor = XxxxxxxException.class)属性。或者直接使用rollbackFor = Exception.class,也就免去了第一步。异步线程的场景多个线程的场景下,只需要牢记每个线程只管理自己的事务即可。每个线程都有一个独立的事务上下文,存在ThreadLocal中,所以事务信息在不同线程之间是隔离的。重灾区:在同一个类中调用本类的方法这个失效场景,是最容易出错的,而且变种还多。在同一个类中调用本类的方法时,牢记以下2点,即可破局:是否会开启事务依赖此类的第一个被外部调用的方法。如果此类的第一个被外部调用的方法有@Transactional注解,那事务生效。调用自己内部方法时,采用的是this.xxxMethod()的方式,这种方式是不会走AOP代理的,所以被调用的内部方法的@Transactional注解不生效。如果确实需要调用内部方法,并且要事务生效的话,那只能将被调用的内部方法独立到新的类中,同时交给Spring管理。一道面试题以上关于事务不生效的用法都比较好记,只有在同一个类中调用本类的方法场景下存在多种变种。具体请看这道面试题。请问以下createAndUpdateUser方法的事务生效吗?@Service public class UserService { @Autowired private UserMapper userMapper; @Transactional public final void createAndUpdateUser() { //注意这里有final修饰 createUser(); updateUserById(); } @Transactional public void createUser() { User user = new User(); user.setId(2L); user.setName("test2"); user.setEmail("test2" + "@test.com"); userMapper.insert(user); System.out.println("create user"); } @Transactional(rollbackFor = Exception.class) public void updateUserById() { User user = userMapper.findById(1L); user.setName("admin1"); userMapper.update(user); int i = 1 / 0; // 此处会抛出异常 System.out.println("update user"); } }如果按照重灾区:在同一个类中调用本类的方法里提到的2个原则,则事务全部生效。如果按照final、static修饰的方法里提到的原则,则事务全部不生效。那结果如何呢?结果是以上方法的事务全部生效。为什么呢?这里在补充一个原则:final修饰的方法如果带上@Transactional注解,事务情况按照被调用的方法自身的事务托管情况而定。因为以上代码中的createUser方法和updateUserById方法,都有@Transactional注解,所以都生效。这种特殊情况也实在是让人瞠目,不过只需要牢记以上几种不生效的用法即可,谁没事儿写这种@Transactional + final的代码呢?除了面试会问……

2025-12-04 285 阅读 0 评论
PHP(Laravel) 实现 sm2、sm3、sm4 加密,对接招行薪福通教程

PHP(Laravel) 实现 sm2、sm3、sm4 加密,对接招行薪福通教程

背景单位最近的业务基本都迁移到招行的薪福通系统上了,但是还是有一些特殊需求要实现,需要对接薪福通的API。银行的api加密方式基本都是固定的国密算法,招行薪福通使用了其中的sm2、sm3和sm4,关于加密算法简单说明一下:SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法,基于ECC。其签名速度与秘钥生成速度都快于RSA,非对称加密,该算法已公开SM3是与SM2一样是由国家密码管理局于2010年12月17日发布的。SM3主要用数字签名及验证、消息认证码生成及验证、随机数生成等,其安全性及效率与SHA-256相当。可以用MD5作为对比理解。校验结果为256位,不可逆,该算法已公开。SM4是国家密码管理局于2012年3月21日发布。对称加密,密钥长度和分组长度均为128位。其他语言如 java 或者 c# 都有比较成熟的密码库支持 sm2、sm3、sm4 加密,而且银行的 sdk 示例基本也只有这两种语言的版本。百度一番发现 php 也有一位大神开源了一个库:https://github.com/lpilp/phpsm2sm3sm4。本文就基于这个库进行开发。安装composer require lpilp/guomi请确保你升级到 composer 2 及以上版本。PHP >=7.2并且打开 gmp 组件支持。我的 PHP 版本是 8.2.9,安装过程中提示:nette/schema 依赖错误,版本不支持 8.2.9。解决办法是修改composer.lock 文件,将依赖的 nette/schema 版本改为较新的支持 php 8.2 的版本,我这里改成 nette/schema v1.3.0 版本后,再次执行 composer 成功安装。签名招行 API 中分别用到了 sm2、sm3、sm4 三种加密算法,其中 sm3、sm4 比较简单,参考 phpsm2sm3sm4 的示例写就可以了。本文重点讲一下 sm2,我被卡了两天。按照招行文档的说明,需要采用 SM2 算法对请求类型+请求路径+请求体 body +请求时间戳的字符串加签。先生成签名字符串signStr,拼接规则为: “POST ” + path + “\n” + “x-alb-digest: ” + body字符串 + “\n” + “x-alb-timestamp: ” + x-alb-timestamp对应的值。https://xft.cmbchina.com/open/#/doc/open-document?id=10692&mid=10684这里需要注意的是,因为拼接的文档里包含换行符,因此拼接的时候换行符要用双引号,而不能是单引号。接下来是对签名字符串进行加签操作,招行文档说的是sm2withsm3,我找了半天也没找到扩展包里有对应的方法,后来发现实际就是sm2加密。//需要加签的字符串拼接 $signStr = "POST " . $url . "?" . $this->flatten($config) . "\n" ."x-alb-digest: ". $body . "\n" . "x-alb-timestamp: " . $timestampS; //通过sm2算法加签,第二个参数false表示每次随机生成签名。 $sm2 = new RtSm2('base64',false); $apiSign = $sm2->doSign($signStr,config('xft.pro.authoritySecret'),'1234567812345678'); //打印签名 echo $apiSign;打印的签名类似:MEQCIA7kwAnoDZFLaqn1MknKHMEUFG7s5CCTTxdPAfyXLIM1AiB7yt0HTKANsY+H5jgP/gxC+aIYfWUxPe01jtO8kqzwew==拿到招行验签网站(https://xft.cmbchina.com/open/#/simulate-tool)验签一直提示签名失败。关键是招行的验签网站也没说明签名是个什么格式的数据,提供的 java sdk 还是编译过的没有源代码,后来发现 c# 的官方 sdk 是有源码版的,遂下载 c# 的 sdk 跑了一下,发现 sdk 生成的签名是一个固定128位的字符串。回到 phpsm2sm3sm4 的 github 主页提了个 issues,作者回复的很快,原来 sm2 加密方法缺省返回是asn1(r,s)格式的 base64 字符串,而招行的签名只是r+s的字符串组合,而作者也封装了相应的转换函数在 src/util/SmSignFormatRS.php 文件。因此对得到的签名进行一下格式转换即可://需要加签的字符串拼接 $signStr = "POST " . $url . "?" . $this->flatten($config) . "\n" ."x-alb-digest: ". $body . "\n" . "x-alb-timestamp: " . $timestampS; //通过sm2算法加签,第二个参数false表示每次随机生成签名。 $sm2 = new RtSm2('base64',false); $apiSign = $sm2->doSign($signStr,config('xft.pro.authoritySecret'),'1234567812345678'); //对签名进行格式转换 $apiSign = bin2hex(base64_decode(SmSignFormatRS::asn1_to_rs($apiSign))); //打印签名 echo $apiSign;这样得到的签名类似:4d510e0a696d3f679b3ff576502686d93c8f2ccc68d4aa54f5911359750520434fd107edd4535230d776ca0602134a08b4de607a618a648c8b048bb68dba4e0e再次验签顺利通过。附 PHP(Laravel)对接招行薪福通、实现 sm2、sm3、sm4加密的示例代码,以全量获取组织列表API为例:https://xft.cmbchina.com/open/#/doc/open-document?id=10692&mid=11616$microTime = microtime(true); $timestampMs = round($microTime * 1000);//当前时间戳,毫秒 $timestampS = floor($microTime);//当前时间戳,秒 $body = '{}'; $key = substr(config('xft.pro.authoritySecret'),0,32); $arr = $this->sm4(hex2bin($key),$body); $body = json_encode($arr);//此处返回的是加密的body密文,与招行模拟器计算结果相同 $sm3 = new RtSm3(); $xAlbDigest = $sm3->digest($body);//此处返回的是对加密后的body内容按照SM3算法进行摘要签名,与招行模拟器计算结果相同。 $domain = 'https://api.cmbchina.com'; $url = '/ORG/orgqry/common/OPORGQRA'; $config = [ 'CSCAPPUID' => config('xft.pro.appid'), //APPID 'CSCPRJCOD' => 'XFV12345', //企业ID 'CSCUSRNBR' => 'A0001', 'CSCUSRUID' => 'AUTO0001', 'CSCREQTIM' => $timestampMs ]; $signStr = "POST " . $url . "?" . $this->flatten($config) . "\n" ."x-alb-digest: ". $body . "\n" . "x-alb-timestamp: " . $timestampS; $sm2 = new RtSm2('base64',false); $apiSign = $sm2->doSign($signStr,config('xft.pro.authoritySecret'),'1234567812345678'); $apiSign = bin2hex(base64_decode(SmSignFormatRS::asn1_to_rs($apiSign))); $response = Http::withHeaders([ 'appid' => config('xft.pro.appid'), 'x-alb-timestamp' => $timestampS, 'x-alb-verify'=>"sm3withsm2", 'x-alb-digest' => $xAlbDigest, 'apisign' => $apiSign, 'KeepAlive' => false, 'UserAgent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11', ]) ->withBody($body,'application/json') ->post($domain . $url . "?" . $this->flatten($config)); $sm4 = new RtSm4(hex2bin($key)); $responseBody = $sm4->decrypt($response->body(),'sm4-ecb'); dd(json_decode($responseBody));转载博主: https://www.02405.com/archives/12558

2025-12-03 302 阅读 0 评论
Nginx 中 $request_uri 和 $uri 之间的区别

Nginx 中 $request_uri 和 $uri 之间的区别

$request_uriThis variable is equal to the original request URI as received from the client including the args. It cannot be modified. Look at $uri for the post-rewrite/altered URI. Does not include host name. Example: “/foo/bar.php?arg=baz”这个变量等于从客户端发送来的原生请求URI,包括参数。它不可以进行修改。一句话总结:$request_uri 包含请求的文件名和路径及所有参数。客户端请求的 URI 比如:/foo/bar.php?arg=baz,那么 $request_uri 就是:/foo/bar.php?arg=baz$uriThis variable is the current request URI, without any arguments (see $args for those). This variable will reflect any modifications done so far by internal redirects or the index module. Note this may be different from $request_uri, as $request_uri is what was originally sent by the browser before any such modifications. Does not include the protocol or host name. Example: /foo/bar.html这个变量指当前的请求URI,不包括任何参数(见$args)。一句话总结:$uri 只包含请求的文件名和路径,不包含包含“?”或“#”等参数。客户端请求的 URI 比如:/foo/bar.php?arg=baz,通过 $uri 获取的只有:/foo/bar.php

2025-12-03 253 阅读 0 评论
移动硬盘没有新建选项不能新建文件夹或文件(写保护)的解决办法

移动硬盘没有新建选项不能新建文件夹或文件(写保护)的解决办法

一、在键盘上同时按下Win+R键,打开运行对话框,输入cmd命令,打开Dos窗口。二、在命令行窗口下输入diskpart命令,稍等片刻进入Diskpart磁盘管理工具。三、输入list disk列出磁盘信息,通过磁盘大小区分移动硬盘或U盘。四、使用select disk number命令选中要操作的移动硬盘,比如我这里要选择磁盘1,那么就输入:select disk 1五、输入att disk clert readonly命令清除磁盘写保护属性。重新进入移动硬盘,现在就可以正常新建文件(夹)了。

2025-12-03 310 阅读 0 评论
Laravel 批量新增或更新方法 upsert 详解

Laravel 批量新增或更新方法 upsert 详解

Laravel 8 起新增了一个upsert方法,用来实现批量更新或新增,如果你想在单个查询中执行多个「新增或更新」,那么应该使用 upsert 方法。该方法的第一个参数包含要插入或更新的值,而第二个参数列出了在关联表中唯一标识记录的列。该方法的第三个也是最后一个参数是一个列数组,如果数据库中已经存在匹配的记录,则应该更新这些列。如果在模型上启用了时间戳,upsert 方法将自动设置 created_at 和 updated_at 时间戳:Flight::upsert([ , ], , );注意除 SQL Server 外,其他所有数据库都要求 upsert 方法的第二个参数中的列具有主键索引或唯一索引。 此外,MySQL 数据库驱动程序忽略了 upsert 方法的第二个参数,总是使用表的主键索引和唯一索引来检测现有的记录Laravel 8 之前的版本,如果我们想要批量更新或新增,只能通过循环+updateOrCreate的方式,每次循环都要请求两次数据库,但是有了upsert我们一共只需要执行两次数据库操作。另外要说明的是,第二个参数数组是用来标识唯一记录的,可以使用主键索引,比如:自增id,也可以使用唯一索引。比如下面的代码:Record::upsert($data,);必须要在数据库上设置 user_id + date 的唯一索引才可以,否则就会不断的执行新增操作。

2025-12-03 298 阅读 0 评论
yum提示Error: rpmdb open failed错误的解决办法

yum提示Error: rpmdb open failed错误的解决办法

centos系统使用yum命令安装软件包时候报错: error: rpmdb: BDB0113 Thread/process 2229/140657048729600 failed: BDB1507 Thread died in Berkeley DB library error: db5 error(-30973) from dbenv->failchk: BDB0087 DB_RUNRECOVERY: Fatal error, run database recovery error: cannot open Packages index using db5 -  (-30973) error: cannot open Packages database in /var/lib/rpm CRITICAL:yum.main: Error: rpmdb open failed 原因是RPM数据库被破坏,重建数据库后恢复正常: cd /var/lib/rpm/ for i in `ls | grep 'db.'`;do mv $i $i.bak;done rpm --rebuilddb yum clean all

2025-12-03 224 阅读 0 评论
右上角角标0-9,右下角角标0-9分享

右上角角标0-9,右下角角标0-9分享

在进行理科运算时,有时我们需要输入角标,比如3的8次方:3⁸或者水的方程式:H₂O等。在搜狗输入法的数学符号中角标只有0-3,没有其他的,因此本文分享一下右上角角标0-9和右下角角标0-9以及一些特殊的角标符号,直接复制即可,支持在网页、文档、微信等软件中显示。上标(右上角角标)º ¹ ² ³ ⁴⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ⁿ ′ ½下标(右下角角标)₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎

2025-12-02 308 阅读 0 评论
Laravel提示:CSRF Token Mismatch.的解决办法

Laravel提示:CSRF Token Mismatch.的解决办法

Laravel程序提示:CSRF Token Mismatch.错误是因为默认启用 CSRF 保护,解决办法有两种:方法一:从 CSRF 保护中排除 URI有时你可能希望从 CSRF 保护中排除一组 URI。比如接入支付宝或者微信支付后接受回调信息的 URI ,因为支付宝或微信不会要向您的路由发送 CSRF 令牌。通常,你应该将这些类型的路由放在 App\Providers\RouteServiceProvider 应用于 routes/web.php 文件中的所有路由的 web 中间件组之外。但是,你现在也可以通过将路由的 URI 添加到 VerifyCsrfToken 中间件的 $except 属性来排除路由:<?php namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; class VerifyCsrfToken extends Middleware { /** * 从 CSRF 验证中排除的 URI。 * * @var array */ protected $except = [ 'alipay/notify', 'https://www.02405.com/wechat/notify', ]; }方法二、在表单或者ajax请求中添加 CSRF 令牌表单代码示例:<form method="POST" action="https://www.02405.com/profile"> @csrf <!-- 或者(标红的代码功能相同,选择任意一种即可) --> <input type="hidden" name="_token" value="{{ csrf_token() }}" /> </form>ajax 请求示例:首先将令牌存储在 HTML 的 meta 标签中:<meta name="csrf-token" content="{{ csrf_token() }}">然后,就可以指示 jQuery 之类的库自动将令牌添加到所有请求标头。这为使用传统 JavaScript 技术的基于 AJAX 的应用程序提供了简单、方便的 CSRF 保护:$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta').attr('content') } });总结通常情况下,建议使用方法二来解决 CSRF Token Mismatch. 问题,这样一般会更安全。只有在特定的需求下并且我们会对请求参数进行严格的校验时才使用方法一。

2025-12-02 328 阅读 0 评论