代码世界 – 清风亦平凡 https://www.mlplus.net 关注IT世界,记录平凡生活 Wed, 21 Feb 2024 07:18:59 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.4.3 Wavesurfer.js一个开源的音频可视化库 https://www.mlplus.net/2024/02/21/wavesurfer-js/ https://www.mlplus.net/2024/02/21/wavesurfer-js/#respond Wed, 21 Feb 2024 07:18:59 +0000 https://www.mlplus.net/?p=4524 Wavesurfer.js

Wavesurfer.js是一个基于JavaScript的开源音频波形可视化库。它利用Web Audio APIHTML5 Canvas技术,为音频数据提供了丰富的可视化展示方式。Wavesurfer.js的主要目标是提供一种灵活且易于集成的工具,使开发者能够在网页上创建交互式音频波形可视化。

wavesurfer.js图

特点

  1. 跨平台兼容性:由于Wavesurfer.js是基于Web标准构建的,因此它可以在任何支持Web Audio APICanvas的浏览器上运行,包括桌面和移动设备。
  2. 多格式支持:Wavesurfer.js支持多种音频格式,如WAV、MP3、OGG等,使开发者能够方便地加载和显示不同类型的音频文件。
  3. 高度可定制Wavesurfer.js提供了丰富的配置选项和API,允许开发者根据自己的需求定制波形的外观、行为以及交互方式。
  4. 音频分析功能:除了基本的波形显示,Wavesurfer.js还提供了音频分析功能,如频谱显示、基频检测等,帮助开发者更深入地理解音频数据的特性。
  5. 插件架构Wavesurfer.js采用插件架构,开发者可以通过添加插件来扩展其功能,如添加音频播放控制、区域选择、标记等。
  6. 性能优化Wavesurfer.js在性能方面也进行了优化,即使在处理大型音频文件时也能保持流畅的波形渲染和交互体验。

使用场景

  • 音乐播放器:在音乐播放器的网页应用中,使用Wavesurfer.js可以为用户提供一个直观且吸引人的音频波形展示,增强用户体验。
  • 音频编辑器:对于需要编辑和处理音频的应用,Wavesurfer.js提供了丰富的音频分析功能和可定制性,使开发者能够构建出功能强大的音频编辑器。
  • 教育和学习:在教育和学习领域,Wavesurfer.js可以用于展示音频信号的特性,帮助学生更好地理解音频的概念和原理。
  • 音频可视化艺术Wavesurfer.js的高度可定制性使其成为音频可视化艺术创作的理想选择,开发者可以创造出独特且富有创意的音频波形展示

项目

wavesurfer.js官网wavesurfer

应用

wavesurfer应用UI

总结

Wavesurfer.js是一个功能强大、易于集成且高度可定制的音频波形可视化库,适用于各种需要音频可视化功能的Web应用场景。



转载请注明:清风亦平凡 » Wavesurfer.js一个开源的音频可视化库

]]>
https://www.mlplus.net/2024/02/21/wavesurfer-js/feed/ 0
Docker使用NPM安装CNPM失败 https://www.mlplus.net/2024/01/22/docker-npm-install-cnpm-failed/ https://www.mlplus.net/2024/01/22/docker-npm-install-cnpm-failed/#respond Mon, 22 Jan 2024 03:45:39 +0000 https://www.mlplus.net/?p=4509 背景

项目使用jenkins自动构建应用,原本构建没有没有什么问题,一天突然就发生了异常且多次重试结果并没有改变。为了更快的构建完成项目,所以就找到简单的处理方法来解决问题。

Docker使用NPM安装CNPM失败-第0张图片

异常信息


RUN npm install -g cnpm@7.1.0 –registry=https://registry.npm.taobao.org
—> Running in 47877716e592
[91mnpm ERR! [0m[91mcode CERT_HAS_EXPIRED
[0m[91mnpm ERR![0m[91m errno CERT_HAS_EXPIRED
[0m[91mnpm ERR![0m[91m request to https://registry.npm.taobao.org/cnpm failed, reason: certificate has expired
[0m[91m
[0m[91mnpm[0m[91m ERR! A complete log of this run can be found in:
[0m[91mnpm ERR![0m[91m /root/.npm/_logs/2024-01-22T03_08_56_192Z-debug.log
[0mThe command ‘/bin/sh -c npm install -g cnpm@7.1.0 –registry=https://registry.npm.taobao.org’ returned a non-zero code: 1

解决方法

如果没有使用的Docker就使用以下代码


npm config set strict-ssl false

使用Docker遇到此问题就Dockerfile中使用以下命令


RUN npm config set strict-ssl false && npm install -g cnpm@7.1.0 --registry=https://registry.npm.taobao.org



转载请注明:清风亦平凡 » Docker使用NPM安装CNPM失败

]]>
https://www.mlplus.net/2024/01/22/docker-npm-install-cnpm-failed/feed/ 0
开源在线文档预览解决方案kkFileView https://www.mlplus.net/2024/01/17/online-document-kkfileview/ https://www.mlplus.net/2024/01/17/online-document-kkfileview/#respond Wed, 17 Jan 2024 03:58:52 +0000 https://www.mlplus.net/?p=4481 背景

因需要进行文档在线预览,所以才有以下内容。因付费方案不在选择范围,所以只能寻求免费开源的解决方案。经过查找发现除了LibreOffice OnlyOfficekkFileView以外并没有发现其他可用的方案,也许是自己原因没有找到其他开源解决方案吧!由于没有在线编辑的需求,经过对比kkFileView部署以及使用极其简单,所以就选择了kkFileView

kkFileView

kkFileView为文件文档在线预览解决方案,该项目使用流行的spring boot搭建,易上手和部署,基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等

开源在线文档预览解决方案kkFileView-第0张图片

项目特性

开源在线文档预览解决方案kkFileView-第1张图片
  • 使用spring boot开发,预览服务搭建部署非常简便
  • rest接口提供服务,跨平台特性(java,php,python,go,php,.net….)都支持,应用接入简单方便
  • 支持普通http/https文件下载url、http/https文件下载流url、ftp下载url等多种预览源
  • 提供zip,tar.gz发行包,提供一键启动脚本和丰富的配置项,方便部署使用
  • 提供Docker镜像发行包,方便在容器环境部署
  • 抽象预览服务接口,方便二次开发,非常方便添加其他类型文件预览支持

目前支持的文件类型如下:

  • 支持 doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx,xlam, xla 等 Office 办公文档
  • 支持 wps, dps, et, ett, wpt 等国产 WPS Office 办公文档
  • 支持 odt, ods, ots, odp, otp, six, ott, fodt, fods 等OpenOffice、LibreOffice 办公文档
  • 支持 vsd, vsdx 等 Visio 流程图文件
  • 支持 wmf, emf 等 Windows 系统图像文件
  • 支持 psd 等 Photoshop 软件模型文件
  • 支持 pdf ,ofd, rtf 等文档
  • 支持 xmind 软件模型文件
  • 支持 bpmn 工作流文件
  • 支持 eml 邮件文件
  • 支持 epub 图书文档
  • 支持 obj, 3ds, stl, ply, gltf, glb, off, 3dm, fbx, dae, wrl, 3mf, ifc, brep, step, iges, fcstd, bim 等 3D 模型文件
  • 支持 dwg, dxf 等 CAD 模型文件
  • 支持 txt, xml(渲染), md(渲染), java, php, py, js, css 等所有纯文本
  • 支持 zip, rar, jar, tar, gzip, 7z 等压缩包
  • 支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp 等图片预览(翻转,缩放,镜像)
  • 支持 tif, tiff 图信息模型文件
  • 支持 tga 图像格式文件
  • 支持 svg 矢量图像格式文件
  • 支持 mp3,wav,mp4,flv 等音视频格式文件
  • 支持 avi,mov,rm,webm,ts,rm,mkv,mpeg,ogg,mpg,rmvb,wmv,3gp,ts,swf 等视频格式转码预览

项目地址

GiteeGithub

部署

官方提供多种部署方式,其中包括使用Docker。因目前项目也是Docker部署,所以就使用此部署方式。

拉取镜像


# 网络环境方便访问docker中央仓库
docker pull keking/kkfileview:4.1.0

# 网络环境不方便访问docker中央仓库
wget https://kkview.cn/resource/kkFileView-4.1.0-docker.tar
docker load -i kkFileView-4.1.0-docker.tar

运行


docker run -it -p 8012:8012 keking/kkfileview:4.1.0

浏览器访问容器8012端口 http://127.0.0.1:8012 即可看到项目演示用首页

使用docker-compose


version: '3.4'

services:

  smart-document-service:
    container_name: smart-document-service
    image: keking/kkfileview:4.1.0
    restart: always
    ports:
      - "8012:8012"
    networks: 
      - smart-network-v1
    environment:
     - TZ=Asia/Shanghai
     - KK_FILE_DIR=/data/file  # 文件路径地址
      #KK_BASE_URL: "https://aa.bb.com/preview/"  # 配置nginx方向代理转发需要用到,详见官方文档
     - KK_CONTEXT_PATH=/previewDocuentFile/  # 配置nginx方向代理转发需要用到,详见官方文档
     - KK_OFFICE_PREVIEW_SWITCH_DISABLED=true  # 是否关闭预览切换,默认开启,如果需要关闭,设置为true
    volumes:
      - /data/smart/document_service/file:/data/file

networks:
  smart-network-v1:
    driver: bridge

项目接入

Nginx转发

由于以上内容更改了KK_CONTEXT_PATH,所以我们的转发内容是更改后的内容previewDocuentFile。其实不用Nginx转发也是可以的,由于项目的对外入口就一个,就有了这个Nginx转发的需求。


   location /previewDocuentFile {
          proxy_pass http://smart-document-service:8012;
   }

当您的项目内需要预览文件时,只需要调用浏览器打开本项目的预览接口,并传入须要预览文件的url,示例如下:


<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/js-base64@3.6.0/base64.min.js"></script>

var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(Base64.encode(previewUrl)));

以上方式打开新页面进行预览,如果不开新页面使用iframe也是一样的。

在线文档预览效果
更多信息参考官方网站https://kkview.cn



转载请注明:清风亦平凡 » 开源在线文档预览解决方案kkFileView

]]>
https://www.mlplus.net/2024/01/17/online-document-kkfileview/feed/ 0
JS录音获取麦克风权限被拒绝访问 https://www.mlplus.net/2023/11/10/js%e5%bd%95%e9%9f%b3%e8%8e%b7%e5%8f%96%e9%ba%a6%e5%85%8b%e9%a3%8e%e6%9d%83%e9%99%90%e8%a2%ab%e6%8b%92%e7%bb%9d%e8%ae%bf%e9%97%ae/ https://www.mlplus.net/2023/11/10/js%e5%bd%95%e9%9f%b3%e8%8e%b7%e5%8f%96%e9%ba%a6%e5%85%8b%e9%a3%8e%e6%9d%83%e9%99%90%e8%a2%ab%e6%8b%92%e7%bb%9d%e8%ae%bf%e9%97%ae/#respond Thu, 09 Nov 2023 16:04:00 +0000 https://www.mlplus.net/?p=4447 背景

因业务需求原因,需要在网页上进行录音,使用Javascript来进行操作,完成录音并保存上传到指定服务器。实现相关代码后进行测试,发现访问音频设备最后被拒绝。

原因

网页上反馈的异常信息如下图所示

网页访问音频设备被拒绝

经过再次的查看,确认未经处理的异常信息如下:

DOMException: Permission denied by system
控制台显示的异常信息

查下浏览器的设置,浏览器是否禁用麦克风,经过确认浏览器并没有发现什么问题。

JS录音获取麦克风权限被拒绝访问-第2张图片
JS录音获取麦克风权限被拒绝访问-第3张图片

浏览器各项检查都非常的正常,尝试录音错误依然再次出现,按道理来讲,应该是没有什么问题的。就目前情况而言,的确可能存在不讲道理的情况呀。随后更换一台电脑进行了一下测试,经过测试发现没有任何问题,正常录音。这就诡异了!也许是音频硬件原因?将这音频设备在其他电脑上进行再次确认,测试结果还是没有问题。这就让人郁闷了.

哎!万万没有想到,麦克风的访问权限在系统设置中根本就没有开。真是服了自己,这个问题竟然没有第一时间确认,真有一巴掌拍在脸上的感觉!

系统声音设置界面
系统麦克风访问权限设置页面

解决

知道了引起异常的具体原因后,解决起来就无比的简单。针对以上问题来说,更改设置就好了。

更改麦克风访问权限
麦克风权限允许后的图例

总结

一些时候往往会忽视一些简单的问题,就默认了这些基础内容不会出现任何问题。当一些基础内容出现问题,就被忽略掉了!还是要避免一些惯性思维,防止不必要的低级问题再次出现。此事记录,更好的警示自己!



转载请注明:清风亦平凡 » JS录音获取麦克风权限被拒绝访问

]]>
https://www.mlplus.net/2023/11/10/js%e5%bd%95%e9%9f%b3%e8%8e%b7%e5%8f%96%e9%ba%a6%e5%85%8b%e9%a3%8e%e6%9d%83%e9%99%90%e8%a2%ab%e6%8b%92%e7%bb%9d%e8%ae%bf%e9%97%ae/feed/ 0
在Docker容器下使用.net 5统计图中文乱码的问题 https://www.mlplus.net/2023/09/24/docker-draw-image-garbled-code/ https://www.mlplus.net/2023/09/24/docker-draw-image-garbled-code/#respond Sun, 24 Sep 2023 15:09:15 +0000 https://www.mlplus.net/?p=4356 背景

客户新提需求,需要增加一张报表,报表需要增加统计图表。因为客户需要将数据以及统计图表一并导出到word中,所以没有使用前端统计相关的类库,比如:echartshighcharts等库。虽然可以使用前端相关类库导出word,但是并不能满足现有需求。后台服务使用的是.net 5,统计图完成后发现中文出现乱码。如下图:

直方图乱码

问题

出现以上乱码问题,其一可能是统计类库不支持中文导致的,也有可能是docker镜像中缺少相应的中文字体。因为在本地开发环境测试并没有问题,更新到测试环境后才发现以上乱码情况,所以应该是镜像中缺少相关中文字体而导致的。

解决

既然是缺少中文字体,那就安装字体就行了。在Windows系统中复制需要的字体到新建名称为chinese文件夹下,将此文件夹放到与dockerfile同一目录下即可。dockerfile文件中新增内容如下:


RUN apt-get -y  install fontconfig
COPY ./chinese /usr/share/fonts
RUN fc-cache -vf
Windows系统字体所在位置:控制面板\外观和个性化\字体
Linux字体目录是:/usr/share/fonts,所以dockerfile中指定将字体复制到此目录。

dockerfile更改后提交构建镜像并重新运行容器,再次查看效果后发现恢复正常,如下图:

直方图修正乱码

总结

docker中使用的镜像,有时候选择的是最小依赖的基础镜像,镜像比较小。但是一些情况是不满足需求的,在不更改基础镜像的情况下,还是要通过docker相关指令安装所需依赖,例如:一些软件、字体或者其他内容。



转载请注明:清风亦平凡 » 在Docker容器下使用.net 5统计图中文乱码的问题

]]>
https://www.mlplus.net/2023/09/24/docker-draw-image-garbled-code/feed/ 0
迁移网站更改域名后一个可以使用的301跳转方案 https://www.mlplus.net/2023/08/31/website-migration-301/ https://www.mlplus.net/2023/08/31/website-migration-301/#respond Wed, 30 Aug 2023 16:36:00 +0000 https://www.mlplus.net/?p=4329 背景

最初,由于购买cc域名不支持备案,所以就使用了阿里云香港云空间。自从2016年网站应用由.net应用BlogEngine.NET改为WordPress至今都使用的阿里云香港云空间。稳定倒是挺稳定的,就是一直感觉访问速度比较慢,本以为是阿里云香港云空间本是这样,就没有研究其慢的真实原因,当然这已经是后话了。

今年云空间快要到期的时候,突然想将网站迁移到国内,为了不在备案期间停止域名访问,就更换了域名。当域名更换完成,空间迁移完毕,貌似一切准备就绪,发现原来空间即将到期,留给301跳转的时间非常不足。本想使用阿里云DNS解析进行301跳转,这个也是最优的解决方案,无奈的是域名必须进行备案且是两个域名都必须备案。如果原来的域名也参与备案的话,网站就需要停止访问了,为了避免这种情况,只能放弃使用此方法。

发现

续费原来空间或者购买腾讯云和百度云等在价格上是不划算的。为了保证以最低的代价进行301跳转,就开始找一些低价无需备案的空间来作为中转。经过两天的查找发现一款非常符合预期的服务商,支持多国语言 ,支持不同的支付方式,包括阿里的支付宝,从2006年成立至今已经17年的justhost。发现最经济的一种方式以存储空间使用多少来进行收费的,1兆每天0.001元,也就是说1兆每天1厘,如下图所示:

服务空间套餐选择

使用

首先,注册该网站并充值1.17美元,当时的最低充值金额。随后,订购所选择的服务计划(套餐),等待10分钟左右邮件通知相关信息。以下便是订购的服务,按天来进行续订,按天进行扣费。

计划订购列表

新建一个php文件index.php上传到根目录,其内容如下


<?php
header("HTTP/1.1 301 Moved Permanently");
header("Location: 需要301跳转到域名");
exit();
?>

完整示例如下:


<?php
header("HTTP/1.1 301 Moved Permanently");
header("Location: https://www.mlplus.net");
exit();
?>

完成以上内容后发现一个问题,只有访问首页才会跳转,访问其他页面是没有作用的。为了达到所有请求进行301跳转,需要进行额外的配置重定向。为了完成以上需求,需要在根目录下使用.htaccess配置文件进行,这个文件如果存在就编辑修改,不存在则新建上传。新增具体内容如下:


RewriteEngine On
RewriteRule ^(.*)$ 这里是具体域名/$1 [R=301,L]

完整示例如下:


RewriteEngine On
RewriteRule ^(.*)$ https://www.mlplus.net/$1 [R=301,L]
以上配置属于Apache服务器的配置

经过一段时间的观察,此服务商也比较稳定,每天0.001元,从2023年07月11日至2023年08月31日止,扣费0.06元。

最初扣费详情图
最新扣费详情

日志

使用的此服务是按照占用空间进行计算收费,网站访问产生的日志文件以及访问统计图表也是被计算在其中的。最初发现占用空间升高就手动清理一次,感觉手动清理太过繁琐,就是用程序自动清理一下。基于.net core使用FluentFTP库进行FTP登录清理操作。

空间暂用统计

使用最简单,最暴力的方式完成,不考虑什么设计与性能,以最快的速度实现目的。

实现代码:


using FluentFTP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JustHost.Ftp.ClearLog
{
    internal class CrontabFtpClear
    {
        System.Timers.Timer timer;
        List<string> paths = new List<string>() { "/domains/skyfinder.cc/logs/", "/domains/skyfinder.cc/stats/" };
        public CrontabFtpClear()
        {
            timer = new System.Timers.Timer();
            timer.Interval = TimeSpan.FromHours(5).TotalMilliseconds;
            //timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
            timer.Elapsed += Timer_Elapsed;
            timer.Enabled = true;
            timer.AutoReset = true;
        }

        private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            timer.Enabled = false;
            ClearProcess();
        }

        private void ClearProcess(){
            try
            {
                Console.WriteLine($"操作:执行指定清理命令 时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
                // 设置连接信息
                var ftpClient = new FtpClient("这里是服务器IP地址", "这里是用户名", "这里是密码");

                // 连接到FTP服务器
                ftpClient.Connect();
                foreach (var item in paths)
                {
                    if (!ftpClient.DirectoryExists(item))
                    {
                        continue;
                    }
                    ftpClient.DeleteDirectory(item, FtpListOption.ForceList);
                }

                ftpClient.Disconnect();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                timer.Enabled = true;
            }
        }


    }
}

调用运行:


using FluentFTP;

namespace JustHost.Ftp.ClearLog
{
    internal class Program
    {
        static void Main(string[] args)
        {
            CrontabFtpClear ftpClear = new CrontabFtpClear();
            Console.WriteLine($"FTP目录清理服务启动 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
            while (true)
            {
                Thread.Sleep(1000);
            }
           
        }
    }
}

示例下载

justhost空间日志自动清理示例



转载请注明:清风亦平凡 » 迁移网站更改域名后一个可以使用的301跳转方案

]]>
https://www.mlplus.net/2023/08/31/website-migration-301/feed/ 0
Nginx禁止指定UA、IP或者IP段访问 https://www.mlplus.net/2023/07/21/nginx-access-denied-ua-ip/ https://www.mlplus.net/2023/07/21/nginx-access-denied-ua-ip/#respond Fri, 21 Jul 2023 04:50:21 +0000 https://www.mlplus.net/?p=4293 背景

在管理后台修改一些内容的时候,想到很久之前安装的重定向插件Redirections,随便就看一下。这不看不知道,看到了就差点大声尖叫。没有想到这么多对站点无意义的请求命中之前设置的重定向规则,一眼就可以发现请求UA不正常。例如:Apache-HttpClientpython-requests

这种是一些非法采集的应用,觉得还是要对这类UA进行一些限制,例如:禁止指定UA访问、拒绝IP或者IP段的访问等。

非正常UA访问记录图

现在服务已经由原来的Apache迁移到了Nginx服务上运行,所以现在的处置方案针对Nginx的。

Nginx禁止特定UA访问

Nginx中有一个内置变量$http_user_agent,表示HTTP请求头中的User-Agent字段。使用正则表达式匹配User-Agent字段中是否包含特定的字符串,符合规则的则禁止访问。具体实现如下:


location / {
    if ($http_user_agent ~* (python-requests|Apache-HttpClient)) {
        return 403;
    }
}

以上配置可以用于防止用户使用某些自动化工具或脚本对站点进行滥用。然而,需要注意的是,这种检测方式并不完全可靠。因为用户可以通过修改User-Agent来绕过此防护措施。所以这种方式只可以被视作拦截大多数爬虫程序的一种简单手段。

Nginx禁止特定的IP以及IP段

如果说Nginx禁止特定的UA来访问是一个泛化的处理方式的话,那么禁止IP或者IP段这是一个具备针对点的解决方法。针对某个IP或者某个IP段,这种方式比较滞后,发现大量无用请求后才可以采取的措施。当然,面对这种恶意的请求,这些也只是最快速、最简单、最低廉的解决方案。

Nginx中,可以使用deny指令来禁止IP地址或IP地址段的访问。这个功能可以用于防止恶意访问和DDoS攻击,以及限制对特定内容的访问。

禁止单个IP地址

要禁止单个IP地址的访问,可以在 Nginx 配置文件中添加以下行:


location / {
    deny 192.168.1.100;
    # 其他配置
}

在此例中,使用 deny 指令来阻止IP地址为 192.168.1.100 的客户端访问该位置的内容。如果有客户端尝试访问,他们将收到403 Forbidden错误。

禁止IP地址段

如果要禁止IP地址段的访问,可以在 Nginx 配置文件中使用 CIDR 表示法。CIDR 表示法允许您指定一个 IP 地址的范围。例如,要阻止 192.168.0.0/24 子网内的所有客户端访问,可以使用以下配置:


location / {
    deny 192.168.0.0/24;
    # 其他配置
}

这将阻止子网内的所有IP地址从该位置访问,包括 192.168.0.1192.168.0.2192.168.0.3 等等。

恢复被禁止的客户端

如果想恢复先前被禁止的 IP 地址或 IP 地址段的客户端,请简单地将其从相关的配置中删除或注释掉。


location / {
    # deny 192.168.1.100;
    # deny 192.168.0.0/24;
    # 其他配置
}

完整实例

下面是一个完整的Nginx配置,它禁止IP地址为192.168.1.100192.168.0.0/24的客户端访问站点:


http {
    server {
        listen 80;
        server_name example.com;
        root /var/www/html;

        location / {
            # 禁止IP地址为192.168.1.100的客户端访问
            deny 192.168.1.100;

            # 禁止IP地址为192.168.0.0/24的客户端访问
            deny 192.168.0.0/24;

            # 允许其他客户端访问
            allow all;

            # 其他配置
        }
    }
}



转载请注明:清风亦平凡 » Nginx禁止指定UA、IP或者IP段访问

]]>
https://www.mlplus.net/2023/07/21/nginx-access-denied-ua-ip/feed/ 0
HTML5音频资源加载失败的处理方法 https://www.mlplus.net/2023/07/16/html5-audio-load-fail/ https://www.mlplus.net/2023/07/16/html5-audio-load-fail/#respond Sun, 16 Jul 2023 14:00:00 +0000 https://www.mlplus.net/?p=4202 背景

页面加载很多音频标签,音频资源一些加载不成功,音频标签不可用,并且排除以下几种情况:

  • 检查音频的链接是否有效
  • 检查音频格式是否支持
  • 检查网络连接是否通畅

如果确认以上内容没有任何问题,则是页面加载过多的音频资源而导致部分音频资源加载失败。可以使用JavaScript进行资源加载重试,当然重试的策略需要进行限制,避免无限制尝试。

实现方式

以下是使用JavaScript代码实现自动重新加载资源的一个简单的示例,当音频标签加载失败时,它会自动尝试重新加载音频:


const audio = document.querySelector('audio');

audio.addEventListener('error', () => {
  audio.load();
});



在上面示例中,首先选择了音频标签,并添加了一个错误事件监听器。当发生错误时,错误事件会触发,并调用load()方法来重新加载音频并尝试播放。

如果希望重试的次数有限制,可以使用一个计数器来限制次数,例如:


const audio = document.querySelector('audio');
let retryCount = 0;
const maxRetries = 3;
audio.addEventListener('error', () => {
  if (retryCount < maxRetries) {
    audio.load();
    retryCount++;
  } else {
    console.log('音频加载失败');
  }
});

在以上的示例中,添加了一个retryCount变量来跟踪尝试次数,并添加了一个maxRetries变量来限制最大尝试次数。

如果重试次数小于最大尝试次数,将重新加载音频,并增加重试计数器。如果达到最大尝试次数,将打印一条错误消息。

这样,就可以使用JavaScript代码来实现自动重新加载音频标签了。这只是一个简单的示例,而实际情况可能需要考虑更多的情况,此示例仅供一个参考!



转载请注明:清风亦平凡 » HTML5音频资源加载失败的处理方法

]]>
https://www.mlplus.net/2023/07/16/html5-audio-load-fail/feed/ 0
WordPress由Apache迁移到Nginx出现404问题的解决方法 https://www.mlplus.net/2023/06/15/wordpress-nginx-rewrite/ https://www.mlplus.net/2023/06/15/wordpress-nginx-rewrite/#respond Thu, 15 Jun 2023 11:01:55 +0000 https://www.mlplus.net/?p=4236 背景

虚拟主机即将到期且位置在香港,为了网站更好的访问速度索性就迁回大陆。在好多年前购买的时候只有Apache可选,这次迁回有Nginx可以选择,官方有说这个性能更加的强劲,所以这次就选了Nginx服务。数据以及文件很容易就完成了迁移,本想万事大吉,随便点击了几下测试全是404,其中包括文章、分类、标签、搜索、分页、站点地图、日期等全部404。WordPress后台固定链接设置的是日期和名称,应该就是URL重写的问题了。在使用Apache的虚拟主机的时候是默认的有相关配置的,但是新购的Nginx虚拟主机就没有这种配置了。所以,只有自己来进行URL的重写配置。

固定链接
404演示

Nginx解决方法

Nginx重写URL配置内容如下:


location / {
    if (!-e $request_filename) {
        rewrite ^/([0-9]+?)/([0-9]+?)/([0-9]+?)/([^/]+)$ /index.php?year=$1&monthnum=$2&day=$3&name=$4 last;
        rewrite ^/([0-9]+?)/([0-9]+?)/([0-9]+?)/?$ /index.php?year=$1&monthnum=$2&day=$3 last;
        rewrite ^/([0-9]+)/([0-9]+)/?$ /index.php?year=$1&monthnum=$2 last;
        rewrite ^/category/(.*)/page/([0-9]+?)/?$ /index.php?category_name=$1&paged=$2 last;
        rewrite ^/category/(.*)$ /index.php?category_name=$1 last;
        rewrite ^/tag/(.*)/page/([0-9]+?)/?$ /index.php?tag=$1&paged=$2  last;
        rewrite ^/tag/(.*)/?$ /index.php?tag=$1 last;
        rewrite ^/page/([0-9]+?)/?$ /index.php?paged=$1 last;
        rewrite ^/([0-9]+?)/([0-9]+?)/page/([0-9]+?)/?$ /index.php?year=$1&monthnum=$2&paged=$3 last;
        rewrite ^/search/(.+?)/page/([0-9]+?)/?$ /index.php?s=$1&paged=$2 last;
        rewrite ^/search/(.+?)/?$ /index.php?s=$1 last;
        rewrite ^/(.+?)$ /index.php?pagename=$1 last;
    }
    try_files $uri $uri/ /index.php?$args;
}

location ~*  /feed/?$ {
    try_files $uri /index.php?$args;
}

location = /wp-sitemap.xml {
    try_files $uri $uri/ /index.php?$args;
}

location ~ ^/wp-sitemap(-index)?.xsl {
    try_files $uri $uri/ /index.php?$args;
}

location ~ ^/wp-sitemap-(.*?)-(.*?).xml$ {
    try_files $uri $uri/ /index.php?$args;
}

如果觉得以上配置太过臃肿,可以使用以下配置


location / {
     try_files $uri $uri/ /index.php?$args;
}

Apache中解决方法

.htaccess文件中加入以下代码:


<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>



转载请注明:清风亦平凡 » WordPress由Apache迁移到Nginx出现404问题的解决方法

]]>
https://www.mlplus.net/2023/06/15/wordpress-nginx-rewrite/feed/ 0
Syncfusion.DocIO.Net.Core操作Word的第三方组件无限制版 https://www.mlplus.net/2023/05/16/syncfusion-docio-net-core/ https://www.mlplus.net/2023/05/16/syncfusion-docio-net-core/#respond Tue, 16 May 2023 04:54:47 +0000 https://www.mlplus.net/?p=4208 背景

一些朋友推荐处理Word可以使用Syncfusion.DocIO,说是API比较好用而且还支持doc格式的文件,因此就了解一下。经过测试确认是比一些开源的处理库好用一点,支持相对来说比较完善,就因为这些所以才会有这篇介绍的内容。

Syncfusion.DocIO.Net.Core

Syncfusion.DocIO.Net.CoreSyncfusion的一组.NET组件,用于创建、编辑和转换Microsoft Word文档。它支持多种文件格式,包括.docx.rtf.txt等。它提供了一组易于使用的 API,可以帮助开发人员轻松地创建、读取和修改 Word 文档。

主要功能

  • 创建Word文档并添加内容、表格、图片等元素。
  • 编辑Word文档中的文本、表格、图片等内容。
  • Word文档转换为其他格式,如PDFHTML等。
  • 支持批量操作,可以同时打开多个Word文档并进行批量编辑和转换。
  • 支持自定义样式和主题,可以根据需要修改文档的外观和布局。
  • 包含额外的安全性特性,可以确保在处理敏感数据时的安全性

使用示例

以下是一个简单的示例,演示如何使用Syncfusion.DocIO.Net.Core创建一个Word文档并添加内容


using Syncfusion.DocIO;
using Syncfusion.DocIO.Xls;
using Syncfusion.DocIO.Xls.Conversion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CreateWordDocument
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建一个新的Word文档
            Document doc = new Document();
            doc.AddSection("Introduction");
            
            //添加一个标题1级别的段落,并在其中添加一些文本
            Section section = doc.Sections[0];
            paragraph = section.Paragraphs.Add("Hello World!", DocFormat.Title1);
            paragraph.Range.InsertAfter("This is a sample document created using Syncfusion.DocIO.NET Core.");
            
            //保存并关闭文档
            doc.Save(@"C:\Documents\Sample.docx");
            doc.Close();
        }
    }
}



结论

Syncfusion.DocIO.Net.Core相对来说API比较好用,操作的格式基本符合预期。缺点就是此组件是商业版本,会有水印以及其他版权提示内容,如下图所示。如果使用比较低的版本,只有页眉页脚有试用版说明,比如:20.1.0.61及一下版本。

Word使用Syncfusion.DocIO导出包含水印与其他说明。

鉴于水印不便于学习,为了更好的营造学习气氛,所以对水印以及其他提示进行移除处理,水印移除后的表现形式如下:

Syncfusion.DocIO.Net.Core操作Word的第三方组件无限制版-第1张图片

如果不是非Syncfusion.DocIO.Net.Core不可,可以选用其他免费开源的Word处理组件。如果一定要使用请购买正版,以便得到更好的效果以及技术支持。虽然此组件相对来说比较完善好用,但最终我还是选用其他的开源组件,例如:

下载

Syncfusion.DocIO.Net.Core21.2.4 Syncfusion.DocIO.Net.Core22.1.37
版本22.1.37中包含Doc、XlsIO、Pdf
以上下载内容仅供学习使用,请勿用于其他用途。



转载请注明:清风亦平凡 » Syncfusion.DocIO.Net.Core操作Word的第三方组件无限制版

]]>
https://www.mlplus.net/2023/05/16/syncfusion-docio-net-core/feed/ 0
安全架构设计基本原则 https://www.mlplus.net/2023/03/29/%e5%ae%89%e5%85%a8%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e5%9f%ba%e6%9c%ac%e5%8e%9f%e5%88%99/ https://www.mlplus.net/2023/03/29/%e5%ae%89%e5%85%a8%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e5%9f%ba%e6%9c%ac%e5%8e%9f%e5%88%99/#respond Wed, 29 Mar 2023 04:51:35 +0000 https://www.mlplus.net/?p=4176 安全原则

在应用系统软件开发设计的过程中,对应用系统的总体设计应当满足如下安全原则

原则说明
最小权限原则
Least Privilege
应用软件的每个模块如进程、用户只能访问当下所必需的信息或者资源。赋予每一个合法动作最小的权限,以保护数据以及功能避免受到错误或者恶意行为的破坏。
权限分离原则
Separation of Duties
对业务的操作、管理和审计权限应该由软件中的不同角色的用户分别承担;普通用户和管理员用户信息应该存放在不同的数据表中。
深度防御原则
Defense in Depth
在应用程序对业务数据进行处理的每个阶段都要考虑安全性问题,不能仅在某个阶段做安全防御,这样单点防御一旦被突破将造成安全风险。
容错保护原则
Fail Secure
当程序出现故障时或系统异常当系统失败时,可以进入到一个失败保护的状态。如果用户请求失败,系统仍可保障安全
单点异常终止原则
Single Point of Failure
当用户提交数据超出预期时,应立即终止程序的执行,不要试图加以修正并继续执行下去。
外来代码安全原则
Least Third Party Components
严格控制第三方函数与插件的使用,对外来代码必须进行详细的安全测试。
代码重用原则
Leveraging Existing Components
尽可能的重用软件已有的模块,这样可以降低引入新的漏洞和攻击界面的可能性。
数据保护原则
Data Protection
对用户数据的保护功能应涵盖用户数据存储的完整性、用户数据传输保密性、数据传输的访问控制、剩余信息的保护、数据反转操作等内容;应对系统中关键数据(如用户密码等)的存储和网络传输时应采用加密保护,实用加密加密算法应该符合国际标准、国家标准和业界标准。
可审计原则
Auditing
在应用系统中设计审计日志记录的功能,并对应用系统产生的日志增加完备的审计功能。
开放设计原则
Open Design
开放设计与“不开放即安全”的原则相对而言,认为设计本身不应具有神秘感。这一原则的具体表现可以参见应用于加密设计的Kerchoff定律,“系统不应单纯依赖私密性,若落入敌人手中则毫无优势可言”;开放设计以提高系统兼容性和可扩展性。
抗抵赖原则
Anti Repudiation
对于涉及支付交易等重要的业务场景,系统设计应有效地防止通信双方抵赖,如采用电子证书签名等方式。
规范性
Standardization
系统设计所采用的安全技术和安全产品应符合国际标准、国家标准和业界标准,为系统的扩展升级、与其他系统的互联提供良好的基础。
可扩展性
Scalability
以当前业务安全需求为基础,充分考虑发展的需要,安全功能模块子系统以插件或接口方式以方便未来的扩展。
实用性
Practicable
安全功能设计需要尽可能的考虑投入产出比,同时尽量控制对用户体验的影响。
符合性
Regulatory Compliance
安全功能的设计尽可能的要符合国家规范、行业规范以及业界的通用标准,如等级保护等规范。



转载请注明:清风亦平凡 » 安全架构设计基本原则

]]>
https://www.mlplus.net/2023/03/29/%e5%ae%89%e5%85%a8%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e5%9f%ba%e6%9c%ac%e5%8e%9f%e5%88%99/feed/ 0
使用Fiddler进行移动端(手机)抓包 https://www.mlplus.net/2023/03/14/fiddler-mobile-setting/ https://www.mlplus.net/2023/03/14/fiddler-mobile-setting/#respond Tue, 14 Mar 2023 09:15:33 +0000 https://www.mlplus.net/?p=4156 背景

客户一个定制企业微信相关内容,其中自动登录成功某些功能不正常,手动登录的又未复现问题,程序都是相同的且问题确实存在。准备使用抓包看一下企业微信自动登录与非自动登录的行为区别,抓包工具选用了大名鼎鼎的Fiddler

Fiddler

Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据(指cookie,html,js,css等文件)。 Fiddler 要比其他的网络调试器要更加简单,因为它不仅仅暴露http通讯还提供了一个用户友好的格式。

Fiddler 是用C#写出来的,它包含一个简单却功能强大的基于JScript.NET 事件脚本子系统,它的灵活性非常棒,可以支持众多的http调试任务,并且能够使用.net框架语言进行扩展。

操作设置

设置允许远程电脑链接

依次选择Tools=>Options=>Connections=>勾选Allow remote computers to connect=>再点击OK

Fiddler tools
Fiddler connections
Fiddler的监听端口是8888,在远端设备上需要Fiddler所在设备的IP以及端口

查看本机IP

使用快捷键Windows+R调出运行小窗口,输入cmd回车即可。

运行
命令行显示本机IP

设置网络代理

打开移动设备Wlan,修改当前连接的网络。

移动设备WLAN

在以下页面进行代理设置,选择手动模式

移动设备WLAN代理设置页面

输入Fiddler所在设备的IP与端口后保存。

移动设备WLAN代理设置

手机打开浏览器进行测试,随便打开页面进行测试即可。

手机浏览移动页面

Fiddler可以看到在手机浏览的网页表示设置成功。

Fiddler查看抓取



转载请注明:清风亦平凡 » 使用Fiddler进行移动端(手机)抓包

]]>
https://www.mlplus.net/2023/03/14/fiddler-mobile-setting/feed/ 0
安全风险状况说明 https://www.mlplus.net/2023/02/24/%e5%ae%89%e5%85%a8%e9%a3%8e%e9%99%a9%e7%8a%b6%e5%86%b5%e8%af%b4%e6%98%8e/ https://www.mlplus.net/2023/02/24/%e5%ae%89%e5%85%a8%e9%a3%8e%e9%99%a9%e7%8a%b6%e5%86%b5%e8%af%b4%e6%98%8e/#respond Fri, 24 Feb 2023 10:02:25 +0000 https://www.mlplus.net/?p=4138 安全风险状况等级说明
良好状态   信息系统处于良好运行状态,没有发现或只存在零星的低风险安全问题,此时只要保持现有安全策略就满足了本系统的安全等级要求。
预警状态信息系统中存在一些漏洞或安全隐患,此时需根据评估中发现的网络、主机、应用和管理等方面的问题对进行有针对性的加固或改进。
严重状态信息系统中发现存在严重漏洞或可能严重威胁到系统正常运行的安全问题,此时需要立刻采取措施,例如安装补丁或重新部署安全系统进行防护等等。
紧急状态信息系统面临严峻的网络安全态势,对组织的重大经济利益或政治利益可能造成严重损害。此时需要与其他安全部门通力协作采取紧急防御措施。

漏洞等级状况说明

低危漏洞 对系统造成较小的影响,攻击成本高,攻击场景较为苛刻,不会直接影响到系统的正常运行,攻击者可能无法通过该漏洞获得权限。
中危漏洞对系统造成一般的影响,攻击成本一般,在特定场景下将可影响系统的正常运行,攻击者需要配合其他安全漏洞方可间接获得权限。
高危漏洞对系统造成严重的影响,攻击成本低,一般情况下可直接利用而无需特定场景和要求,攻击者可直接利用该漏洞获得权限。



转载请注明:清风亦平凡 » 安全风险状况说明

]]>
https://www.mlplus.net/2023/02/24/%e5%ae%89%e5%85%a8%e9%a3%8e%e9%99%a9%e7%8a%b6%e5%86%b5%e8%af%b4%e6%98%8e/feed/ 0
Docker构建VUE项目NPM构建134异常 https://www.mlplus.net/2023/02/23/docker-build-npm134-error/ https://www.mlplus.net/2023/02/23/docker-build-npm134-error/#respond Thu, 23 Feb 2023 08:10:53 +0000 https://www.mlplus.net/?p=4131 背景

一个前端的VUE项目,需要打一个Docker的镜像离线包。通过命令行构建镜像,通过等待比较长的时间后,NPM构建抛出异常。

异常内容

以下是关于这次异常的详细信息:

docker构建VUE项目异常图片
=> ERROR [build-stage 10/10] RUN npm run build                                                                 1222.9s
------
 > [build-stage 10/10] RUN npm run build:
#18 7.649
#18 7.649 > vue-antd-admin@0.7.4 build /app
#18 7.649 > vue-cli-service build
#18 7.649
#18 24.59
#18 24.60 -  Building for production...
#18 1221.3
#18 1221.3 <--- Last few GCs --->
#18 1221.3
#18 1221.3 [24:0x40d39c0]  1206698 ms: Mark-sweep (reduce) 986.8 (995.9) -> 986.1 (997.4) MB, 6393.8 / 0.1 ms  (average mu = 0.076, current mu = 0.019) allocation failure scavenge might not succeed
#18 1221.3
#18 1221.3
#18 1221.3 <--- JS stacktrace --->
#18 1221.3
#18 1221.3 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
#18 1221.5  1: 0xa18da8 node::Abort() [/usr/local/bin/node]
#18 1221.5  2: 0x969880 node::FatalError(char const*, char const*) [/usr/local/bin/node]
#18 1221.5  3: 0xb74138 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
#18 1221.5  4: 0xb742c4 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
#18 1221.5  5: 0xd1c820 v8::internal::Heap::EnsureFromSpaceIsCommitted() [/usr/local/bin/node]
#18 1221.5  6: 0xd1d288  [/usr/local/bin/node]
#18 1221.5  7: 0xd2af4c v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node]
#18 1221.5  8: 0xd2e41c v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
#18 1221.5  9: 0xd00714 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/usr/local/bin/node]
#18 1221.5 10: 0x101ba54 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/usr/local/bin/node]
#18 1221.5 11: 0x137bb0c  [/usr/local/bin/node]
#18 1221.5 qemu: uncaught target signal 6 (Aborted) - core dumped
#18 1221.7 Aborted
#18 1221.8 npm ERR! code ELIFECYCLE
#18 1221.8 npm ERR! errno 134
#18 1221.8 npm ERR! vue-antd-admin@0.7.4 build: `vue-cli-service build`
#18 1221.8 npm ERR! Exit status 134
#18 1221.8 npm ERR!
#18 1221.8 npm ERR! Failed at the vue-antd-admin@0.7.4 build script.
#18 1221.8 npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
#18 1222.0
#18 1222.0 npm ERR! A complete log of this run can be found in:
#18 1222.0 npm ERR!     /root/.npm/_logs/2023-02-16T06_26_23_271Z-debug.log
------
executor failed running [/bin/sh -c npm run build]: exit code: 134


经过查看发现NPM异常退出代码134,通过错误代码了解到应该是内存不足导致的。优先确认自己使用的这台物理机器内存,并未发现问题。应该是Docker本身子系统内存问题,经过查看确认允许内存为2G,这内存允许范围的确可能出现内存不足的问题。将内存设置为4GB后保存,再次构建项目,经过一段时间的等待,Docker镜像构建成功。

Docker构建VUE项目NPM构建134异常-第1张图片

Docker构建VUE项目NPM构建134异常-第2张图片



转载请注明:清风亦平凡 » Docker构建VUE项目NPM构建134异常

]]>
https://www.mlplus.net/2023/02/23/docker-build-npm134-error/feed/ 0
Chrome浏览器中的XPath https://www.mlplus.net/2022/12/04/chrome%e6%b5%8f%e8%a7%88%e5%99%a8%e4%b8%ad%e7%9a%84xpath/ https://www.mlplus.net/2022/12/04/chrome%e6%b5%8f%e8%a7%88%e5%99%a8%e4%b8%ad%e7%9a%84xpath/#respond Sat, 03 Dec 2022 16:52:11 +0000 https://www.mlplus.net/?p=4030 背景

某一个应用自动在网页上获取一些文本内容,本来是通过document.querySelector来找指定节点。经过一段时间网页貌似升级了,一些节点的class属性的值会出现随机的变动,每次class属性的值都会不一样。最初的方式就失去了作用,根据节点内容的分析发现可以通过xpath来获取。曾经在IE浏览器上使用过XPath,并且API相当简单。在非IE浏览器上貌似没有这么好用。以下内容在Chrome浏览器进行尝试,经过测试可以完成自己的预期工作。

浏览器支持

Mozilla是根据DOM标准来实现对XPath的支持的。DOM Level 3附加标准DOM Level 3 XPath定义了用于在DOM中计算XPath表达式的接口。遗憾的是,这个标准要比微软直观的方式复杂得多。虽然有好多与XPath相关的对象,最重要的两个是:XPathEvaluatorXPathResultXPathEvaluator利用方法evaluate()计算XPath表达式。

evaluate()方法有五个参数:XPath表达式、上下文节点、命名空间解释程序和返回的结果的类型,同时在XPathResult中存放结果(通常为null)。命名空间解释程序,只有在XML代码用到了XML命名空间时才是必要的,所以通常留空,置为null。返回结果的类型,可以是以下十个常量值之一

参数解释
XPathResult.ANY_TYPE返回符合XPath表达式类型的数据
XPathResult.ANY_UNORDERED_NODE_TYPE返回匹配节点的节点集合,但顺序可能与文档中的节点的顺序不匹配
XPathResult.BOOLEAN_TYPE返回布尔值
XPathResult.FIRST_ORDERED_NODE_TYPE返回只包含一个节点的节点集合,且这个节点是在文档中第一个匹配的节点
XPathResult.NUMBER_TYPE返回数字值
XPathResult.ORDERED_NODE_ITERATOR_TYPE返回匹配节点的节点集合,顺序为节点在文档中出现的顺序。这是最常用到的结果类型
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE返回节点集合快照,在文档外捕获节点,这样将来对文档的任何修改都不会影响这个节点列表。节点集合中的节点与它们出现在文档中的顺序一样
XPathResult.STRING_TYPE返回字符串值
XPathResult.UNORDERED_NODE_ITERATOR_TYPE返回匹配节点的节点集合,不过顺序可能不会按照节点在文档中出现的顺序排列
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE返回节点集合快照,在文档外捕获节点,这样将来对文档的任何修改都不会影响这个节点列表。节点集合中的节点和文档中原来的顺序不一定一样。

JavaScript实现XPath选择节点


function xpathQuery(xpath){
  let resultXpath = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
  let result=[];
  if(resultXpath){
     let item;
     while(item=resultXpath.iterateNext())
     {
        result.push(item);
     }
  } 
  return result;
}

xpathQuery("//div[@class='container-body']/div[contains(@class,'order-item')]");
JavaScript 使用XPath



转载请注明:清风亦平凡 » Chrome浏览器中的XPath

]]>
https://www.mlplus.net/2022/12/04/chrome%e6%b5%8f%e8%a7%88%e5%99%a8%e4%b8%ad%e7%9a%84xpath/feed/ 0
Javascript判断当前页面是否处于激活状态 https://www.mlplus.net/2022/11/21/javascript%e5%88%a4%e6%96%ad%e5%bd%93%e5%89%8d%e9%a1%b5%e9%9d%a2%e6%98%af%e5%90%a6%e5%a4%84%e4%ba%8e%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81/ https://www.mlplus.net/2022/11/21/javascript%e5%88%a4%e6%96%ad%e5%bd%93%e5%89%8d%e9%a1%b5%e9%9d%a2%e6%98%af%e5%90%a6%e5%a4%84%e4%ba%8e%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81/#respond Mon, 21 Nov 2022 05:44:00 +0000 https://www.mlplus.net/?p=4020 背景

现有一个考试项目,当在浏览器进行考试时需要判断用户是否存在切屏,如果切屏就对当前考试进行自动强制交卷。浏览器中可通过window对象的onbluronfocus判断,或者documenthidden属性判断。

获取焦点(onfocus)和失去焦点(onblur)

关于是否失焦点,浏览器对象有onfocus onblur事件可以监听。但是触发这两个事件的前提是页面之前是获取焦点的,就是说要是激活的。也就是说页面刚刚渲染完,用户在没有页面上任何操作时,页面是不会正常监听这两个事件的;或者页面在打开状态下,但是触发了onblur之后并无页面操作的情况下也不会正常监听这两个事件。直到,用户操作页面触发focus,之后离开页面才会触发blur,再次点击到当前页面时才会触发focus,如此反复都会触发相应的事件。

onblur

  • chrome浏览器下,点击console面板也会触发blur事件,前提是之前是focus的状态。
  • 页面最小化;
  • 浏览器切换tab页面;
  • 页面中的任何弹窗;
  • focus状态下切换到其他应用

onfocus

  • 用户存在页面操作(包括页面中js脚本运行。如页面加载完无js运行,用户无操作,则不会触发 );
  • 事件触发前提下,页面最大化;
  • 事件触发前提下,页面从其他tab页切换回当前页面;

onblur与onfocus示例

  
//离开          
window.onblur = function () {
         console&&console.log("切屏了onblur");
 };
 //回来
 window.onfocus = function () {
        console&&console.log("切屏了onfocus");
  };

HTML5新增API

HTML5新增的API, visibilitychange, document.hidden, document.visibilityState来实现相关内容。

visibilitychange

  • 浏览器支持 visibilitychangeHTML5API所以支持持最新浏览器 Chrome, Firefox, IE10+
  • addEventListener添加事件,当页面发生改变就会触发

老版本浏览器如果失效需要添加前缀

  • mozvisibilitychange 火狐
  • msvisibilitychange IE
  • webkitvisibilitychange 谷歌,Safari

document.hidden

hiddendocument的属性,可以判断页面是否显示的是当前的页面。visibilitychange事件就是触发页面可见的事件。当然不同的浏览器内核记得要加前缀。表示页面处于非激活状态,反之处于激活状态。

  • 页面最小化
  • 页面在后台运行
  • 切换tab栏到其他页面

当其hiddenfalse的时候就是当前网页正在被用户浏览,其值为ture就是当前网页进入后台。

document.visibilityState

  • visible 是浏览器当前激活,窗口不是最小化状态
  • hidden 不是当前激活,或者窗口最小化了
  • prerender 重新生成,对用户不可见

visibilitychange事件示例

        
var hiddenProperty = 'hidden' in document ? 'hidden' :
            'webkitHidden' in document ? 'webkitHidden' :
                'mozHidden' in document ? 'mozHidden':null;
        var visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');
        var onVisibilityChange = function () {
            if (!document[hiddenProperty]) {
                console && console.log('页面激活');
            } else {
                console && console.log('页面非激活');
            }
        }
        document.addEventListener(visibilityChangeEvent, onVisibilityChange);

以上两种方式在某些情况下存在一定差异,为了实现一些需求,可能需要组合使用。



转载请注明:清风亦平凡 » Javascript判断当前页面是否处于激活状态

]]>
https://www.mlplus.net/2022/11/21/javascript%e5%88%a4%e6%96%ad%e5%bd%93%e5%89%8d%e9%a1%b5%e9%9d%a2%e6%98%af%e5%90%a6%e5%a4%84%e4%ba%8e%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81/feed/ 0
在非HTTPS站点中使用Content Security Policy引发的问题 https://www.mlplus.net/2022/10/25/csp/ https://www.mlplus.net/2022/10/25/csp/#respond Tue, 25 Oct 2022 08:40:42 +0000 https://www.mlplus.net/?p=4011 背景

有一个客户需要对以前老项目部分功能进行升级,需要升级页面按照最新版本的内容进行更新,测试发现页面无法加载。F12使用开发者工具发现所有资源文件异常,所有的资源文件竟然自动将HTTP协议换成 HTTPS 协议。异常信息如下图所示:

资源加载HTTPS错误

项目目前部署是非HTTPS的,很奇怪为什么会自动转换为HTTPS资源。经过排查在异常页面中发现了问题,在head节点下发现以下代码


<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

以上内容会将HTTP协议自动转换为HTTPS协议。

Content-Security-Policy

Content-Security-PolicyCSP)允许站点管理者控制用户代理能够为指定的页面加载哪些资源。除了少数例外情况,设置的政策主要涉及指定服务器的源和脚本结束点。这将帮助防止跨站脚本攻击(Cross-Site Script)。

CSP 的实质就是白名单制度,大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。

两种方法可以启用 CSP。一种是通过 HTTP 头信息的Content-Security-Policy的字段。

另一种是通过网页的<meta>标签。

  • 脚本:只信任当前域名
  • <object>标签:不信任任何URL,即不加载任何资源
  • 样式表:只信任cdn.example.orgthird-party.org
  • 框架(frame):必须使用HTTPS协议加载
  • 其他资源:没有限制

限制选项

资源加载限制

以下选项限制各类资源的加载。

  • script-src:外部脚本
  • style-src:样式表
  • img-src:图像
  • media-src:媒体文件(音频和视频)
  • font-src:字体文件
  • object-src:插件(比如 Flash
  • child-src:框架
  • frame-ancestors:嵌入的外部资源(比如<frame><iframe><embed><applet>
  • connect-srcHTTP 连接(通过 XHRWebSocketsEventSource等)
  • worker-srcworker脚本
  • manifest-src:manifest 文件

default-src

default-src用来设置上面各个选项的默认值。


Content-Security-Policy: default-src 'self

上面代码限制所有的外部资源,都只能从当前域名加载。

如果同时设置某个单项限制(比如font-src)和default-src,前者会覆盖后者,即字体文件会采用font-src的值,其他资源依然采用default-src的值。

URL 限制

有时,网页会跟其他 URL 发生联系,这时也可以加以限制。

  • frame-ancestors:限制嵌入框架的网页
  • base-uri:限制<base#href>
  • form-action:限制<form#action>

其他限制

其他一些安全相关的功能,也放在了 CSP 里面。

  • block-all-mixed-contentHTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
  • upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议
  • plugin-types:限制可以使用的插件格式
  • sandbox:浏览器行为的限制,比如不能有弹出窗口等。

参考

https://www.ruanyifeng.com/blog/2016/09/csp.html



转载请注明:清风亦平凡 » 在非HTTPS站点中使用Content Security Policy引发的问题

]]>
https://www.mlplus.net/2022/10/25/csp/feed/ 0
使用ABP框架UI找不到文件的异常 https://www.mlplus.net/2022/10/05/abp-install-libs/ https://www.mlplus.net/2022/10/05/abp-install-libs/#respond Wed, 05 Oct 2022 14:03:03 +0000 https://www.mlplus.net/?p=4000 背景

自己的一个小应用使用了ABP框架最新内容创建了一个项目模板,为了快速的完成第一版就是用来ABP自带的UI框架。在测试项目模板正常与否的时候发现关于UI方面的异常信息,第一次使用ABP官方提供的UI框架,所以先在此做一下记录。

以下是异常信息:

AbpException: Could not find the bundle file ‘/libs/abp/core/abp.css’ for the bundle ‘LeptonXLite.Global’!

ABP框架UI异常图片

解决方法

安装ABP框架UI依赖包abp install-libs



转载请注明:清风亦平凡 » 使用ABP框架UI找不到文件的异常

]]>
https://www.mlplus.net/2022/10/05/abp-install-libs/feed/ 0
.Net Core获取Window系统机器码 https://www.mlplus.net/2022/10/04/dot-net-core-machinecode/ https://www.mlplus.net/2022/10/04/dot-net-core-machinecode/#respond Tue, 04 Oct 2022 06:27:45 +0000 https://www.mlplus.net/?p=3989 背景

因为朋友想做一个软件认证相关的东西,所以考虑到关于电脑唯一标识机器码的问题。关于机器码之前并没有真正实现过,也只是了解大概的原理。今天就做一下简单记录,以便以后使用。

机器码

机器码指的是软件根据计算机的硬件信息,例如:CPU、内存、主板序列号等,按照一定的算法生成的一串无规律的字符串,并且在不同计算机上生成的机器码是不一样的,因为每台计算机的硬件信息不一样。机器码,也叫序列号认证码注册申请码等。

代码实现


using System.Management;
using System.Runtime.InteropServices;

namespace HonourWorld.Core.Common
{
    public sealed class MachineCode
    {
        private readonly static MachineCode machineCode=new MachineCode();
        public static string GetMachineCodeString()
        {
            string machineCodeString = $"PC.{machineCode.GetCpuInfo()}.{machineCode.GetHDid()}.{machineCode.GetDiskSerialNumber()}.{machineCode.GetMacAddress()}.{machineCode.GetBiosSerialNumber()}";
            return machineCodeString;
        }
        private MachineCode()
        { 
        }
        ///   <summary>
        ///   获取cpu序列号
        ///   </summary>
        ///   <returns> string </returns>
        public string GetCpuInfo()
        {
            string? cpuInfo = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass cimobject = new ManagementClass("Win32_Processor"))
                    {
                        ManagementObjectCollection moc = cimobject.GetInstances();
                        foreach (ManagementObject mo in moc)
                        {
                            if (mo == null || mo.Properties == null)
                            {
                                continue;
                            }
                            var info = mo.Properties["ProcessorId"];
                            if (info!=null)
                            {
                                cpuInfo = info.Value.ToString();
                            }
                            mo?.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return (cpuInfo??"").Trim();
        }
        ///   <summary>
        ///   获取硬盘ID
        ///   </summary>
        ///   <returns> string </returns>
        public string GetHDid()
        {
            string HDid = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass cimobject1 = new ManagementClass("Win32_DiskDrive"))
                    {
                        ManagementObjectCollection moc1 = cimobject1.GetInstances();
                        foreach (ManagementObject mo in moc1)
                        {
                            HDid = (string)mo.Properties["Model"].Value;
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return HDid.Trim();
        }

        /// <summary>
        /// 获得硬盘序列号
        /// </summary>
        /// <returns></returns>
        public string GetDiskSerialNumber()
        {
            string serialNumber = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass bios = new ManagementClass("Win32_PhysicalMedia"))
                    {
                        ManagementObjectCollection moc1 = bios.GetInstances();
                        foreach (ManagementObject mo in moc1)
                        {
                            serialNumber = (string)mo.Properties["SerialNumber"].Value;
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return serialNumber.Trim();
        }

        ///   <summary>
        ///   获取网卡硬件地址
        ///   </summary>
        ///   <returns> string </returns>
        public string GetMacAddress()
        {
            string mocAddress = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration"))
                    {
                        ManagementObjectCollection moc2 = mc.GetInstances();
                        foreach (ManagementObject mo in moc2)
                        {
                            if ((bool)mo["IPEnabled"]&& mo["MacAddress"]!=null)
                            {
                                mocAddress = (string)mo["MacAddress"];
                            }
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return mocAddress.Trim();
        }

        /// <summary>
        /// 获得主板序列号
        /// </summary>
        /// <returns></returns>
        public string GetBiosSerialNumber()
        {

            string serialNumber = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass bios = new ManagementClass("Win32_BaseBoard"))
                    {
                        ManagementObjectCollection moc1 = bios.GetInstances();
                        foreach (ManagementObject mo in moc1)
                        {
                            serialNumber = (string)mo.Properties["SerialNumber"].Value;
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return serialNumber.Trim();
        }

    }
}

调用方式


        private void btnIntroduction_Click(object sender, EventArgs e)
        {
            string machineCode = Honour.MachineCode.GetMachineCodeString();
            machineCode = EncryptionSha.GetSha1HashString(machineCode);
            machineCode = Regex.Replace(machineCode, @"\w{8}", "$0-", RegexOptions.IgnoreCase).TrimEnd('-');
            var result = MessageBox.Show($"机器码:{machineCode}", "提示", MessageBoxButtons.OK);
        }

机器码是使用CPU序列号硬盘ID硬盘序列号MAC地址主板序列号进行拼接的字符串。由于电脑硬件信息拼接而成的无规则字符串,所以显得杂乱。为了让其看上去比较规范就对拼接的取散列信息,散列算法有很多种,例如:MD5SHA1等。这里使用SHA1来实现需求。

机器码

示例下载

.Net Core机器码示例
以上获取机器相关信息的方式方法只支持Windows系统。



转载请注明:清风亦平凡 » .Net Core获取Window系统机器码

]]>
https://www.mlplus.net/2022/10/04/dot-net-core-machinecode/feed/ 0
判断登录的QQ是否已经加入指定的QQ群之二 https://www.mlplus.net/2022/09/25/check-qq-in-group/ https://www.mlplus.net/2022/09/25/check-qq-in-group/#comments Sun, 25 Sep 2022 10:32:10 +0000 https://www.mlplus.net/?p=3966 背景

任何事情都有一个原由,本篇内容也不例外。如标题所示,关于判断登录的QQ是否已经加入指定的QQ群的问题。为什么是之二?因为之前已经写过一次相关内容了。既然曾经已经写过一次为什么还要写第二次?因为今天早上收到了一个邮件通知,这个是博客评论通知,有人评论就以邮件形式告知。评论内容是这样的:c#那个QQ群验证已经无法使用了能更新下吗。如下图所示:

关于C#QQ群验证的评论回复图

没有错,就如我回复的一样,示例程序的代码的确不可以用了,不过思路依然可以使用。

分析

其实没有什么好分析的,思路与上次一样。只是上次验证的地址(http://qun.qzone.qq.com)无效了,可能是下线了吧!不过QQ也有关于群里管理的网站(https://qun.qq.com/),这个网站依然可以实现这样的操作。实现比较简单,访问网址https://qun.qq.com/member.html并登录,然后通过正则表达式匹配就行了!

QQ群管理

此次还是与上次有些变化的,网页登录竟然取消了账号与密码登录,只能手机扫码或者选择已经登录的账号。也许是为了安全考虑吧,不过这也不重要了!

实现

这里使用Microsoft .netWindows Form实现示例,使用WebBrowser(其实也可以使用CefSharpWebView2等控件)控件加载网址https://qun.qq.com/member.html登录成功后,有使用正则匹配指定的群号来完成操作。

代码大致如下:


using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace QQGroupVerification
{
    public partial class QQVerificationLogin : Form
    {
        public QQVerificationLogin()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            webBrowser1.Navigate("https://xui.ptlogin2.qq.com/cgi-bin/xlogin?pt_disable_pwd=1&appid=715030901&daid=73&hide_close_icon=1&pt_no_auth=1&s_url=https%3A%2F%2Fqun.qq.com%2Fmember.html%23");
            webBrowser1.ScriptErrorsSuppressed=true;
            webBrowser1.DocumentCompleted += WebBrowser1_DocumentCompleted;
        }

        private void WebBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (webBrowser1.ReadyState == WebBrowserReadyState.Complete&&webBrowser1.Url.ToString().StartsWith("https://qun.qq.com"))
            {
                webBrowser1.Hide();
                string webPageString = webBrowser1.Document.Body.InnerHtml;
                webPageString = Regex.Replace(webPageString, @"<script\s?type=""text/template""\s+?id=.+?>[\s\S]+?</script>", "", RegexOptions.IgnoreCase);
                var match = Regex.Match(webPageString, @"<div\sclass=.?body-content.?>[\s\S]+?<div\sclass=.?member-body.?>[\s\S]+(?=<div\s+?class=""foot\sbtn-footer"")", RegexOptions.IgnoreCase);
                if (match == null|| !match.Success)
                {
                    return;
                }
                string groupid =this.textBox1.Text;// "这里是你的QQ群ID";
                if (Regex.IsMatch(match.Value, $"data-id=\"{groupid}\""))
                {
                    MessageBox.Show("验证成功");
                    CloseChrome();
                    ProgramMain form2 = new ProgramMain();
                    form2.Show();

                }
                else
                {
                    MessageBox.Show("请加指定交流群入本群");
                    CloseChrome();
                    Application.Exit();
                }
            }
            bool isBusy = webBrowser1.IsBusy;
        }
      
        public void CloseChrome()
        {
            if (webBrowser1 != null)
            {
                webBrowser1.Dispose();
            }
        }

    }
}

此次代码与上次相比主要是多了两个正则,为什么要多这两个正则?新的QQ群管理网站用了前后端分离用了一些模板,这些模板可能会干扰接下来的操作。所以,第一个正则表达式替换掉所有JavaScript模板相关代码,替换为空字符串。第二个正则用来获取QQ群里列表区域所有文本内容,接下来才是判断返回信息中是否包含指定的QQ群。

预览

判断登录的QQ是否已经加入指定的QQ群之二-第2张图片
判断登录的QQ是否已经加入指定的QQ群之二-第3张图片

另一种方式

这一种方式是使用Windows Api查找窗体句柄,要打开指定的群,找到了窗体就认为加入了群。不过这个方法是不精准的,这个通过查找窗体的标题。知道要验证QQ群的名称,随便建一个临时群修改名称为要验证QQ群名称就可以过掉验证。

实现


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace QQGroupVerificationByWindowsApi
{
    public class WindownApiVerification
    {
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        private static extern IntPtr FindWindow(string IpClassName, string IpWindowName);
        /// <summary>
        /// 找到句柄
        /// </summary>
        /// <param name="IpClassName">类名</param>
        /// <returns></returns>
        public static IntPtr GetHandle(string IpClassName, string IpWindowName)
        {
            return FindWindow(IpClassName, IpWindowName);

        }
        /// <summary>
        /// 判断是否打开了某指定的QQ群
        /// </summary>
        /// <param name="qqGroupName">参数是群名</param>
        /// <returns></returns>
        public static bool IsInQQGroup(string qqGroupName) 
        {
            IntPtr result = GetHandle("TXGuiFoundation", qqGroupName);
            if (result == IntPtr.Zero)
            {
                return false;
            }
            return true;
        }
    }
}

使用方式如下:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace QQGroupVerificationByWindowsApi
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string qqGroup = textBox1.Text;
            if(string.IsNullOrWhiteSpace(qqGroup))
            {

                MessageBox.Show("请输入需要验证的QQ群的名称","提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
                return;
            }
           var result=WindownApiVerification.IsInQQGroup(qqGroup);
            if (result)
            {
                MessageBox.Show("存在", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            MessageBox.Show("不存在", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}

预览

判断登录的QQ是否已经加入指定的QQ群之二-第4张图片

示例下载

判断登录的QQ是否已经加入指定的QQ群

总结

以上就是关于判断QQ是否加入指定QQ群的简单判断,使用哪种方式就看个人想法了!其实还有其他的一些方法,例如:内存、网络等。不过就不再进行深入了解了,实现其他的方式现在也没有意义。无论哪种方式都可以被绕过,有时候不必太过执着。

警告:本章内容仅供学习交流,请勿使用非法用途。有任何非法行为均需自行承担!!!!



转载请注明:清风亦平凡 » 判断登录的QQ是否已经加入指定的QQ群之二

]]>
https://www.mlplus.net/2022/09/25/check-qq-in-group/feed/ 6
.NET CORE实现SHA1 https://www.mlplus.net/2022/09/24/net-core-sha1/ https://www.mlplus.net/2022/09/24/net-core-sha1/#comments Sat, 24 Sep 2022 14:34:21 +0000 https://www.mlplus.net/?p=3956 背景

因业务需求,需要保存一些指定的字符串,但这些字符串可能相同。为了避免重复存储造成数据大量冗余,所以需要对这些字符串进行一些散列计算。使这一些相同的字符串产生唯一的标识,实现业务上的需求。关于字符串散列算法还是比较多的,例如:MD2MD4MD5Sha1Sha256Sha512等等,在结合字符串散列碰撞以及散列最终长度考虑,最后选中Sha1作为此次散列算法。

SHA-1

(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。

.NET CORE 实现 SHA1

以下是使用.Net Core自带类库实现Sha1的简单方法。

    
public static class EncryptionSha
{
        public static byte[] GetSha1Hash(string inputString)
        {
            var sha1 = SHA1.Create();
            byte[] data = sha1.ComputeHash(Encoding.UTF8.GetBytes(inputString));
            sha1.Dispose();
            return data;
        }

        public static string GetSha1HashString(string inputString)
        {
            StringBuilder sb = new StringBuilder();
            byte[] hashByte = GetSha1Hash(inputString);
            foreach (byte b in hashByte)
            {
                sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }
    }

使用方式


using System;

namespace TestSha1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(EncryptionSha.GetSha1HashString("Hello World!"));
            Console.ReadKey();
        }
    }
}
.net core使用Sha1散列调用方式

示例

SHA1散列



转载请注明:清风亦平凡 » .NET CORE实现SHA1

]]>
https://www.mlplus.net/2022/09/24/net-core-sha1/feed/ 2
使用Nginx配置资源目录达到下载目的 https://www.mlplus.net/2022/09/05/nginx-downlaod/ https://www.mlplus.net/2022/09/05/nginx-downlaod/#respond Mon, 05 Sep 2022 02:24:24 +0000 https://www.mlplus.net/?p=3931 使用Nginx配置资源目录达到下载目的-第0张图片

背景

实施人员临时需要在客户服务器上配置一个可下载的目录,为了可以在内网进行交换文件。

Nginx配置

       
location /resources {
          #下载的资源目录 绝对路径 最后 "/" 结尾
          alias /app/smart/logs/;
          autoindex on;
          autoindex_format html; #以html风格将目录展示在浏览器中
          autoindex_exact_size off; #切换为 off 后,以可读的方式显示文件大小,单位为 KB、MB 或者 GB
          autoindex_localtime on; #以服务器的文件时间作为显示的时间
          client_max_body_size 4048M;
          proxy_max_temp_file_size 4048M;
          proxy_send_timeout 600; #后端服务器数据回传时间(代理发送超时)
          proxy_read_timeout 600; #连接成功后,后端服务器响应时间(代理接收超时)
          
          #符合条件,直接下载
          if ($request_filename ~* ^.*?\.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx)$){
             add_header Content-Disposition attachment;
          }
        }

访问地址规则为:http://url/resources,这样就可以看到我们需要页面。示例如下:

https://www.skyfinder.cc/resources



转载请注明:清风亦平凡 » 使用Nginx配置资源目录达到下载目的

]]>
https://www.mlplus.net/2022/09/05/nginx-downlaod/feed/ 0
WordPress自动对没有alt属性的img添加alt属性 https://www.mlplus.net/2022/08/07/wordpress-auot-add-img-alt/ https://www.mlplus.net/2022/08/07/wordpress-auot-add-img-alt/#respond Sun, 07 Aug 2022 01:00:00 +0000 https://www.mlplus.net/?p=3918 背景

使用了微软必应搜索的站长工具,看到必应搜索工具中SEO报告提示错误蛮多。主要有两个错误提示,其中一个是关于页面描述内容长短问题,另外一个就是img标签没有alt属性的问题。页面固定img标签的alt属性比较容易改,而文章内容的img标签的alt属性就没有那么方便了。

SEO错误提示

博客使用的是wordpress,其实处理起来也没有那么麻烦。一种方法就是使用现成的插件,例如:SEO Friendly Images,另一种就是在使用模板目录下,找到functions.php函数文件添加自定义函数处理。

这里提供自定义函数,内容如下:


//Wordpress判断并自动添加图片ALT属性
function image_alt($imgalt) {
	global $post;
	$title = $post->post_title;
	$imgUrl = "/<img\s*?.+?[^>]>/si";
	$isMatch=preg_match_all($imgUrl,$imgalt,$matches,PREG_SET_ORDER);
	if($isMatch) {
		if(!empty($matches) ) {
			for ($i=0; $i < count($matches); $i++) {
				$tag = $url = $matches[$i][0];
				$tag=preg_replace('/alt="\s*"/','',$tag);
				$judge = '/alt=/';
				$isMatched=preg_match($judge,$tag,$match,PREG_OFFSET_CAPTURE);
				if($isMatched) {
				   continue;
				}
				$tag=preg_replace('/<img/','<img alt="'.$title.'-第'.$i.'张图片"',$tag);
				$imgalt =str_replace($url,$tag,$imgalt);
			}
		}
	}
	return $imgalt;
}
add_filter('the_content', 'image_alt');



转载请注明:清风亦平凡 » WordPress自动对没有alt属性的img添加alt属性

]]>
https://www.mlplus.net/2022/08/07/wordpress-auot-add-img-alt/feed/ 0
MYSQL修改数据库、表、字段字符集 https://www.mlplus.net/2022/08/05/mysql-update-character/ https://www.mlplus.net/2022/08/05/mysql-update-character/#respond Fri, 05 Aug 2022 01:00:00 +0000 https://www.mlplus.net/?p=3906 背景

由于mysql低版本暴了漏洞,所以客户就升级了mysql版本。升级到了最新版8.0.30,升级最新版后,应用就再也查不出数据,恢复到最初的版本有可以查到数据。经过确认最初数据库的表字符集为utf8,升级后这些字符集自动修改为utf8mb3。新版本数据库移除了utf8字符集,而应用本身不支持utf8mb3,所以需要修改这些字符集。经过再次确认最新版mysql以及应用都支持utf8mb4,最终决定修改字符集为utf8mb4

修改字符集

数据库


ALTER DATABASE 数据库名称 DEFAULT CHARACTER SET 编码名称 [COLLATE ...];

例如:


ALTER DATABASE activity CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci;

ALTER TABLE 表名称  CONVERT TO CHARACTER SET 编码名称 [COLLATE ...]

例如:

ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

以上修改会同时变更现有数据的字符集。如果只修改表的默认字符集,而不修改现有数据的字符集可以使用以下内容


ALTER TABLE 表名称 DEFAULT CHARACTER SET 编码名称 [COLLATE...];

例如:

ALTER TABLE users DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

字段

ALTER TABLE 表名称 CHANGE 字段名称 字段名称 CHARACTER SET 编码名称 [COLLATE ...];

例如:

ALTER TABLE users CHANGE LoginName LoginName VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

批量修改数据库表字符集

第一步

使用以下脚本并执行,产生修改数据表字符集的脚本。

SELECT 
CONCAT("ALTER TABLE ",TABLE_SCHEMA,'.',TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;") 
AS target_tables
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA IN('数据库名称','数据库名称') 
AND TABLE_TYPE="BASE TABLE";

例如:

SELECT 
CONCAT("ALTER TABLE ",TABLE_SCHEMA,'.',TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;") 
AS target_tables
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA IN('phpdbeqx') 
AND TABLE_TYPE="BASE TABLE";
MYSQL修改数据库、表、字段字符集-第0张图片

导出以上查询结果,执行导出的查询结果即可。如果数据量比较大,执行的时间可能会比较久。

查看字符集

查看数据库支持的字符集

select * from information_schema.character_sets;

查看数据库支持的检验规则

select * from information_schema.collations;

查看表字符集及检验规则

select * from information_schema.tables where table_name='表名' 

查看字段编码

SHOW FULL COLUMNS FROM  表名



转载请注明:清风亦平凡 » MYSQL修改数据库、表、字段字符集

]]>
https://www.mlplus.net/2022/08/05/mysql-update-character/feed/ 0
.NET CORE引用Aspose.Words的异常Could not load file or assembly https://www.mlplus.net/2022/08/01/net-core-could-not-load-file-or-assembly/ https://www.mlplus.net/2022/08/01/net-core-could-not-load-file-or-assembly/#respond Mon, 01 Aug 2022 07:22:50 +0000 https://www.mlplus.net/?p=3897 背景

引用了一个第三方的dll库,编译无任何异常提示,但是运行就报异常。自从有了Nuget基本没有再使用直接引用dll这种方式了。经过确认dll也设置了输出到目录,在 bin目录也的确存在此dll库。

异常信息

Could not load file or assembly ‘Aspose.Words, Version=20.6.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56’. 系统找不到指定的文件。
System.IO.FileNotFoundException: Could not load file or assembly ‘Aspose.Words, Version=20.6.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56’. 系统找不到指定的文件。
File name: ‘Aspose.Words, Version=20.6.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56’

.NET CORE引用Aspose.Words的异常Could not load file or assembly-第0张图片

解决

查看下引用dll库的项目文件,项目文件中少了dll的相关版本号。


<Reference Include="Aspose.Words">

在这条记录中添加对应的版本号后,问题得到解决!如下:


<Reference Include="Aspose.Words" Version="20.6.0.0">



转载请注明:清风亦平凡 » .NET CORE引用Aspose.Words的异常Could not load file or assembly

]]>
https://www.mlplus.net/2022/08/01/net-core-could-not-load-file-or-assembly/feed/ 0