-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlocal-search.xml
1098 lines (528 loc) · 510 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>将我们的博客录入bing搜索引擎</title>
<link href="/2025/01/22/site-bing-site-seo/"/>
<url>/2025/01/22/site-bing-site-seo/</url>
<content type="html"><![CDATA[<p>随着ChatGPT的加成,2024年,微软旗下的bing在pc端已经完全超过了百度,已经超过了半数。</p><p>所以,为了能有更多人访问我们的作品,我们也要考虑实现bing对我们的博客站点收录的问题了。<br><img src="/../../images/site/seo/202412bing-statcounter-in-china-pc.jpeg" alt="2024年12月中国pc端搜索引擎的市场占有率"></p><h1 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h1><p>其实,搜索引擎是如何工作的呢?</p><p>在以前,搜索引擎会根据网页的链接情况来递归的对所有能http链接到的网页进行爬取, 后来为了限制爬虫,衍生出了robot协议和sitemap网站地图,用来主动高速搜索引擎,我有哪些东西你可以爬。</p><p>我们这里就利用sitemap网站地图的办法来实现bing收录我们的站点内容。</p><h1 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h1><h2 id="创建sitemap-xml文件"><a href="#创建sitemap-xml文件" class="headerlink" title="创建sitemap.xml文件"></a>创建sitemap.xml文件</h2><p>我现在使用的hexo主题搭建的博客系统, 所以有现成的插件可以帮助我们生成sitemap.xml文件。</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ada">npm install hexo-generator-sitemap <span class="hljs-comment">--save</span><br></code></pre></td></tr></table></figure><p>虽然版本比较老了,但依旧能用。</p><p>还可以在<code>_config.yml</code>按如下配置调整:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># 默认配置如下</span><br><span class="hljs-attr">sitemap:</span><br> <span class="hljs-attr">path:</span> <br> <span class="hljs-bullet">-</span> <span class="hljs-string">sitemap.xml</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">sitemap.txt</span><br> <span class="hljs-attr">template:</span> <span class="hljs-string">./sitemap_template.xml</span><br> <span class="hljs-attr">template_txt:</span> <span class="hljs-string">./sitemap_template.txt</span><br> <span class="hljs-attr">rel:</span> <span class="hljs-literal">false</span><br> <span class="hljs-attr">tags:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">categories:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><p>如果不想某个文章被添加到sitemap中,可以在文章的front matter中添加<code>sitemap: false</code>。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>title: 不想加入sitemap的文章<br>date: 2025-01-22<br><span class="hljs-section">sitemap: false</span><br><span class="hljs-section">---</span><br></code></pre></td></tr></table></figure><p>安装配置完成后,在项目自动构建的时候,这个sitemap文件也就会自动生成了,无需额外操作,可以通过如下方式验证:</p><figure class="highlight dts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dts"><span class="hljs-symbol">https:</span><span class="hljs-comment">//your-blog-domain.com/sitemap.xml</span><br></code></pre></td></tr></table></figure><h2 id="bing站长后台添加网站"><a href="#bing站长后台添加网站" class="headerlink" title="bing站长后台添加网站"></a>bing站长后台添加网站</h2><p>打开bing的站点管理工具<a href="https://www.bing.com/webmasters">webmasters</a>页面,第一次打开需要登录。<br><img src="/../../images/site/seo/bing_tool_home.jpeg" alt="bing站点管理工具首页"><br>选择手动添加网站:<br><img src="/../../images/site/seo/bing_site_tool.png" alt="手动添加网站"></p><p>然后需要在我们的博客根目录下放置一个文件,来验证这个网站的有效性。<br><img src="/../../images/site/seo/bing_verify_site_method.png" alt="验证网站方式"><br>将上面的<code>BingSiteAuth.xml</code>下载下来放到自己网站的根目录下,几分钟后就可以验证成功了。</p><p>到此,我们的站点算是创建成功了。</p><h2 id="提交sitemap文件到bing"><a href="#提交sitemap文件到bing" class="headerlink" title="提交sitemap文件到bing"></a>提交sitemap文件到bing</h2><p>在左侧导航栏里选择<code>网站地图</code>,然后添加网站地图,这个时候就会用到上面生成的sitemap文件。<br><img src="/../../images/site/seo/bing_add_sitemap.png" alt="添加网站地图"><br>把上面验证过的sitemap地址输入,点击提交,就完事了。</p><h2 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h2><p>最后,在bing搜索框里输入<code>site:your-blog-domain.com</code>,如果能看到我们的博客,就说明我们的站点已经被bing收录了。</p><p>以后我们在写新的博客时,新的网页会在打包时自动添加到sitemap.xml中,然后bing会隔一段时间更新一下站点地图,这样我们新的文章就会被bing收录啦。</p><p>如此,一劳永逸,我们就可以安安心心的写文章啦。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://github.com/hexojs/hexo-generator-sitemap">hexo-generator-sitemap github地址</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>建站</tag>
</tags>
</entry>
<entry>
<title>hexo建站增加自己的评论模块</title>
<link href="/2025/01/09/site-hexo-comment-feature/"/>
<url>/2025/01/09/site-hexo-comment-feature/</url>
<content type="html"><![CDATA[<h1 id="jekyll时代"><a href="#jekyll时代" class="headerlink" title="jekyll时代"></a>jekyll时代</h1><p>使用的是beaudar插件, 在更新到hexo-fluid 博客架构后仍然可以使用,<br>但是需要代码侵入式改造,不如fluid适配的评论体系更优雅,所以这次我们尝试一些其他的评论插件。</p><p>评论插件的挑选原则:</p><ul><li>免费:我的博客除了域名,全都是免费的,不可能为了一个评论模块而付费</li><li>易接入:最后是fluid主题支持的,不然还要侵入式改造代码</li><li>响应速度:希望评论模块快速响应</li><li>可匿名:可以匿名发表评论,注册制会极大的影响用户发言的积极性</li></ul><p>根据以上原则,我们对fluid官方支持的评论插件进行了筛选与评测:</p><h1 id="beaudar"><a href="#beaudar" class="headerlink" title="beaudar"></a>beaudar</h1><p>基于 GitHub Issues.博客的每次评论都会在github主站上生成一个issue,评论内容会以markdown格式保存在issue的评论区。<br>参考以前的文章:<a href="https://www.hancher.top/2022/09/09/site-beaudar-error/">beaudar评论插件安装</a></p><h1 id="Gitalk"><a href="#Gitalk" class="headerlink" title="Gitalk"></a><a href="https://gitalk.github.io/">Gitalk</a></h1><p>基于 GitHub Issues. 通beaudar一样。</p><p>这种方式完全免费,且适合那些通过github pages 部署的博客。</p><h2 id="安装过程"><a href="#安装过程" class="headerlink" title="安装过程"></a>安装过程</h2><ol><li><a href="https://github.com/settings/applications/new">点击这里</a>申请github 的oauth授权应用<br><img src="/images/site/comment/github_oauth_app_register.png" alt="github oauth 注册"><br>按图中配置即可,地址写自己博客的地址,其他的随便写,后续还可以改。 </li><li>创建app的密钥<br><img src="/images/site/comment/github_oauth_app_gen_client_secret.png" alt="授权app的id和密钥"> </li><li><code>_config.yml</code>里撇嘴gitalk的配置<br><strong>fluid配置:</strong></li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">post:</span><br> <span class="hljs-attr">comments:</span> <span class="hljs-comment"># 开启评论</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">type:</span> <span class="hljs-string">gitalk</span> <span class="hljs-comment"># 选择评论插件类型</span><br><br><span class="hljs-attr">gitalk:</span><br> <span class="hljs-attr">clientID:</span> <span class="hljs-string">xxxxxxxxx</span> <span class="hljs-comment"># github 应用id,必须</span><br> <span class="hljs-attr">clientSecret:</span> <span class="hljs-string">6fcbc2edcfxxx</span> <span class="hljs-comment"># github 应用密钥,必须</span><br> <span class="hljs-attr">repo:</span> <span class="hljs-string">hanchers.github.io</span> <span class="hljs-comment"># github 仓库, 必须</span><br> <span class="hljs-attr">owner:</span> <span class="hljs-string">hancher</span> <span class="hljs-comment"># github 仓库所有者, 必须</span><br> <span class="hljs-attr">admin:</span> [<span class="hljs-string">'admin'</span>] <span class="hljs-comment"># github 仓库有写权限的账号</span><br> <span class="hljs-attr">language:</span> <span class="hljs-string">zh-CN</span> <span class="hljs-comment"># 语言</span><br> <span class="hljs-attr">labels:</span> [<span class="hljs-string">'Gitalk'</span>] <span class="hljs-comment"># GitHub issue 的标签。</span><br> <span class="hljs-attr">perPage:</span> <span class="hljs-number">10</span> <span class="hljs-comment"># 每页大小</span><br> <span class="hljs-attr">pagerDirection:</span> <span class="hljs-string">last</span> <span class="hljs-comment"># 评论排序方式, last为按评论创建时间倒叙,first为按创建时间正序</span><br> <span class="hljs-attr">distractionFreeMode:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># 类似Facebook评论框的全屏遮罩效果.</span><br> <span class="hljs-attr">createIssueManually:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># 如果当前页面没有相应的 isssue 且登录的用户属于 admin,则会自动创建 issue。如果设置为 true,则显示一个初始化页面</span><br> <span class="hljs-comment"># 反向代理,为了支持 CORS. 默认的不可用,需要自己配置,参考下述文档</span><br> <span class="hljs-comment"># https://github.com/gitalk/gitalk/issues/429</span><br> <span class="hljs-comment"># https://github.com/Zibri/cloudflare-cors-anywhere</span><br> <span class="hljs-attr">proxy:</span> <span class="hljs-string">https://shielded-brushlands-08810.herokuapp.com/https://github.com/login/oauth/access_token</span> <span class="hljs-comment"># 有些人搭建的代理</span><br><span class="hljs-comment"># proxy: https://cors-server.68756978.xyz/github_access_token # 备用代理地址1</span><br><br></code></pre></td></tr></table></figure><p>效果如图,然后我们就可以评论了<br><img src="/images/site/comment/gitalk_init_show.png" alt="gitalk评论效果图"></p><h2 id="评价"><a href="#评价" class="headerlink" title="评价"></a>评价</h2><ol><li>需要配置代理地址,这里有一定的开发成本,如果使用开源的,不稳定性还很高(放弃选择的主要原因)。</li><li>使用上需要注册github</li><li>响应有点慢</li></ol><h1 id="utterances"><a href="#utterances" class="headerlink" title="utterances"></a><a href="https://utteranc.es/">utterances</a></h1><p>同样也是基于GitHub Issues实现,原理通上面的一样</p><h2 id="安装过程-1"><a href="#安装过程-1" class="headerlink" title="安装过程"></a>安装过程</h2><p>同beaudar一样,<a href="https://github.com/apps/utterances">点击这里在github上安装应用</a>。</p><p>选择你的github博客仓库。<br><img src="/images/site/comment/utterances_install.png" alt="utterances安装选择"><br>安装好后,在<code>_config.yml</code>里配置utterances的配置即可</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">post:</span><br> <span class="hljs-attr">comments:</span> <span class="hljs-comment"># 开启评论</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">type:</span> <span class="hljs-string">gitalk</span> <span class="hljs-comment"># 选择评论插件类型</span><br><br><span class="hljs-comment"># 其他配置可以参考官网</span><br><span class="hljs-attr">utterances:</span><br> <span class="hljs-attr">repo:</span> <span class="hljs-string">Hanchers/Hanchers.github.io</span><br> <span class="hljs-attr">issue_term:</span> <span class="hljs-string">pathname</span><br> <span class="hljs-attr">label:</span> <span class="hljs-string">utterances</span><br> <span class="hljs-attr">theme:</span> <span class="hljs-string">github-light</span><br> <span class="hljs-attr">theme_dark:</span> <span class="hljs-string">github-dark</span><br></code></pre></td></tr></table></figure><p>效果:<br><img src="/images/site/comment/utterances_show.png" alt="utterances效果"></p><h2 id="评价-1"><a href="#评价-1" class="headerlink" title="评价"></a>评价</h2><ol><li>utterances整体上比gitalk要好一些,安装简单很多,且不用代理。</li><li>完全依赖github,这一点和beaudar一样,能满足我评价数据持久化的需求。</li><li>不幸的是,响应上,同beaudar一样,都比较慢</li><li>fluid主题支持,安装很简单。</li><li>评价功能使用比较少,且不喜欢折腾的,这款插件值得推荐。</li></ol><h1 id="giscus"><a href="#giscus" class="headerlink" title="giscus"></a><a href="https://giscus.app/zh-CN">giscus</a></h1><p>giscuss是利用 GitHub Discussions 实现的评论系统。</p><p>同上级几个评论插件不同的是,前面的是利用的GitHub Issues,而giscuss是利用GitHub Discussions。<br><img src="/images/site/comment/github_discussions.png" alt="github讨论模块"><br>giscus 加载时,会使用 GitHub Discussions 搜索 API 根据选定的映射方式(如 URL、pathname、title 等)来查找与当前页面关联的 discussion。如果找不到匹配的 discussion,giscus bot 就会在第一次有人留下评论或回应时自动创建一个 discussion。</p><p>访客如果想要评论,必须按照 GitHub OAuth 流程授权 giscus app 代表他发布,或者可以直接在 GitHub Discussion 里评论。你可以在 GitHub 上管理评论。</p><h2 id="安装过程-2"><a href="#安装过程-2" class="headerlink" title="安装过程"></a>安装过程</h2><ol><li><p>启用github仓库的Discussions功能</p></li><li><p><a href="https://github.com/apps/giscus">点击这里</a>安装giscus应用,然后选择你开启讨论模块的仓库</p></li><li><p><a href="https://giscus.app/zh-CN">点击这里</a>按下图配置仓库名称和你的评论所有类型<br><img src="/images/site/comment/giscus_config.png" alt="giscus配置"></p></li><li><p>giscus 会自动从github获取对应的id<br><img src="/images/site/comment/giscus_config_show.png" alt="giscus配置结果.png"></p></li><li><p>拿到id后,我们就可以在<code>_config.yml</code>配置了</p></li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">post:</span><br> <span class="hljs-attr">comments:</span> <span class="hljs-comment"># 开启评论</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">type:</span> <span class="hljs-string">giscus</span> <span class="hljs-comment"># 选择评论插件类型</span><br><br><span class="hljs-comment"># 其他配置可以参考官网</span><br><span class="hljs-attr">giscus:</span><br> <span class="hljs-attr">repo:</span> <span class="hljs-string">Hanchers/Hanchers.github.io</span><br> <span class="hljs-attr">repo-id:</span> <span class="hljs-string">Mxxx</span><br> <span class="hljs-attr">category:</span> <span class="hljs-string">Q&A</span><br> <span class="hljs-attr">category-id:</span> <span class="hljs-string">Dxxxx</span><br> <span class="hljs-attr">theme-light:</span> <span class="hljs-string">light</span><br> <span class="hljs-attr">theme-dark:</span> <span class="hljs-string">dark</span><br> <span class="hljs-attr">mapping:</span> <span class="hljs-string">pathname</span><br> <span class="hljs-attr">reactions-enabled:</span> <span class="hljs-number">1</span><br> <span class="hljs-attr">emit-metadata:</span> <span class="hljs-number">0</span><br> <span class="hljs-attr">input-position:</span> <span class="hljs-string">top</span><br> <span class="hljs-attr">lang:</span> <span class="hljs-string">zh-CN</span><br></code></pre></td></tr></table></figure><p>效果:<br><img src="/../../images/site/comment/giscus_show.png" alt="giscus效果展示.png"></p><h2 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h2><p>Q:前面都配置好后,发现评论模块加载不出来,打开F12,发现请求接口报错了:<code>Discussion not found</code>.<br>A: 很多人都遇到过这个问题,解决办法就是去仓库下手动加一条评论,等一会就好了。个人理解这里是github延迟导致的。</p><h2 id="评价-2"><a href="#评价-2" class="headerlink" title="评价"></a>评价</h2><ol><li>整个安装过程挺丝滑的</li><li>数据完全托管到github,完全满足我的数据持久化和免费存储的需求</li><li>效果也不错,响应速度中等</li><li>还支持表情,挺惊喜的</li><li>唯一的问题就是发表评论需要github登录,无法匿名评论</li><li>基于github托管数据的插件里最推荐此款</li></ol><p><strong>至此,基于github,完全免费的评论插件都比较完了,接下来就是需要第三方网站托管数据或者自建服务托管数据的评论插件了。</strong></p><h1 id="livere"><a href="#livere" class="headerlink" title="livere"></a><a href="https://www.livere.com/">livere</a></h1><p>来必力,没想到是一个韩国的网站,用来做评论数据托管等内容,需要注册。</p><p>来必力各版本的功能比较(2025-01-09):<br><img src="/images/site/comment/livere_version_compare.png" alt="livere版本比较"></p><h2 id="安装过程-3"><a href="#安装过程-3" class="headerlink" title="安装过程"></a>安装过程</h2><p><a href="https://www.livere.com/">点击这里注册livere</a> </p><p>注册并登录成功后,在这个页面选择city模块,然后点击“安装使用”<br><img src="/images/site/comment/livere_city_install.png" alt="livere city 版本安装"></p><p>安装成功后,会得到一个脚本注入代码。<br><img src="/images/site/comment/livere_script.png" alt="livere安装代码"><br>将上面的data-uid复制出来放到<code>_config.yml</code>配置里即可。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">post:</span><br> <span class="hljs-attr">comments:</span> <span class="hljs-comment"># 开启评论</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">type:</span> <span class="hljs-string">gitalk</span> <span class="hljs-comment"># 选择评论插件类型</span><br><br><span class="hljs-comment"># 其他配置可以参考官网</span><br><span class="hljs-attr">livere:</span><br> <span class="hljs-attr">uid:</span> <span class="hljs-string">'your uid'</span><br></code></pre></td></tr></table></figure><p>效果:<br><img src="/images/site/comment/livere_show.png" alt="livere评论模块效果"><br>但是点击后,会弹出第三方账号登录窗口。<br><img src="/images/site/comment/livere_show_register.png" alt="提示注册"></p><h2 id="评价-3"><a href="#评价-3" class="headerlink" title="评价"></a>评价</h2><ol><li>免费,数据能够持久保存</li><li>韩国网站,注册的过程中都是韩文,不太友好</li><li>响应速度上,有点慢,但是比utterances快一点</li><li>页面有点丑</li><li>需要qq等账号注册才能使用,不太推荐</li></ol><h1 id="畅言"><a href="#畅言" class="headerlink" title="畅言"></a><a href="https://changyan.kuaizhan.com/">畅言</a></h1><p>畅言,一个国内比较老的评论插件,需要注册,可以免费存储数据。</p><h2 id="安装过程-4"><a href="#安装过程-4" class="headerlink" title="安装过程"></a>安装过程</h2><ol><li>点击官网,注册一个账号</li><li>去官网工作台,创建一个【畅言云评】的应用</li><li>进入应用详情,在后台总览里可以看到应用的id和密钥<br><img src="/../../images/site/comment/changyan_appid.png" alt="畅言云评id和密钥"></li></ol><p>将上面的id和密钥复制出来,放到<code>_config.yml</code>配置里即可。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">post:</span><br> <span class="hljs-attr">comments:</span> <span class="hljs-comment"># 开启评论</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">type:</span> <span class="hljs-string">changyan</span> <span class="hljs-comment"># 选择评论插件类型</span><br><br><span class="hljs-comment"># 其他配置可以参考官网</span><br><span class="hljs-attr">changyan:</span><br> <span class="hljs-attr">appid:</span> <span class="hljs-string">'id'</span><br> <span class="hljs-attr">appkey:</span> <span class="hljs-string">'secret'</span><br></code></pre></td></tr></table></figure><p>效果:<br><img src="/../../images/site/comment/changyan_show.png" alt="畅言云评效果"></p><h2 id="评价-4"><a href="#评价-4" class="headerlink" title="评价"></a>评价</h2><ol><li>畅言的网站比较老了,但是注册过程还是很简单的</li><li>从后台来看,对于评论的功能支持还是挺完善的,可以控评,也有很多其他的插件</li><li>可以免费存储数据</li><li>发布评论需要登录,门槛太高,pass.</li></ol><h1 id="twikoo插件"><a href="#twikoo插件" class="headerlink" title="twikoo插件"></a><a href="https://twikoo.js.org/quick-start.html">twikoo插件</a></h1><p>一个简洁、安全、免费的静态网站评论系统。</p><ul><li>免费搭建(使用云开发 / Vercel / 私有服务器作为评论后台)</li><li>简单部署(支持云开发 / Vercel 一键部署)</li></ul><p>这是一款私有化部署的评论插件,可以在自己的服务器上部署,官方也提供了一整套云部署的免费方案,可以自行选择。<br><img src="/../../images/site/comment/twioo_install_choose.png" alt="twikoo的N种云部署方式"></p><p>因为是私有化部署的方案,这就涉及到数据存储的问题,官方提供的方案是使用mongodb的免费存储服务,需要自行注册。 </p><h2 id="安装过程-5"><a href="#安装过程-5" class="headerlink" title="安装过程"></a>安装过程</h2><p>无论哪一种方案都设计到mongodb数据库的申请,所以第一步就是要申请mongodb免费版。</p><h3 id="mongodb申请"><a href="#mongodb申请" class="headerlink" title="mongodb申请"></a>mongodb申请</h3><p>参考<a href="https://twikoo.js.org/mongodb-atlas.html">官方文章</a>,写的很详细了。</p><h3 id="netlify安装"><a href="#netlify安装" class="headerlink" title="netlify安装"></a>netlify安装</h3><p>因为网速和流量的原因,我选择的是netlify做我的评论插件托管云服务。</p><ol><li>打开 <a href="https://github.com/twikoojs/twikoo-netlify">twikoojs/twikoo-netlify</a> 点击 fork 将仓库 fork 到自己的账号下,后面netlify部署的时候会用到这个仓库。</li><li><a href="https://app.netlify.com/">点击这里</a> 注册netlify账号,可能需要翻墙,使用github账号登录。</li><li>netlify注册的时候可以选择仅授权上面fork的【twikoo-netlify】库</li><li>进入netlify控制台,点击【import an existing site】,然后选择上面fork的【twikoo-netlify】库.<br><img src="/../../images/site/comment/netlify_home.png" alt="netlify新建site页面"></li><li>配置环境变量,配置你的域名和mongodb的<code>MONGODB_URI</code>,值就是mongodb 申请里获得的nodejs 连接url<br><img src="/../../images/site/comment/netlify_add_mongo_url.png" alt="配置mongodb环境变量"></li><li>点击deploy,等待部署完成。 然后请求你配置的域名<code>https://xxxxx.netlify.app/</code> ,如果看到如下内容,说明部署成功。</li></ol><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br><span class="hljs-attr">"code"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">100</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">"message"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Twikoo 云函数运行正常,请参考 https://twikoo.js.org/frontend.html 完成前端的配置"</span><span class="hljs-punctuation">,</span><br><span class="hljs-attr">"version"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1.6.41"</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><ol start="7"><li>配置完成后,就可以在<code>_config.yml</code>配置twikoo了。</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">post:</span><br> <span class="hljs-attr">comments:</span> <span class="hljs-comment"># 开启评论</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">type:</span> <span class="hljs-string">twikoo</span> <span class="hljs-comment"># 选择评论插件类型</span><br><br><span class="hljs-comment"># 其他配置可以参考官网</span><br><span class="hljs-attr">twikoo:</span><br> <span class="hljs-attr">envId:</span> <span class="hljs-string">https://xxxxxxxxxx.netlify.app/.netlify/functions/twikoo</span><br> <span class="hljs-attr">region:</span> <span class="hljs-string">ap-shanghai</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">window.location.pathname</span><br></code></pre></td></tr></table></figure><p>效果:<br><img src="/../../images/site/comment/twikoo_netlify_show.png" alt="twikoo效果"></p><h3 id="其他部署方式"><a href="#其他部署方式" class="headerlink" title="其他部署方式"></a>其他部署方式</h3><p><a href="https://blog.zhheo.com/p/2e6bbbd0.html">基于腾讯云的twikoo部署方案</a>, 注意因为腾讯云免费额度已经没有了,这个方案已经失效,仅供学习参考。</p><h2 id="后台配置"><a href="#后台配置" class="headerlink" title="后台配置"></a>后台配置</h2><p>twikoo内嵌了一套后台配置页面,可以配置评论的显示,邮件提醒、垃圾内容过滤等内容。</p><p>入口就是刷新旁边的配置按钮。<br><img src="/../../images/site/comment/twikoo_config_entry.png" alt="twikoo_config_entry.png"><br>第一次打开配置页面会要求你配置账号密码,这个一定要记好,不然找回会很麻烦。<br>配置好密码后,在后台就可以对评论做一些特殊配置了。</p><p>这里就对一些主要的配置进行介绍。</p><ul><li>SITE_NAME:你的网站名称</li><li>SITE_URL:你的网站地址</li><li>BLOGGER_NICK: 你的博客昵称</li><li>BLOGGER_EMAIL:博主邮箱,当评论邮箱匹配到这个邮箱,会显示为博主。 当有新增评论是,也会给这个邮箱发邮件。</li><li>MASTER_TAG:博主显示名称</li><li>HIDE_ADMIN_CRYPT:隐藏管理面板入口的暗号,只有在“昵称”一栏输入相同的“暗号”时,管理面板入口才会显示。这个暗号一定要牢记。</li><li>SHOW_IMAGE:是否支持评论图片</li><li>IMAGE_CDN:插入图片所使用的图床</li><li>SHOW_EMOTION:是否支持表情</li><li>SHOW_UA:是否显示用户系统和浏览器</li><li>SHOW_REGION:是否显示用户 IP 属地到省</li><li>SENDER_EMAIL:发送邮件的邮箱地址,为了不和日常的邮箱干扰,这里我又新申请了一个。<blockquote><p>说明一下,当有新评论时,会发邮件给博主。就是这个 SENDER_EMAIL 发送给 BLOGGER_EMAIL</p></blockquote></li><li>SENDER_NAME: 新评论时的主题</li><li>SMTP_SERVICE: 邮件发送的 SMTP 服务商</li><li>SMTP_USER: 和SENDER_EMAIL一样</li><li>SMTP_PASS: 邮件服务器的账号密码,可以到你的邮箱设置里查看,在开启你的smtp是会提示。<br>最后会有一个邮件测试,可以填上自己的邮箱去验证一下。</li></ul><p>这些配置在保存后,会在mongodb里创建一个表,用来存储这些配置。</p><h2 id="评价-5"><a href="#评价-5" class="headerlink" title="评价"></a>评价</h2><ol><li>因为是私有化部署,过程比较复杂</li><li>整个过程都是免费的资源,满足我的免费持久化需求</li><li>评论可以匿名,不用注册,比较友好</li><li>页面也比较美观,支持评论的评论,最终选择了这个。</li></ol><h1 id="最后pass掉的插件"><a href="#最后pass掉的插件" class="headerlink" title="最后pass掉的插件"></a>最后pass掉的插件</h1><ul><li>Valine: 基于 LeanCloud,这个有免费的存储空间。但是国内的LeanCloud 账号需要域名备案,要使用身份证等认证,接入很麻烦,遂放弃。</li><li>Waline: 从 Valine 衍生而来,放弃原因同上</li><li>Disqus: 国内用户直接使用容易被墙,直接放弃</li><li>remark42: 需要自建托管服务,直接放弃</li></ul><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>折腾了两天,终于把几款评论插件验证完了。</p><p>整体来看,有两款效果比较好,分别是twikoo和giscus。</p><ul><li>giscus: 安装比较简单,数据托管在github, 但评论需要github账号登录,适合那些快速上手的博主。</li><li>twikoo: 安装比较复杂,需要申请mongodb数据库,申请云服务免费资源,适合那些喜欢折腾的博主。</li></ul><h1 id="todo"><a href="#todo" class="headerlink" title="todo"></a>todo</h1><ul><li><input disabled="" type="checkbox"> 等我的域名备案成功了,再尝试一下valine和waline</li><li><input disabled="" type="checkbox"> 找个时间试一下twikoo的私有化部署方式</li></ul><p>最后,您觉得还有什么要补充的吗?</p>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>建站</tag>
<tag>评论插件</tag>
<tag>hexo</tag>
</tags>
</entry>
<entry>
<title>jekyll迁移到Hexo博客架构全过程</title>
<link href="/2025/01/07/site-hexo-from-jekyll/"/>
<url>/2025/01/07/site-hexo-from-jekyll/</url>
<content type="html"><![CDATA[<h1 id="为什么要迁移到hexo"><a href="#为什么要迁移到hexo" class="headerlink" title="为什么要迁移到hexo"></a>为什么要迁移到hexo</h1><h2 id="什么是Hexo"><a href="#什么是Hexo" class="headerlink" title="什么是Hexo"></a>什么是Hexo</h2><p>Hexo 是一个快速、简洁且高效的博客框架(使用NodeJs)。 Hexo 使用 Markdown(或其他标记语言)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。</p><h2 id="为什么选择了Hexo"><a href="#为什么选择了Hexo" class="headerlink" title="为什么选择了Hexo"></a>为什么选择了Hexo</h2><ol><li>好看,好看,好看</li><li>其中的归档功能做的很好,支持分组、也支持标签</li><li>很好的集成了网站统计相关的功能</li></ol><h1 id="迁移过程"><a href="#迁移过程" class="headerlink" title="迁移过程"></a>迁移过程</h1><h2 id="Hexo环境搭建"><a href="#Hexo环境搭建" class="headerlink" title="Hexo环境搭建"></a>Hexo环境搭建</h2><ol><li>首先确保安装了nodejs和git<br>因为 Hexo 是基于 nodejs 的,所以需要先安装 nodejs,这里我使用的是 nvm 管理 nodejs 版本,安装好 nvm 后,执行 nvm install nodejs 安装 nodejs。</li></ol><p>我安装的是2025年1月最新lts版本的nodejs: <code>v22.12.0</code></p><ol start="2"><li>安装Hexo</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm install -g hexo-cli<br></code></pre></td></tr></table></figure><p>因为我的nodejs是最新版本,所以我的hexo版本是也是当前最新版本:<code> 7.3.0</code></p><h2 id="创建Hexo博客网站"><a href="#创建Hexo博客网站" class="headerlink" title="创建Hexo博客网站"></a>创建Hexo博客网站</h2><p>选择一个你想创建项目代码的位置,执行以下命令创建Hexo博客网站:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 选择合适的nodejs 版本</span><br>nvm use v22.12.0<br><span class="hljs-comment"># 创建项目</span><br>hexo init my_blod<br><span class="hljs-built_in">cd</span> my_blod<br><span class="hljs-comment"># 初始化依赖</span><br>npm install<br></code></pre></td></tr></table></figure><p>然后我们就得到了一个默认的博客项目<code>my_blog</code>,其中的package.json文件如下:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"hexo-site"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"version"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"private"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"scripts"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"build"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"hexo generate"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"clean"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"hexo clean"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"deploy"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"hexo deploy"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"server"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"hexo server"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"version"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"7.3.0"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"dependencies"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"hexo"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^7.3.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-generator-archive"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^2.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-generator-category"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^2.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-generator-index"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^4.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-generator-tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^2.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-renderer-ejs"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^2.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-renderer-marked"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^7.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-renderer-stylus"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^3.0.1"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-server"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^3.0.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-theme-fluid"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^1.9.8"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hexo-theme-landscape"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"^1.0.0"</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>到了这一步,我们就可以启动最原始版本的项目了:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cd</span> my_blod<br>hexo server<br><span class="hljs-comment"># or </span><br>npm run server<br></code></pre></td></tr></table></figure><p>然后访问 <code>http://localhost:4000</code> 就可以看到默认的博客了。<br><img src="/images/site/hexo_raw_init.png" alt="hexo默认主题页面"><br>官方给我们创建了一个<code>hello world</code>的博客. 我们可以参考这个来写我们自己的博客了。</p><p>常用的hexo命令参考<a href="https://hexo.io/zh-cn/docs/commands">这里</a></p><h2 id="安装我喜欢的主题Fluid"><a href="#安装我喜欢的主题Fluid" class="headerlink" title="安装我喜欢的主题Fluid"></a>安装我喜欢的主题<a href="https://hexo.fluid-dev.com/docs/start/#%E4%B8%BB%E9%A2%98%E7%AE%80%E4%BB%8B">Fluid</a></h2><p>当然这里可以使用任何你喜欢的主题,安装模式大同小异。</p><ol><li>安装Fluid主题的最新版本</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cd</span> my_blod<br>npm install --save hexo-theme-fluid<br></code></pre></td></tr></table></figure><ol start="2"><li>配置主题<br> 如下修改 Hexo 博客目录中的 _config.yml:</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">theme:</span> <span class="hljs-string">fluid</span> <span class="hljs-comment"># 指定主题</span><br><br><span class="hljs-attr">language:</span> <span class="hljs-string">zh-CN</span> <span class="hljs-comment"># 指定语言,会影响主题显示的语言,按需修改</span><br></code></pre></td></tr></table></figure><ol start="3"><li>创建【关于页】<br> 首次使用主题的「关于页」需要手动创建:</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">hexo new page about<br></code></pre></td></tr></table></figure><p>创建成功后修改 /source/about/index.md,添加 <strong>layout</strong> 属性,。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>title: 标题<br><span class="hljs-section">layout: about</span><br><span class="hljs-section">---</span><br><br>这里写关于页的正文,支持 Markdown, HTML<br></code></pre></td></tr></table></figure><blockquote><p>layout: about 必须存在,并且不能修改成其他值,否则不会显示头像等样式。</p></blockquote><p><img src="/images/site/hexo_fluid_init.png" alt="fluid主题启动后的博客页面"></p><h1 id="迁移jekyll博客文章"><a href="#迁移jekyll博客文章" class="headerlink" title="迁移jekyll博客文章"></a>迁移jekyll博客文章</h1><h2 id="文章迁移"><a href="#文章迁移" class="headerlink" title="文章迁移"></a>文章迁移</h2><ol><li>把 _posts 文件夹内的所有文件复制到 source/_posts 文件夹</li><li>在 _config.yml 中修改 new_post_name 参数</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">new_post_name:</span> <span class="hljs-string">:year-:month-:day-:title.md</span><br></code></pre></td></tr></table></figure><ol start="3"><li>如果不想使用步骤2,也可以把所有的文章的文件名的年月日前缀去掉,也可以(作为轻度洁癖患者,我选的这种方式)。</li></ol><h2 id="图片迁移"><a href="#图片迁移" class="headerlink" title="图片迁移"></a>图片迁移</h2><p>原先我的所有的图片资源都是在项目的根目录下的 <code>/images</code> 路径下,然后在文章里通过markdown的图片引用方式引用。</p><p>迁移到hexo后,只需将<code>images</code>目录 移动到 <code>source</code> 路径下,其他的都不用动,图片就可以自动解析了。</p><p>因为hexo 会将source目录下的所有文件都移动到网站的根路径下,所以图片的最终引用地址是一样的。</p><h2 id="网站配置修改"><a href="#网站配置修改" class="headerlink" title="网站配置修改"></a>网站配置修改</h2><p>通用hexo配置按照如下配置修改 <code>_config.yml</code> 文件:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># 网站名称</span><br><span class="hljs-attr">title:</span> <span class="hljs-string">寒澈笔记</span><br><span class="hljs-attr">subtitle:</span> <span class="hljs-string">'一个程序员眼中的世界'</span><br><span class="hljs-comment"># 网站路径</span><br><span class="hljs-attr">url:</span> <span class="hljs-string">https://www.hancher.top</span><br></code></pre></td></tr></table></figure><p>fluid主题配置特性配置修改如下文件<code>_config.fluid.yml</code>, 会根据主题配置自动选择相应主题的配置。<br>而且这里的配置优先级最高,会覆盖<code>_config.yml</code>配置。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># fluid 主题配置</span><br><br><span class="hljs-comment"># 网站配置</span><br><span class="hljs-comment">## 头部</span><br><span class="hljs-attr">navbar:</span><br> <span class="hljs-comment"># 博客标题</span><br> <span class="hljs-attr">blog_title:</span> <span class="hljs-string">寒澈笔记</span><br> <span class="hljs-comment"># 可以自定义导航栏</span><br><span class="hljs-comment"># menu:</span><br><span class="hljs-comment"># - { key: 'home', link: '/', icon: 'iconfont icon-home-fill' }</span><br><span class="hljs-comment"># - { key: 'tag', link: '/tags/', icon: 'iconfont icon-tags-fill' }</span><br><span class="hljs-comment"># - { key: 'about', link: '/about/', icon: 'iconfont icon-user-fill', name: '联系我' }</span><br><br><span class="hljs-comment">## 底部</span><br><span class="hljs-attr">footer:</span><br> <span class="hljs-attr">statistics:</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">source:</span> <span class="hljs-string">"busuanzi"</span> <span class="hljs-comment"># 可选 leancloud | busuanzi 根据自己需求选择</span><br> <span class="hljs-attr">pv_format:</span> <span class="hljs-string">"总访问量 {} 次"</span> <span class="hljs-comment"># 显示的文本,{}是数字的占位符(必须包含),下同</span><br> <span class="hljs-attr">uv_format:</span> <span class="hljs-string">"总访客数 {} 人"</span><br><br><span class="hljs-attr">force_https:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># 强制https</span><br><br><span class="hljs-comment"># 内容配置</span><br><span class="hljs-comment">## 首页</span><br><span class="hljs-attr">index:</span><br> <span class="hljs-attr">slogan:</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">text:</span> <span class="hljs-string">人生总有一些东西值得回忆</span><br><br><span class="hljs-attr">about:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">"寒澈"</span><br> <span class="hljs-attr">intro:</span> <span class="hljs-string">"一名喜欢文史哲的程序员"</span><br> <span class="hljs-attr">icons:</span> <span class="hljs-comment"># 不要把 icon 注释掉,否则无法覆盖配置</span><br> <span class="hljs-bullet">-</span> { <span class="hljs-attr">class:</span> <span class="hljs-string">'iconfont icon-github-fill'</span>, <span class="hljs-attr">link:</span> <span class="hljs-string">'https://github.com/Hanchers/hanchers.github.io'</span> }<br> <span class="hljs-bullet">-</span> { <span class="hljs-attr">class:</span> <span class="hljs-string">'iconfont icon-zhihu-fill'</span>, <span class="hljs-attr">link:</span> <span class="hljs-string">'https://www.zhihu.com/people/bh.zhi'</span> }<br> <span class="hljs-bullet">-</span> { <span class="hljs-attr">class:</span> <span class="hljs-string">'iconfont icon-wechat2-fill'</span>, <span class="hljs-attr">qrcode:</span> <span class="hljs-string">'/images/about/see-history.jpg'</span> }<br><br><span class="hljs-comment">## 文章</span><br><span class="hljs-attr">post:</span><br> <span class="hljs-attr">meta:</span><br> <span class="hljs-attr">author:</span> <span class="hljs-comment"># 作者,优先根据 front-matter 里 author 字段,其次是 hexo 配置中 author 值</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">false</span><br> <span class="hljs-attr">date:</span> <span class="hljs-comment"># 文章日期,优先根据 front-matter 里 date 字段,其次是 md 文件日期</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">false</span><br> <span class="hljs-attr">format:</span> <span class="hljs-string">"dddd, MMMM Do YYYY, h:mm a"</span> <span class="hljs-comment"># 格式参照 ISO-8601 日期格式化</span><br> <span class="hljs-attr">wordcount:</span> <span class="hljs-comment"># 字数统计</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">format:</span> <span class="hljs-string">"{} 字"</span> <span class="hljs-comment"># 显示的文本,{}是数字的占位符(必须包含),下同</span><br> <span class="hljs-attr">min2read:</span> <span class="hljs-comment"># 阅读时间</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">format:</span> <span class="hljs-string">"{} 分钟"</span><br> <span class="hljs-attr">views:</span> <span class="hljs-comment"># 阅读次数</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">source:</span> <span class="hljs-string">"busuanzi"</span> <span class="hljs-comment"># 统计数据来源,可选:leancloud | busuanzi 注意不蒜子会间歇抽风</span><br> <span class="hljs-attr">format:</span> <span class="hljs-string">"{} 次"</span><br> <span class="hljs-attr">copyright:</span> <span class="hljs-comment"># 版权声明</span><br> <span class="hljs-attr">license:</span> <span class="hljs-string">'BY-NC-ND'</span><br> <span class="hljs-attr">update_date:</span> <span class="hljs-comment"># 来源 front-matter 里 updated 字段</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br><br></code></pre></td></tr></table></figure><h1 id="github-pages-配置修改"><a href="#github-pages-配置修改" class="headerlink" title="github pages 配置修改"></a>github pages 配置修改</h1><p>我是用的私有项目,然后通过github action的方式来自动编译部署github pages的, 具体可以参考我的历史文章,有记录。</p><p>这里要做的是在github 上重新配置hexo环境和 打包脚本。</p><p>进入本地项目的 <code>.github/workflows</code>目录下,删除以前的workflow文件,然后新建一个文件,文件名随意,内容如下:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># ci name</span><br><span class="hljs-comment"># ci 脚本名称</span><br><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">Hexo</span> <span class="hljs-string">site</span> <span class="hljs-string">to</span> <span class="hljs-string">public</span> <span class="hljs-string">github</span> <span class="hljs-string">repos</span><br><br><span class="hljs-attr">on:</span><br> <span class="hljs-comment"># Runs on pushes targeting the default branch</span><br> <span class="hljs-comment"># 当代码 push 到 main 分支时, 执行该脚本</span><br> <span class="hljs-attr">push:</span><br> <span class="hljs-attr">branches:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">main</span><br><br> <span class="hljs-comment"># Allows you to run this workflow manually from the Actions tab</span><br> <span class="hljs-comment"># 允许你 手动执行 这个脚本</span><br> <span class="hljs-attr">workflow_dispatch:</span><br><br><span class="hljs-comment"># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</span><br><span class="hljs-comment"># GITHUB_TOKEN 的操作权限</span><br><span class="hljs-attr">permissions:</span><br> <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span><br> <span class="hljs-attr">pages:</span> <span class="hljs-string">write</span><br> <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span><br><br><span class="hljs-comment"># Allow one concurrent deployment</span><br><span class="hljs-attr">concurrency:</span><br> <span class="hljs-attr">group:</span> <span class="hljs-string">"pages"</span><br> <span class="hljs-attr">cancel-in-progress:</span> <span class="hljs-literal">true</span><br><br><span class="hljs-comment"># job 执行任务, 可以有多个, 默认并行运行, 可以通过 needs 关键字来设置依赖的其他 jobs</span><br><span class="hljs-comment"># step : 任务下的执行步骤, 一个job 会有多个步骤</span><br><span class="hljs-comment"># 操作: 一个step 下可以执行多个操作, 通常一行脚本为一个操作</span><br><span class="hljs-attr">jobs:</span><br> <span class="hljs-comment"># Build job</span><br> <span class="hljs-attr">build:</span><br> <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br> <span class="hljs-attr">steps:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout版本</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span><br> <span class="hljs-comment"># 准备ruby 环境</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">使用Node:</span> <span class="hljs-string">v22.12.0</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-comment"># Examples: 20, 18.19, >=16.20.2, lts/Iron, lts/Hydrogen, *, latest, current, node</span><br> <span class="hljs-comment"># Ref: https://github.com/actions/setup-node#supported-version-syntax</span><br> <span class="hljs-attr">node-version:</span> <span class="hljs-string">"22.12"</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">缓存</span> <span class="hljs-string">NPM</span> <span class="hljs-string">依赖</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v4</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">node_modules</span><br> <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.OS</span> <span class="hljs-string">}}-npm-cache</span><br> <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span><br><span class="hljs-string"> ${{ runner.OS }}-npm-cache</span><br><span class="hljs-string"></span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">安装npm依赖</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">构建博客内容</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">更新博客网站到public目录</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-pages-artifact@v3</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">./public</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">部署_site到博客网站仓库</span><br> <span class="hljs-attr">working-directory:</span> <span class="hljs-string">./public</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string"> git init</span><br><span class="hljs-string"> git checkout -b main</span><br><span class="hljs-string"> git add -A</span><br><span class="hljs-string"> git -c user.name='hancher' -c user.email='[email protected]' commit -m 'update blog' </span><br><span class="hljs-string"> git push "https://${{github.actor}}:${{secrets.PUB_BLOG}}@github.com/Hanchers/hanchers.github.io" HEAD:main -f -q</span><br><span class="hljs-string"></span><br></code></pre></td></tr></table></figure><p>如果不是私有项目部署的话,使用官方的配置即可。官方配置如下:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Hexo</span> <span class="hljs-string">博客部署</span><br><br><span class="hljs-attr">on:</span><br> <span class="hljs-attr">push:</span><br> <span class="hljs-attr">branches:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">main</span> <span class="hljs-comment"># default branch</span><br><br><span class="hljs-attr">jobs:</span><br> <span class="hljs-attr">build:</span><br> <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br> <span class="hljs-attr">steps:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span><br> <span class="hljs-comment"># If your repository depends on submodule, please see: https://github.com/actions/checkout</span><br> <span class="hljs-attr">submodules:</span> <span class="hljs-string">recursive</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-number">20</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-comment"># Examples: 20, 18.19, >=16.20.2, lts/Iron, lts/Hydrogen, *, latest, current, node</span><br> <span class="hljs-comment"># Ref: https://github.com/actions/setup-node#supported-version-syntax</span><br> <span class="hljs-attr">node-version:</span> <span class="hljs-string">"20"</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">NPM</span> <span class="hljs-string">dependencies</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v4</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">node_modules</span><br> <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.OS</span> <span class="hljs-string">}}-npm-cache</span><br> <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span><br><span class="hljs-string"> ${{ runner.OS }}-npm-cache</span><br><span class="hljs-string"></span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Dependencies</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">Pages</span> <span class="hljs-string">artifact</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-pages-artifact@v3</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">./public</span><br> <span class="hljs-attr">deploy:</span><br> <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span><br> <span class="hljs-attr">permissions:</span><br> <span class="hljs-attr">pages:</span> <span class="hljs-string">write</span><br> <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span><br> <span class="hljs-attr">environment:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">github-pages</span><br> <span class="hljs-attr">url:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.deployment.outputs.page_url</span> <span class="hljs-string">}}</span><br> <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br> <span class="hljs-attr">steps:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span><br> <span class="hljs-attr">id:</span> <span class="hljs-string">deployment</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/deploy-pages@v4</span><br></code></pre></td></tr></table></figure><p>然后就可以到 hanchers.github.io 仓库下查看博客内容是否更新成功了。</p><p>如果博客内容更新了,就可以安装你的博客地址去访问博客了。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://hexo.io/zh-cn/docs/">hexo 建站官方文档</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://hexo.fluid-dev.com/docs/start">fluid主题官方文档</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="https://hexo.io/zh-cn/docs/migration">jekyll博客迁移到hexo</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>建站</tag>
<tag>hexo</tag>
</tags>
</entry>
<entry>
<title>2025年博客更新计划</title>
<link href="/2025/01/03/site-plan-in-2025/"/>
<url>/2025/01/03/site-plan-in-2025/</url>
<content type="html"><![CDATA[<h1 id="2025年博客更新计划"><a href="#2025年博客更新计划" class="headerlink" title="2025年博客更新计划"></a>2025年博客更新计划</h1><p>2024年已经过去,2025年已经到来。</p><p>在过去的一年了,因为经历了很多重大的事情,比如换工作、比如结婚、再比如公众号等原因,导致这个博客更新的频率比较慢。</p><p>现在,2025年了,新的开始,我也打算重新恢复博客的更新计划,并制定一些规划,既是我这一年努力的方向,也方便在2025年末的时候回来复盘。</p><h1 id="更新计划"><a href="#更新计划" class="headerlink" title="更新计划"></a>更新计划</h1><h2 id="网站部分"><a href="#网站部分" class="headerlink" title="网站部分"></a>网站部分</h2><ul><li><input checked="" disabled="" type="checkbox"> 现在的主题感觉有点丑,而且在文档归档方面有点不足,看到一个新的<a href="https://github.com/fluid-dev/hexo-theme-fluid?tab=readme-ov-file">主题</a>很喜欢,计划改造一下,可能会伤筋动骨</li><li><input disabled="" type="checkbox"> 彻底解决国内访问本网站的问题,目前发现的<a href="https://github.com/xingpingcn/enhanced-FaaS-in-China">参考文章</a>,有空研究一下</li><li><input checked="" disabled="" type="checkbox"> 研究一下,如何让博客拥有<strong>更新时间</strong>的功能,记录我对博客内容的修改</li><li><input disabled="" type="checkbox"> 为了加快网站的访问速度,考虑引入图床功能</li><li><input disabled="" type="checkbox"> 将上述改造整理成文档,并分享到博客</li></ul><h2 id="内容部分"><a href="#内容部分" class="headerlink" title="内容部分"></a>内容部分</h2><ul><li><input disabled="" type="checkbox"> 博客的定位从纯技术博客,变更为一个技术+生活类型模块,增加一些非技术类的内容</li><li><input disabled="" type="checkbox"> 我的部分改造升级,打通我的所有的内容生态体系,提升我的个人品牌</li><li><input disabled="" type="checkbox"> 计划开发一套个人的小程序,并记录小程序上线的全过程</li><li><input disabled="" type="checkbox"> 增加一些收集的资源分享功能,开源分享的精神总要有人来继承</li><li><input disabled="" type="checkbox"> 技术方面,除了日常问题的总结外,继续向上游理论部分探索</li></ul><h1 id="计划完成"><a href="#计划完成" class="headerlink" title="计划完成"></a>计划完成</h1><ul><li>2025年01月11日 完成了博客从jekyll 到 hexo 的改造,同时支持了更新时间的显示</li></ul><h1 id="生命不息,折腾不止"><a href="#生命不息,折腾不止" class="headerlink" title="生命不息,折腾不止"></a>生命不息,折腾不止</h1><p>发现我就是一个喜欢折腾的人</p>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>建站</tag>
<tag>更新计划</tag>
</tags>
</entry>
<entry>
<title>西部数据提供免费版Paragon实现与mac电脑的读写</title>
<link href="/2024/12/30/life-ntfs-for-mac-free/"/>
<url>/2024/12/30/life-ntfs-for-mac-free/</url>
<content type="html"><![CDATA[<h1 id="现象"><a href="#现象" class="headerlink" title="现象"></a>现象</h1><p>俗套的现象:买了一个西部数据的移动硬盘,然后又买了一个mac电脑,将硬盘插入到电脑后发现硬盘里的数据可以读,但是无法将电脑里的文件写入。</p><p>其实本质上的原因就是 wb的硬盘是ntfs格式的, mac电脑不支持ntfs格式的读写.</p><p>本人电脑:macbook pro m1版 </p><p>系统版本:macos 14</p><h1 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h1><p>如果为了保持和windows系统的兼容性,wb的硬盘必须使用ntfs格式,那么通过安装ntfs转换软件也能实现。mac应用商店里有很多这种软件,但是要收费。</p><p>应用商店之外常用的软件就是 <code>uxera</code>和<code>Paragon</code>,也要收费。</p><p>后来找了好久,在西部数据的官方帖子上找到一个免费实现的法子,原来西部数据官方也提供了一套mac版读写数据的驱动。</p><p>以下是官方帖子的链接:<br><a href="https://support-eu.wd.com/app/answers/detailweb/a_id/34871/~/external-drive%3A-paragon-ntfs-driver-for-mac">Steps to Install Paragon NTFS Driver for macOS for WD and SanDisk Professional</a></p><h2 id="大概安装步骤如下:"><a href="#大概安装步骤如下:" class="headerlink" title="大概安装步骤如下:"></a>大概安装步骤如下:</h2><ol><li><p>下载驱动<br><a href="https://downloads.wdc.com/wdapp/Paragon_NTFS_Driver_for_Mac.zip">Paragon_NTFS_Driver_for_Mac</a><br>解压后双击软件开始安装。</p></li><li><p>选择:安装ntfs for mac<br><img src="/images/others/ntfs_for_mac_1.png" alt="s1"><br>然后接着按照步骤一直下一步就可以了<br><img src="/images/others/ntfs_for_mac_2.png" alt="s2"><br><img src="/images/others/ntfs_for_mac_3.png" alt="s3"><br><img src="/images/others/ntfs_for_mac_4.png" alt="s4"><br><img src="/images/others/ntfs_for_mac_5.png" alt="s5"><br><img src="/images/others/ntfs_for_mac_6.png" alt="s6"></p></li><li><p>在最后一步,会要求在系统配置里代开可信任权限,关机重启。<br>需要注意的是,这里不仅仅是要求在mac的系统安全设置里打开第三方软件信任项,还要在系统的安全模型里开启对第三方软件的信任项。</p></li></ol><p>如果以前没有修改过安全模型下的配置,大概率会提示需要在“恢复”环境中修改安全性设置。</p><p>这个需要在电脑关机后,长按触控ID或电源按钮就能进入安全配置环境,选择设置配置,然后在上面的菜单选择里找到安全配置相关菜单,勾选信任第三方软件修改系统内核就可以了(因为是安全模式,没有截图,按照这个步骤找就可以了)。</p><p>如果对电脑安全有洁癖的用户,这一步操作要慎重,也可以转投付费阵营。</p><h1 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h1><p>因为网络的原因,上面的驱动可能是下载失败,这里提供一个备用地址:<br><a href="https://url41.ctfile.com/f/62647441-1442742761-6dcabd?p=45td">Paragon_NTFS_Driver_for_Mac.zip 17.0.246</a> (访问密码: 45td)</p><p><a href="https://support-en.wd.com/app/answers/detailweb/a_id/29957">驱动版本列表</a></p>]]></content>
<categories>
<category>生活</category>
</categories>
<tags>
<tag>生活</tag>
</tags>
</entry>
<entry>
<title>linux安装ClamAV杀毒软件然后扫毒</title>
<link href="/2024/12/25/safe-clamav-and-scan/"/>
<url>/2024/12/25/safe-clamav-and-scan/</url>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>最近在一个项目上要求在linux服务器上安装杀毒软件,然后开启系统扫描,然后删除病毒。</p><p>然后找了找了一些资料,比较常用的有<strong>ClamAV</strong> 和 <strong>Comodo</strong>, 因为ClamAV开源,且使用比较广泛,最终选择了ClamAV。</p><p>我们的服务器是centos7.9, 下面就是在CentOS上安装ClamAV的过程,比较简单。</p><h2 id="CentOS-上安装ClamAV"><a href="#CentOS-上安装ClamAV" class="headerlink" title="CentOS 上安装ClamAV"></a>CentOS 上安装ClamAV</h2><p>以下是在 CentOS 上安装 <strong>ClamAV</strong> 步骤:</p><h3 id="步骤-1:安装-EPEL-仓库"><a href="#步骤-1:安装-EPEL-仓库" class="headerlink" title="步骤 1:安装 EPEL 仓库"></a><strong>步骤 1:安装 EPEL 仓库</strong></h3><p>ClamAV 通常通过 <strong>EPEL</strong>(Extra Packages for Enterprise Linux)仓库提供,因此首先需要确保 EPEL 仓库已安装。</p><p>如果失败可以考虑使用阿里云的源 <a href="https://www.cnblogs.com/qingqingyuntian/articles/18414842">yum 仓库更换阿里云</a></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> yum install epel-release -y<br></code></pre></td></tr></table></figure><h3 id="步骤-2:安装-ClamAV"><a href="#步骤-2:安装-ClamAV" class="headerlink" title="步骤 2:安装 ClamAV"></a><strong>步骤 2:安装 ClamAV</strong></h3><p>安装 EPEL 仓库后,你可以通过以下命令安装 ClamAV:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> yum install clamav clamav-update clamd -y<br></code></pre></td></tr></table></figure><ul><li><code>clamav</code> 包含命令行工具(如 <code>clamscan</code>)。</li><li><code>clamav-update</code> 是更新病毒库所需的工具(即 <code>freshclam</code>)。</li><li><code>clamd</code> 是clam的系统服务。</li></ul><p>验证安装是否成功:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">clamscan --version<br></code></pre></td></tr></table></figure><h3 id="步骤-3:更新病毒库"><a href="#步骤-3:更新病毒库" class="headerlink" title="步骤 3:更新病毒库"></a><strong>步骤 3:更新病毒库</strong></h3><p>安装完成后,首先需要更新 ClamAV 的病毒库,以确保它能识别最新的恶意软件。运行以下命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> freshclam<br></code></pre></td></tr></table></figure><p><code>freshclam</code> 会从 ClamAV 的服务器下载并更新病毒库,如果超时,等一会再多试几次,我试了3次成功了。</p><figure class="highlight subunit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs subunit">ClamAV update process started at Wed Dec 25 16:33:20 2024<br>WARNING: Your ClamAV installation is OUTDATED!<br>WARNING: Local version: 0.103.11 Recommended version: 0.103.12<br>DON'T PANIC! Read https://docs.clamav.net/manual/Installing.html<br>daily database available for download (remote version: 27497)<br><span class="hljs-keyword">Time:</span> 14.0s, ETA: 0.0s [========================>] 61.48MiB/61.48MiB<br><span class="hljs-keyword">Testing </span>database: '/var/lib/clamav/tmp.8095e9d84b/clamav<span class="hljs-string">-0</span>e8a55acea5c8848d6871677901e577d.tmp-daily.cvd' ...<br>Database test passed.<br>daily.cvd updated (version: 27497, sigs: 2071426, f-level: 90, builder: raynman)<br>main database available for download (remote version: 62)<br><span class="hljs-keyword">Time:</span> 50.6s, ETA: 0.0s [========================>] 162.58MiB/162.58MiBBB<br><span class="hljs-keyword">Testing </span>database: '/var/lib/clamav/tmp.8095e9d84b/clamav-ac7f382ef620ff4ad4d10aaed27f9dce.tmp-main.cvd' ...<br>Database test passed.<br>main.cvd updated (version: 62, sigs: 6647427, f-level: 90, builder: sigmgr)<br>bytecode database available for download (remote version: 335)<br><span class="hljs-keyword">Time:</span> 1.5s, ETA: 0.0s [========================>] 282.94KiB/282.94KiB<br><span class="hljs-keyword">Testing </span>database: '/var/lib/clamav/tmp.8095e9d84b/clamav-bbc727f2e5a1bc6a0e835c65fb9cf566.tmp-bytecode.cvd' ...<br>Database test passed.<br>bytecode.cvd updated (version: 335, sigs: 86, f-level: 90, builder: raynman)<br></code></pre></td></tr></table></figure><h3 id="步骤-4:启动-ClamAV-守护进程(可选)"><a href="#步骤-4:启动-ClamAV-守护进程(可选)" class="headerlink" title="步骤 4:启动 ClamAV 守护进程(可选)"></a><strong>步骤 4:启动 ClamAV 守护进程(可选)</strong></h3><p>如果你希望使用 ClamAV 的守护进程(<code>clamd</code>),你需要启动它。守护进程会提高扫描效率,特别是对于较大的文件。</p><ol><li>启用clam默认配置</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cp</span> /etc/clamd.d/scan.conf /etc/clamd.d/scan.conf.bak<br>sed -i -e <span class="hljs-string">"s/^Example/#Example/"</span> /etc/clamd.d/scan.conf<br>vim /etc/clamd.d/scan.conf<br></code></pre></td></tr></table></figure><p>放开如下的注释</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># LocalSocket /run/clamd.scan/clamd.sock</span><br></code></pre></td></tr></table></figure><ol start="2"><li>重命名clam服务的@文件</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">mv</span> /usr/lib/systemd/system/clamd\@.service /usr/lib/systemd/system/clamd.service<br></code></pre></td></tr></table></figure><ol start="3"><li>修改服务配置</li></ol><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[Unit]</span><br><span class="hljs-attr">Description</span> = clamd scanner daemon<br><span class="hljs-attr">After</span> = syslog.target nss-lookup.target network.target<br><span class="hljs-section">[Service]</span><br><span class="hljs-attr">Type</span> = forking<br><span class="hljs-attr">ExecStart</span> = /usr/sbin/clamd -c /etc/clamd.d/scan.conf<br><span class="hljs-comment"># Reload the database</span><br><span class="hljs-attr">ExecReload</span>=/bin/kill -USR2 <span class="hljs-variable">$MAINPID</span><br><span class="hljs-attr">Restart</span> = <span class="hljs-literal">on</span>-failure<br><span class="hljs-attr">TimeoutStartSec</span>=<span class="hljs-number">420</span><br><span class="hljs-section">[Install]</span><br><span class="hljs-attr">WantedBy</span> = multi-user.target<br></code></pre></td></tr></table></figure><ol start="4"><li><p>启动 <code>clamd</code> 服务:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> systemctl daemon-reload<br><span class="hljs-built_in">sudo</span> systemctl start clamd.service<br></code></pre></td></tr></table></figure></li><li><p>设置 <code>clamd</code> 在系统启动时自动启动:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> systemctl <span class="hljs-built_in">enable</span> clamd.service<br></code></pre></td></tr></table></figure></li></ol><h3 id="步骤-5:运行-ClamAV-执行病毒扫描"><a href="#步骤-5:运行-ClamAV-执行病毒扫描" class="headerlink" title="步骤 5:运行 ClamAV 执行病毒扫描"></a><strong>步骤 5:运行 ClamAV 执行病毒扫描</strong></h3><p>你可以使用 <code>clamscan</code> 执行病毒扫描。以下是一些常见的用法:</p><ul><li><p><strong>扫描单个文件</strong>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">clamscan /path/to/file<br></code></pre></td></tr></table></figure></li><li><p><strong>扫描整个目录</strong>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">clamscan -r /path/to/directory<br></code></pre></td></tr></table></figure></li><li><p><strong>扫描并删除感染文件</strong>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">clamscan -r --remove /path/to/directory<br></code></pre></td></tr></table></figure></li><li><p><strong>扫描并显示详细信息</strong>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">clamscan -r -v /path/to/directory<br></code></pre></td></tr></table></figure></li><li><p><strong>将结果输出到文件</strong>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">clamscan -r /path/to/directory --<span class="hljs-built_in">log</span>=clamav_scan_results.txt<br><span class="hljs-comment"># or</span><br>clamscan -r /path/to/directory > clamav_scan_results.txt<br><span class="hljs-comment"># 后台运行</span><br><span class="hljs-built_in">nohup</span> clamscan -r /path/to/directory --<span class="hljs-built_in">log</span>=clamav_scan_results.txt &<br></code></pre></td></tr></table></figure><p>结果:</p></li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs text">----------- SCAN SUMMARY -----------<br>Known viruses: 8702931<br>Engine version: 0.103.11<br>Scanned directories: 44573<br>Scanned files: 66053<br>Infected files: 0<br>Total errors: 21600<br>Data scanned: 4565.67 MB<br>Data read: 27114.51 MB (ratio 0.17:1)<br>Time: 1178.142 sec (19 m 38 s)<br>Start Date: 2024:12:25 17:12:38<br>End Date: 2024:12:25 17:32:17<br></code></pre></td></tr></table></figure><h3 id="步骤-6:配置-ClamAV-可选"><a href="#步骤-6:配置-ClamAV-可选" class="headerlink" title="步骤 6:配置 ClamAV (可选)"></a><strong>步骤 6:配置 ClamAV (可选)</strong></h3><p>如果需要,你可以配置 ClamAV 来调整其行为。ClamAV 的配置文件位于:</p><ul><li><strong><code>clamd</code> 配置文件</strong>:<code>/etc/clamd.d/scan.conf</code></li><li><strong><code>freshclam</code> 配置文件</strong>:<code>/etc/freshclam.conf</code></li></ul><p>你可以编辑这些配置文件,调整日志文件路径、扫描选项等。例如,编辑 <code>clamd</code> 配置文件来启用详细日志:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> vi /etc/clamd.d/scan.conf<br></code></pre></td></tr></table></figure><p>修改以下内容:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">LogFile /var/log/clamd.log<br>LogTime <span class="hljs-built_in">yes</span><br></code></pre></td></tr></table></figure><h3 id="步骤-7:定期更新病毒库"><a href="#步骤-7:定期更新病毒库" class="headerlink" title="步骤 7:定期更新病毒库"></a><strong>步骤 7:定期更新病毒库</strong></h3><p>为了确保 ClamAV 能及时识别最新的威胁,你需要定期更新病毒库。你可以通过设置 <strong>cron 任务</strong> 来自动更新病毒库。</p><ol><li><p>编辑 <code>crontab</code> 文件:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> crontab -e<br></code></pre></td></tr></table></figure></li><li><p>添加以下行来每天更新病毒库(此例为每天凌晨 2 点):</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">0 2 * * * /usr/bin/freshclam --quiet<br></code></pre></td></tr></table></figure></li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h2><ol><li>安装 EPEL 仓库并通过 <code>yum</code> 安装 ClamAV。</li><li>使用 <code>freshclam</code> 更新病毒库。</li><li>启动并配置 <code>clamd</code> 守护进程(可选)。</li><li>使用 <code>clamscan</code> 执行病毒扫描。</li><li>配置定时任务自动更新病毒库。</li></ol><p>通过这些步骤,你可以在 CentOS 上成功安装和配置 ClamAV,帮助你扫描和清理系统中的病毒和恶意软件。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://docs.clamav.net/manual/Usage/Scanning.html">clamav 官方文档</a></p><p><a href="https://www.cnblogs.com/qingqingyuntian/articles/18414842">yum 仓库更换阿里云</a></p><p><a href="https://gist.github.com/fernandoaleman/50b134b987297f97c803c91b591e5c52">centos7 配置ClamAV</a></p>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>linux</tag>
<tag>安全</tag>
</tags>
</entry>
<entry>
<title>mysql出现了waiting for global read lock 全局事务等待问题排查</title>
<link href="/2024/08/01/db-mysql-waiting-for-global-read-lock/"/>
<url>/2024/08/01/db-mysql-waiting-for-global-read-lock/</url>
<content type="html"><![CDATA[<h1 id="问题发现"><a href="#问题发现" class="headerlink" title="问题发现"></a>问题发现</h1><p>在一个普通的日子里,寒澈在操作他负责的一个内部系统的时候,突然发现一个修改状态的操作不能操作了。 查看服务器日志,发现数据库连接超时了:<br><img src="/images/tech/db/mysql-read-timeout.jpg" alt="mysql连接超时"><br>第一反应是数据库挂了,导致数据库连不上了。</p><p>可是,神奇的是,接下来发现数据库的查询功能还能用,但是所有的状态修改类的写操作都不能用了。 这下知道遇到麻烦了,肯定是mysql数据库的产生锁了,把所有的写操作阻塞了。</p><p>按照现在的这个情况,mysql读没有问题,写被阻塞,出问题的锁肯定是读锁(共享锁),而且还是全局性质的,因为所有的写都被影响了。</p><h1 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h1><p>按照上面的思路去数据库里执行一下<code>SHOW ENGINE INNODB STATUS</code>, 查看一下数据库的状态。 结果如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs log">*** WE ROLL BACK TRANSACTION (2)<br>------------<br>TRANSACTIONS<br>------------<br>Trx id counter 48415098<br>Purge done for trx's n:o < 48412278 undo n:o < 0 state: running but idle<br>History list length 0<br>LIST OF TRANSACTIONS FOR EACH SESSION:<br>---TRANSACTION 421386947297640, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947296832, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947296024, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947295216, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947294408, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947293600, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947291984, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947288752, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947287944, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947287136, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947283904, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br>---TRANSACTION 421386947280672, not started<br>0 lock struct(s), heap size 1128, 0 row lock(s)<br></code></pre></td></tr></table></figure><p>发现了大量的如上的事务在积压,没有执行。 与预期一致,就是某个锁导致事务阻塞了。</p><p>注意这里不会是死锁问题,因为死锁是锁冲突,mysql会自动解决的,不会导致事务长时间阻塞。</p><p>查询一下mysql的执行进程,看看情况:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"># 查询一个耗时最久的 进程<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> information_schema.PROCESSLIST <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> <span class="hljs-type">time</span> <span class="hljs-keyword">desc</span>;<br></code></pre></td></tr></table></figure><p>发现了如下场景<br><img src="/images/tech/db/mysql_waiting_for_global_read_lock.jpg" alt="waiting for global read lock"></p><p>由此我们知道:</p><ol><li>确实发生了一个全局性的读锁<code>waiting for global read lock</code>导致了后续写操作的进行。 后面的事务都在等待这个读锁的释放。</li><li>情况比我们想象的严重,这个读锁不仅影响了我们自己的实例,还把整个mysql下所有的库实例都影响了。</li><li>问题的原因也发现了,就是有一个binlog dump后没有结束导致的。也就是那个耗时最久的进程。</li></ol><h1 id="问题解决"><a href="#问题解决" class="headerlink" title="问题解决"></a>问题解决</h1><p>既然发现了这个锁产生的原因,直接把这个进程kill掉就可以了</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql">kill <span class="hljs-number">3518815</span><br></code></pre></td></tr></table></figure><p>binlog的进程杀掉后,后面阻塞的任务果然都没了。</p><p>记录一下排查问题过程中常用的sql</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs sql"># 查询当前元数据锁及关联的线程和事务<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> performance_schema.metadata_locks l<br><span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> performance_schema.threads t <span class="hljs-keyword">on</span> t.THREAD_ID <span class="hljs-operator">=</span> l.OWNER_THREAD_ID <br><span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> performance_schema.events_transactions_current et <span class="hljs-keyword">on</span> et.THREAD_ID <span class="hljs-operator">=</span> t.THREAD_ID <br><span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> information_schema.INNODB_TRX trx <span class="hljs-keyword">on</span> t.PROCESSLIST_ID <span class="hljs-operator">=</span> trx.trx_mysql_thread_id <br><br># 当前系统内的事务<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> information_schema.innodb_trx;<br><br># 查询一个耗时最久的 进程<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> information_schema.PROCESSLIST <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> <span class="hljs-type">time</span> <span class="hljs-keyword">desc</span>;<br><br># 查询innodb 状态<br><span class="hljs-keyword">SHOW</span> ENGINE INNODB STATUS<br></code></pre></td></tr></table></figure><h1 id="问题避免"><a href="#问题避免" class="headerlink" title="问题避免"></a>问题避免</h1><p>发现问题,解决问题,避免问题才是我们排查问题最终要的目的。</p><p>我们这个问题是flink cdc 数据的时候导致的,后续的解决办法很简单:<br>第一: 以后flink 不要cdc主库了,避免对业务产生影响。<br>第二:flink cdc 一定要做好异常拦截处理,我们这就是因为异常导致fink退出了,无法释放锁<br>第三:如果可以的话,尽量使用flink的无锁cdc版本</p><h3 id="关于这个global-read-lock是怎么产生的"><a href="#关于这个global-read-lock是怎么产生的" class="headerlink" title="关于这个global read lock是怎么产生的"></a>关于这个<code>global read lock</code>是怎么产生的</h3><p>当mysql在dump整个数据库的时候,会对数据库执行一个<code>FLUSH TABLES WITH READ LOCK</code>命令来加锁,这个命令会停止对所有表的写操作,并允许读操作继续。<br>等备份完成,会使用<code>UNLOCK TABLES</code>来解锁,</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://dev.mysql.com/doc/refman/8.4/en/flush.html">mysql 加 global read lock 机制</a></p>]]></content>
<categories>
<category>数据库</category>
</categories>
<tags>
<tag>mysql</tag>
</tags>
</entry>
<entry>
<title>如果让百度爬虫爬我的github博客</title>
<link href="/2024/05/22/site-how-baidu-spide-github-site/"/>
<url>/2024/05/22/site-how-baidu-spide-github-site/</url>
<content type="html"><![CDATA[<p>我们辛辛苦苦的通过github pages建好我们的博客之后。接下来可能会想要更多的人来访问我们的博客,<br>然后一顿操作后发现,github 把百度的爬虫屏蔽了,所以我们的网站也就不能被百度搜索了,我们的博客也就没法被百度搜索到了。</p><p>这怎么行呢? 我们的博客主要群体还是国内的程序员的,少了百度这个来源,访问量一下子少了一大半。</p><p>作为一名程序员,我们怎么能被这个事情难倒呢?</p><p>让我们来分析一下,github 把百度的爬虫屏蔽了,这导致了我们的博客不能被百度收录。既然百度不能爬github了,我们可以让百度去别的地方爬我们的博客呀。</p><p>思路一开天地宽,我们可以通过CDN的机制来把我们的博客搬到CDN上,然后让百度访问CDN就可以了呀。</p><p>可惜,试过七牛云等一些国内的CDN,发现同步网站还要备案,这个我要是有,也就不用这么麻烦的想办法同步内容到百度了,这一步是走不通了。</p><p>最后,我发现了<a href="https://vercel.com/">vercel</a>这个网站,可以镜像我们的github 博客,而又没有屏蔽百度的爬虫,完美的解决了我的问题。</p><h1 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h1><ul><li>github博客站建好</li><li>一个域名,我们需要域名来做博客的跳转。</li></ul><h1 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h1><h2 id="关联vercel"><a href="#关联vercel" class="headerlink" title="关联vercel"></a>关联vercel</h2><ol><li>注册登录<a href="https://vercel.com/">vercel</a></li><li>新建一个项目(add new project), 关联到自己github上的 博客仓库<br><img src="/images/site/vercel-import-github-project.jpg" alt="vercel关联github"></li><li>添加指向vercel项目的二级域名,比如我的<code>blog.hancher.top</code><br><img src="/images/site/vercel-domain.jpg" alt="vercel配置域名"></li><li>此时,我们在Vercel里的项目已经构建好了,下次 GitHub 项目的任何更新都会触发 Vercel 项目更新<br><img src="/images/site/vercel-project-view.jpg" alt="vercel效果"></li></ol><h2 id="配置域名解析"><a href="#配置域名解析" class="headerlink" title="配置域名解析"></a>配置域名解析</h2><p>去域名的解析后台,添加域名的CNAME解析,记录值<code>cname-china.vercel-dns.com</code> 此时,通过<code>blog.hancher.top</code>就可以访问vercel CDN上的静态网站了。</p><p>当然,也可以将记录值改为Vercel的自己项目的vercel.app 域名。将线路解析类型改为<code>百度</code> 亦可。</p><h2 id="关联百度解析"><a href="#关联百度解析" class="headerlink" title="关联百度解析"></a>关联百度解析</h2><ol><li>去<a href="https://ziyuan.baidu.com/">百度站点管理</a> 里注册账号,创建<code>blog.hancher.top</code>域名的网站,即可以抓取我们的博客站点了。</li><li>将百度给的一个签名放到网站的根目录下<br><img src="/images/site/baidu-ziyuan-config.jpg" alt="百度站点"></li><li>通过<strong>抓取诊断</strong> 来验证我们的博客是否可以正常抓取<br><img src="/images/site/baidu-ziyuan-fetch-test.jpg" alt="抓取诊断"></li><li>耐心等待几天,在百度搜索框里搜索 <code>site:blog.hancher.top</code>来验证我们的配置是否生效,此时大概率就能搜到自己的博客文章了。<br><img src="/images/site/baidu-site-search.jpg" alt="验证博客"></li></ol><h2 id="2024年更新"><a href="#2024年更新" class="headerlink" title="2024年更新"></a>2024年更新</h2><p>因为Vercel被墙(dns污染方式), 该方法暂时失效。等待后续解决。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://www.zhihu.com/question/30898326">知乎-如何解决百度爬虫无法爬取搭建在Github上的个人博客的问题</a><br><a href="https://blog.csdn.net/weixin_40026797/article/details/126919662">建站过程中的踩坑记录:自定义域名、百度收录与备案</a></p>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>建站</tag>
<tag>vercel</tag>
<tag>网站曝光</tag>
</tags>
</entry>
<entry>
<title>羽毛球学习资源</title>
<link href="/2024/01/12/favorite-badminton/"/>
<url>/2024/01/12/favorite-badminton/</url>
<content type="html"><![CDATA[<h2 id="视频"><a href="#视频" class="headerlink" title="视频"></a>视频</h2><p>李在福系列:</p><ol><li><a href="https://www.bilibili.com/video/BV1Yx411W7nJ">追球(基础系列)</a></li><li><a href="https://www.bilibili.com/video/BV1Yx411W7sS">双打</a></li><li><a href="https://www.bilibili.com/video/BV1Zx411W7AH">混双</a></li><li><a href="https://www.bilibili.com/video/BV1Zx411W7FC">杀球技巧</a></li><li><a href="https://www.bilibili.com/video/BV1hx411V7we">网前扑球</a></li><li><a href="https://www.bilibili.com/video/BV1hx411V7cZ">反手后场高球</a></li><li><a href="https://www.bilibili.com/video/BV1hx411V7cK">步伐</a></li><li><a href="https://www.bilibili.com/video/BV1hx411V7cL">步伐训练</a></li><li><a href="https://www.bilibili.com/video/BV1hx411V7uY">论球</a></li></ol>]]></content>
<categories>
<category>生活</category>
</categories>
</entry>
<entry>
<title>springdoc-openapi的集成与常见问题</title>
<link href="/2023/11/29/spring-springdoc-openapi/"/>
<url>/2023/11/29/spring-springdoc-openapi/</url>
<content type="html"><![CDATA[<p>在项目中经常使用swagger来作为项目接口的自动化api文档,尤其是springboot项目,一般都是通过<code>springfox-boot-starter</code>来集成swagger的使用。<br><code>springfox-boot-starter</code> 在2.x时代用着很方便,但是随着项目的发展,尤其是spring项目的不断推出新版本,<br><code>springfox-boot-starter</code>在3.0版本就已经明显存在兼容性问题了,而且已经3年多没维护了,所以寻找替代品是一种虽然不紧迫但很必须的事项了。<br>而springdoc-openapi就是springfox-boot-starter的替代品,且获得了swagger的官方支持,算是springfox在springboot高版本的天热替代品。<br><img src="/images/tech/spring/springfox-stop-update.jpg" alt="springfox已经3年未更新了"></p><p>本文就介绍springdoc-openapi的集成与常见问题。</p><h1 id="项目依赖"><a href="#项目依赖" class="headerlink" title="项目依赖"></a>项目依赖</h1><p>如果你的项目是springboot2.x版本,请使用springdoc-openapi的1.x版本。<br>如果你的项目是springboot3.x版本,请使用springdoc-openapi的2.x版本。</p><p>本文案例的环境是:</p><ul><li>java 17</li><li>springboot 2.7.18</li></ul><h1 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h1><p>使用idea创建一个新的maven springboot项目,添加如下依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springdoc<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>springdoc-openapi-ui<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.7.0<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>创建一个Controller</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping("/hello")</span><br><span class="hljs-meta">@Tag(name="openapi demo")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HelloWorldController</span> {<br><br> <span class="hljs-meta">@GetMapping("/world")</span><br> <span class="hljs-meta">@Operation(summary = "helloWorld接口",description = "接口描述信息")</span><br> <span class="hljs-meta">@Parameter(name = "name", description = "名称", required = true, in = ParameterIn.QUERY)</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">helloWorld</span><span class="hljs-params">(String name)</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span>+ Optional.of(name).orElse(<span class="hljs-string">"world"</span>);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 隐藏的api,不再swagger 显示</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-meta">@GetMapping("/hidden")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">hiddenApi</span><span class="hljs-params">(String name)</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span>+ Optional.of(name).orElse(<span class="hljs-string">"world"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p>然后就可以直接启动了,不用任何配置,就可以看到swagger的界面了。<br>swagger默认地址: <a href="http://localhost:8080/swagger-ui/index.html#/">http://localhost:8080/swagger-ui/index.html#/</a><br><img src="/images/tech/spring/springdoc-swagger-default.jpg" alt="springdoc版的swagger的默认页面"></p><h2 id="通过配置文件修改相关配置"><a href="#通过配置文件修改相关配置" class="headerlink" title="通过配置文件修改相关配置"></a>通过配置文件修改相关配置</h2><p>如果你不想使用默认的配置,可以通过配置文件修改相关配置。比如我通过下面配置对swagger的界面进行了微调。</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-comment"># application.properties</span><br><span class="hljs-attr">springdoc.swagger-ui.disable-swagger-default-url</span>=<span class="hljs-string">true</span><br><span class="hljs-comment"># 布局 StandaloneLayout,BaseLayout</span><br><span class="hljs-attr">springdoc.swagger-ui.layout</span>=<span class="hljs-string">BaseLayout</span><br><span class="hljs-attr">springdoc.swagger-ui.path</span>=<span class="hljs-string">/v3/swagger-ui.html</span><br><span class="hljs-comment"># swagger-ui 默认关联的api接口配置, 要与api-docs.path 一致。 不配置默认取api-docs.path配置</span><br><span class="hljs-attr">springdoc.swagger-ui.url</span>=<span class="hljs-string">/v3/test/api-docs</span><br><span class="hljs-comment"># api json 接口配置, 可以自定义其他json接口</span><br><span class="hljs-attr">springdoc.api-docs.path</span>=<span class="hljs-string">/v3/test/api-docs</span><br></code></pre></td></tr></table></figure><p>因为修改了swagger页面的访问地址,所以我们的访问地址变成了:<br><a href="http://localhost:8080/v3/swagger-ui/index.html#/">http://localhost:8080/v3/swagger-ui/index.html#/</a><br>效果如图:<br><img src="/images/tech/spring/springdoc-swagger-properties.jpg" alt="springdoc版的swagger的properties配置版"></p><h2 id="通过自定义配置类修改相关配置"><a href="#通过自定义配置类修改相关配置" class="headerlink" title="通过自定义配置类修改相关配置"></a>通过自定义配置类修改相关配置</h2><p>如果我们想要更自有的配置,可以通过自定义配置类来修改相关配置。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SwaggerConfig</span> {<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 添加摘要信息</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-keyword">public</span> OpenAPI <span class="hljs-title function_">openAPI</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">OpenAPI</span>()<br> .info(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Info</span>().title(<span class="hljs-string">"hancher demo open api"</span>)<br> .description(<span class="hljs-string">"helloWorld接口文档"</span>)<br> .version(<span class="hljs-string">"v3.0.0"</span>)<br> <span class="hljs-comment">// 联系人</span><br> .contact(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Contact</span>().name(<span class="hljs-string">"寒澈"</span>).url(<span class="hljs-string">"https://www.hancher.top/"</span>))<br> )<br> <span class="hljs-comment">// 其他文档信息</span><br> .externalDocs(<span class="hljs-keyword">new</span> <span class="hljs-title class_">ExternalDocumentation</span>()<br> .description(<span class="hljs-string">"寒澈笔记"</span>)<br> .url(<span class="hljs-string">"https://www.hancher.top/"</span>));<br> }<br><br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 模式三: 自定义api分组</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> */</span><br><span class="hljs-comment">// @Bean</span><br> <span class="hljs-keyword">public</span> GroupedOpenApi <span class="hljs-title function_">publicApi</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> GroupedOpenApi.builder()<br> .packagesToScan(<span class="hljs-string">"com.hancher.demo.springbootopenapi.controller"</span>) <span class="hljs-comment">// 扫描包</span><br> .displayName(<span class="hljs-string">"寒澈openapi"</span>)<br> .addOpenApiMethodFilter(method -> method.isAnnotationPresent(Operation.class))<br> .build();<br> }<br>}<br></code></pre></td></tr></table></figure><p>还是默认访问地址:<a href="http://localhost:8080/swagger-ui/index.html#/">http://localhost:8080/swagger-ui/index.html#/</a><br><img src="/images/tech/spring/springdoc-swagger-openApi.jpg" alt="springdoc版的swagger的openapi配置版"></p><h1 id="迁移成本"><a href="#迁移成本" class="headerlink" title="迁移成本"></a>迁移成本</h1><p>除了将老版本的注解替换成新的注解之外,没有任何其他改的。<br><img src="/images/tech/spring/springdoc-from-springfox.jpg" alt="springfox迁移到springdoc的注解变动"></p><h1 id="集成中遇到的问题"><a href="#集成中遇到的问题" class="headerlink" title="集成中遇到的问题"></a>集成中遇到的问题</h1><p>我在集成过程中遇到的最大的问题就是明明集成好了,但是swagger的访问页面还是swagger的默认petStore的页面,而不是我配置的controller的页面。这个问题让我排查了好久。<br><img src="/images/tech/spring/springdoc-petstore-page.jpg" alt="springdoc总是显示默认petstore接口"></p><p>首先,我们我们要先了解的是,swagger-ui是一套api展示页面,他的数据是从<code>v3/api-docs</code>接口获取的json数据。<br>所以排查问题,我们首先要看页面有没有调用这个接口(有可能我们在配置文件了将这个接口改了),再次看看这个接口有没有返回正常的json结果。<br><img src="/images/tech/spring/springdoc-v3-apidocs.jpg" alt="springdoc的数据接口"><br>如果这这个接口返回了正常的数据,且是我们的配置的接口数据,那么问题很可能是我们的一些安全配置将swagger的相关接口给屏蔽了。<br>比如<code>WebSecurityConfigurerAdapter</code>, 我就是在这个环节上出的问题</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span><br>{<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity httpSecurity)</span> <span class="hljs-keyword">throws</span> Exception<br> {<br> <br> }<br>}<br></code></pre></td></tr></table></figure><h1 id="项目demo"><a href="#项目demo" class="headerlink" title="项目demo"></a>项目demo</h1><p>集成了application,java bean config 两种方式的demo<br><a href="https://github.com/Hanchers/spring-boot-demo/tree/master/springdoc-openapi">springdoc-demo</a></p>]]></content>
<categories>
<category>spring</category>
</categories>
<tags>
<tag>spring</tag>
<tag>openapi</tag>
</tags>
</entry>
<entry>
<title>linux定时任务crontab不执行的问题排查过程</title>
<link href="/2023/10/13/linux-linux-crontab-not-work/"/>
<url>/2023/10/13/linux-linux-crontab-not-work/</url>
<content type="html"><![CDATA[<h3 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h3><p>最近在搞一个比较复杂的crontab 定时任务, 核心思路就是通过crontab触发一个bash脚本,调用docker内的一个指令,来实现docker积压日志的删除清理操作。</p><p>然后问题就来了:我通过命令行调用脚本,能够正常执行,但是通过crontab,脚本就死活调用不成功。</p><p>以下就是记录的排查crontab调用不成功的过程:</p><h3 id="案发现场"><a href="#案发现场" class="headerlink" title="案发现场"></a>案发现场</h3><p>crontab上配置定时任务,把该有的环境变量都配置上,脚本的开头也加上了<code>source /etc/profile</code> 为了方便排查,调度改为2分钟一次</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs crontab">*/2 * * * * /usr/bin/docker exec -it container-name bash -c 'sh /var/log/del_overdue_log.sh' <br></code></pre></td></tr></table></figure><p>不出意外的话,定时任务调度失败了。 </p><p>通过<code>tail -f /var/log/cron</code> 去cron调度日志里查看调用情况:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">Oct 13 10:58:01 : CROND[27764]: (root) CMD (/usr/bin/docker <span class="hljs-built_in">exec</span> -it container-name bash -c <span class="hljs-string">'sh /var/log/del_overdue_log.sh'</span>)<br>Oct 13 10:58:01 : (root) MAIL (mailed 54 bytes of output but got status 0x004b#012)<br></code></pre></td></tr></table></figure><p>日志表示,我们的cron任务调度了,但是没有成功。 失败原因本来想通过mail发给当前root用户的,但发送失败了,原因是获得了一个失败状态吗。</p><p>如何解决这个mail发送失败的问题呢?</p><h4 id="启用postfix-接收系统邮件"><a href="#启用postfix-接收系统邮件" class="headerlink" title="启用postfix 接收系统邮件"></a>启用postfix 接收系统邮件</h4><p>去<code>/etc/postfix/main.cf</code> 配置文件,搜索inet_interfaces,改成如下配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">inet_interfaces = all<br><span class="hljs-comment"># inet_interfaces = localhost</span><br></code></pre></td></tr></table></figure><p>重启postfix </p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl restart postfix<br></code></pre></td></tr></table></figure><p>此时,我们就可以收到cron调度失败的系统通知邮件了。</p><p><code>tail -f /var/spool/mail/root </code> 查看邮件信息:</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">From</span> [email protected] Fri Oct <span class="hljs-number">13</span> <span class="hljs-number">12</span>:<span class="hljs-number">56</span>:<span class="hljs-number">01</span> <span class="hljs-number">2023</span><br><span class="hljs-attribute">Return</span>-Path: <[email protected]><br><span class="hljs-attribute">X</span>-Original-To: root<br><span class="hljs-attribute">Delivered</span>-To: [email protected]<br><span class="hljs-attribute">Received</span>: by host.localdomain (Postfix, from userid <span class="hljs-number">0</span>)<br><span class="hljs-attribute">id</span> D94301329A7; Fri, <span class="hljs-number">13</span> Oct <span class="hljs-number">2023</span> <span class="hljs-number">12</span>:<span class="hljs-number">56</span>:<span class="hljs-number">01</span> +<span class="hljs-number">0800</span> (CST)<br><span class="hljs-attribute">From</span>: <span class="hljs-string">"(Cron Daemon)"</span> <[email protected]><br><span class="hljs-attribute">To</span>: [email protected]<br><span class="hljs-attribute">Subject</span>: Cron <root@host> /usr/bin/docker exec -it container-name bash -c 'sh /var/log/del_overdue_log.sh'<br><span class="hljs-attribute">Content</span>-Type: text/plain; charset=UTF-<span class="hljs-number">8</span><br><span class="hljs-attribute">Auto</span>-Submitted: auto-generated<br><span class="hljs-attribute">Precedence</span>: bulk<br><span class="hljs-attribute">X</span>-Cron-Env: <XDG_SESSION_ID=<span class="hljs-number">2387</span>><br><span class="hljs-attribute">X</span>-Cron-Env: <XDG_RUNTIME_DIR=/run/user/<span class="hljs-number">0</span>><br><span class="hljs-attribute">X</span>-Cron-Env: <LANG=en_US.UTF-<span class="hljs-number">8</span>><br><span class="hljs-attribute">X</span>-Cron-Env: <SHELL=/bin/sh><br><span class="hljs-attribute">X</span>-Cron-Env: <HOME=/root><br><span class="hljs-attribute">X</span>-Cron-Env: <PATH=/usr/bin:/bin><br><span class="hljs-attribute">X</span>-Cron-Env: <LOGNAME=root><br><span class="hljs-attribute">X</span>-Cron-Env: <USER=root><br><span class="hljs-attribute">Message</span>-Id: <<span class="hljs-number">20231013045601</span>[email protected]><br><span class="hljs-attribute">Date</span>: Fri, <span class="hljs-number">13</span> Oct <span class="hljs-number">2023</span> <span class="hljs-number">12</span>:<span class="hljs-number">56</span>:<span class="hljs-number">01</span> +<span class="hljs-number">0800</span> (CST)<br><br><span class="hljs-attribute">the</span> input device is not a TTY<br></code></pre></td></tr></table></figure><p>会发现,失败的原因是我们的脚本调度器不是TTY(终端),所以失败了。</p><blockquote><p>注意,问题定位后记得把postfix配置恢复原状</p></blockquote><h4 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h4><p>找到日志了,任务调度失败的原因也找到了,分析一下我们的任务指令,发现我们在脚本调度命令里存在选项<code> docker exec -it</code> 配置。</p><p>问题就在这个<code>-it</code> 上面了,这要求我们开启一个交互式终端来控制docker指令,而crontab调度器明显不是一个终端,所以失败了。</p><h3 id="问题解决"><a href="#问题解决" class="headerlink" title="问题解决"></a>问题解决</h3><p>把命令行里的<code>-it</code>去掉,改为<code>-d</code>后端调用就可以了。以下是最终版配置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs crontab">* 1 * * * /usr/bin/docker exec -d container-name bash -c 'sh /var/log/del_overdue_log.sh' >/dev/null 2>$1 <br></code></pre></td></tr></table></figure><p>最终指令改了两点:</p><ol><li>将<code>-it</code>改为<code>-d</code>, 解决crontab调度失败的问题。</li><li>在crontab命令上增加 <code>>/dev/null 2>$1</code> 配置,这是为了取消任务调度的系统日志产生,防止系统日志爆炸,也可以不加。</li></ol><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><p>授之以渔不如授之以渔, 出现问题不可怕,只要我们掌握了一些解决问题的方法论, 任何问题都能迎刃而解。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://www.geeksforgeeks.org/crontab-in-linux-with-examples/">‘crontab’ in Linux with Examples</a></p><p><a href="https://zhangge.net/5093.html">crontab不执行排查</a></p>]]></content>
<categories>
<category>编程人生</category>
</categories>
<tags>
<tag>linux</tag>
</tags>
</entry>
<entry>
<title>java应用连接docker内的oracle时报ORA-01882错误</title>
<link href="/2023/08/18/exception-java-oracle-docker-timezone-not-find/"/>
<url>/2023/08/18/exception-java-oracle-docker-timezone-not-find/</url>
<content type="html"><![CDATA[<h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>数据库升级,用docker容器安装oracle11g数据库,应用连接的时候报ORA-01882错误</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs stylus">java<span class="hljs-selector-class">.sql</span><span class="hljs-selector-class">.SQLException</span>: ORA-<span class="hljs-number">00604</span>: error occurred at recursive SQL level <span class="hljs-number">1</span><br>ORA-<span class="hljs-number">01882</span>: timezone region not found<br><br> at oracle<span class="hljs-selector-class">.jdbc</span><span class="hljs-selector-class">.driver</span><span class="hljs-selector-class">.T4CTTIoer11</span><span class="hljs-selector-class">.processError</span>(T4CTTIoer11<span class="hljs-selector-class">.java</span>:<span class="hljs-number">509</span>)<br> at oracle<span class="hljs-selector-class">.jdbc</span><span class="hljs-selector-class">.driver</span><span class="hljs-selector-class">.T4CTTIoer11</span><span class="hljs-selector-class">.processError</span>(T4CTTIoer11<span class="hljs-selector-class">.java</span>:<span class="hljs-number">456</span>)<br> at oracle<span class="hljs-selector-class">.jdbc</span><span class="hljs-selector-class">.driver</span><span class="hljs-selector-class">.T4CTTIoer11</span><span class="hljs-selector-class">.processError</span>(T4CTTIoer11<span class="hljs-selector-class">.java</span>:<span class="hljs-number">451</span>)<br></code></pre></td></tr></table></figure><p>环境:</p><ul><li>jdk : openjdk8</li><li>oracle: 11g</li></ul><h2 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h2><p>问题本质上是java应用程序启动时, 找不到本地系统的时区环境变量导致。 我们只有把时区环境变量配置上就可以了。</p><p>解决办法有二:</p><ol><li>java应用程序启动时增加环境变量配置</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">-Duser.timezone=xxx(xxx为oracle时区ID)<br></code></pre></td></tr></table></figure><ol start="2"><li>直接修改服务器的系统环境变量(更彻底)</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 时间更新为东八区时间</span><br><span class="hljs-built_in">cp</span> /usr/share/zoneinfo/Asia/Shanghai /etc/localtime<br><span class="hljs-comment"># 配置时区为东八区。 Java可以通过Timezone获取时区的,修改完重启服务即可。</span><br><span class="hljs-built_in">echo</span> <span class="hljs-string">'Asia/Shanghai'</span> >/etc/timezone<br></code></pre></td></tr></table></figure><p>如果是docker的话,将宿主机改为上述配置后,docker容器启动(run)增加挂载配置就可以了</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs arcade">-v /etc/localtime:<span class="hljs-regexp">/etc/</span>localtime<br>-v /etc/<span class="hljs-built_in">timezone</span>:<span class="hljs-regexp">/etc/</span><span class="hljs-built_in">timezone</span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>编程人生</category>
</categories>
<tags>
<tag>java</tag>
<tag>异常</tag>
<tag>oracle</tag>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title>git优雅迁库的方式</title>
<link href="/2023/07/31/git-mirror/"/>
<url>/2023/07/31/git-mirror/</url>
<content type="html"><![CDATA[<p>在我们开发项目的时候,经常会遇到将一个git仓库迁移到一个新的git仓库的情况。 这个时候很多人会把原始的仓库最新代码复制到新库目录下,然后force push即可。</p><p>但是这样会产生一个问题,就是新库会把我们的历史提交记录等信息全部丢失了,当做一个全新的仓库来处理,这样给我们后续的问题追踪回溯带来了一些问题。</p><p>下面我们介绍一下git优雅迁库的三种方式。</p><h3 id="裸库方式-推荐"><a href="#裸库方式-推荐" class="headerlink" title="裸库方式(推荐)"></a>裸库方式(推荐)</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">// 使用--bare克隆裸仓库<br>git <span class="hljs-built_in">clone</span> --bare git@url1<br>// 推送到新库<br>git push git@url2<br></code></pre></td></tr></table></figure><h3 id="镜像库方式"><a href="#镜像库方式" class="headerlink" title="镜像库方式"></a>镜像库方式</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">// 使用--mirror克隆裸仓库<br>git <span class="hljs-built_in">clone</span> --mirror git@url1<br>// 推送到新库<br>git push git@url2<br></code></pre></td></tr></table></figure><p>镜像库是裸库的一种简写,等同于:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ git <span class="hljs-built_in">clone</span> --bare <span class="hljs-variable">$URL</span><br>$ (<span class="hljs-built_in">cd</span> $(<span class="hljs-built_in">basename</span> <span class="hljs-variable">$URL</span>) && git remote add --mirror=fetch origin <span class="hljs-variable">$URL</span>)<br></code></pre></td></tr></table></figure><p>由此可知,裸库方式clone下的是git仓库的基本信息。镜像库不仅把基本信息clone下来了,还拉去了最新的分支代码。</p><h3 id="新增remote方式"><a href="#新增remote方式" class="headerlink" title="新增remote方式"></a>新增remote方式</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git remote add origin2 [repo-url]<br>git push origin2<br></code></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://stackoverflow.com/questions/3959924/whats-the-difference-between-git-clone-mirror-and-git-clone-bare">git clone –mirror 和 –bare的区别</a></p>]]></content>
<categories>
<category>编程人生</category>
<category>git</category>
</categories>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title>jdbc反序列化漏洞修复</title>
<link href="/2023/07/04/safe-mysql-jdbc-object-serial/"/>
<url>/2023/07/04/safe-mysql-jdbc-object-serial/</url>
<content type="html"><![CDATA[<h2 id="安全漏洞描述"><a href="#安全漏洞描述" class="headerlink" title="安全漏洞描述"></a>安全漏洞描述</h2><p>Jdbc反序列化漏洞发生在后台代码使用mysql-connector组件连接数据库且攻击者可控制连接参数的情况下。攻击者可以构造恶意的mysql数据库使服务器去访问链接它从而触发反序列化漏洞导致命令执行</p><p>mysql 连接参数风险点:autoDeserialize,statementInterceptors</p><h2 id="风险等级:高"><a href="#风险等级:高" class="headerlink" title="风险等级:高"></a>风险等级:高</h2><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><ul><li>升级mysql版本到8.0.27+,官方已经修复此问题</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>mysql<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mysql-connector-java<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>8.0.27<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ul><li>过滤掉风险参数<br> 一般情况下不需要对autoDeserialize和statementInterceptors参数进行设置。在后端代码设置连接字符参数白名单,或者将这两个参数直接替换掉。<br> 替换代码参考:</li></ul><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs applescript">Properties <span class="hljs-keyword">prop</span> = new Properties();<br><span class="hljs-keyword">prop</span>.<span class="hljs-keyword">put</span>(<span class="hljs-string">"autoDeserialize"</span>, <span class="hljs-string">"false"</span>);<br><span class="hljs-keyword">prop</span>.<span class="hljs-keyword">put</span>(<span class="hljs-string">"allowLoadLocalInfile"</span>, <span class="hljs-string">"false"</span>);<br><span class="hljs-keyword">prop</span>.<span class="hljs-keyword">put</span>(<span class="hljs-string">"allowUrlInLocalInfile"</span>, <span class="hljs-string">"false"</span>);<br><span class="hljs-keyword">prop</span>.<span class="hljs-keyword">put</span>(<span class="hljs-string">"allowLoadLocalInfileInPath"</span>, <span class="hljs-string">""</span>);<br><span class="hljs-keyword">prop</span>.<span class="hljs-keyword">put</span>(<span class="hljs-string">"statementInterceptors"</span>, <span class="hljs-string">""</span>);<br><br>Connectionconn=DriverManager.getConnection(DB_URL,<span class="hljs-keyword">prop</span>);<br></code></pre></td></tr></table></figure><h2 id="安全无小事!!!"><a href="#安全无小事!!!" class="headerlink" title="安全无小事!!!"></a>安全无小事!!!</h2><p><a href="https://cloud.tencent.com/developer/article/1822662">Jdbc反序列化漏洞复现浅析</a></p><p><a href="https://www.anquanke.com/post/id/237357#h3-13">探探JDBC反序列化漏洞</a></p><p><a href="https://wiki.wgpsec.org/knowledge/ctf/JDBC-Unserialize.html">狼组-JDBC反序列化漏洞</a></p>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>mysql</tag>
<tag>安全</tag>
</tags>
</entry>
<entry>
<title>CORS跨域攻击与修复</title>
<link href="/2023/06/21/safe-cors-attack/"/>
<url>/2023/06/21/safe-cors-attack/</url>
<content type="html"><![CDATA[<p>CORS跨域的逆向操作,如何收回放开的跨域权限。</p><p>以下是CORS漏洞的一些内容:</p><h2 id="安全漏洞描述"><a href="#安全漏洞描述" class="headerlink" title="安全漏洞描述"></a>安全漏洞描述</h2><p>Web服务端CORS(跨域资源共享)错误配置,无法正确验证Origin头,任何网站都可以发出使用用户凭据发出的请求,并读取对这些请求的响应,信任任意来源可以有效地禁用同源策略,从而允许第三方网站进行双向交互,易导致敏感信息泄露</p><p><img src="/images/tech/safe/safe_cros_attack.png" alt="swagger-switch"></p><h2 id="风险等级:高"><a href="#风险等级:高" class="headerlink" title="风险等级:高"></a>风险等级:高</h2><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>需要修复两处:<br>1,代码级别(必修)<br>程序默认是禁止跨域的,也可以去掉相关逻辑。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs code">// 比如: *.domain.com, 127.0.0.1 <br>@Value("${cros.allow-origin:*}")<br>private String crosAllowOrigin;<br><br>@Bean<br>public CorsFilter corsFilter()<br>{<br> CorsConfiguration config = new CorsConfiguration();<br> config.setAllowCredentials(false);<br> // 设置访问源地址, <br> for (String origin : crosAllowOrigin.split(",")) {<br> config.addAllowedOriginPattern(origin);<br> }<br> // 设置访问源请求头 , 也可以配置 <br> config.addAllowedHeader("*");<br> // 设置访问源请求方法 , 也可以配置 <br> config.addAllowedMethod("*");<br> // 有效期 1800秒 <br> config.setMaxAge(1800L);<br> // 添加映射路径,拦截一切请求 <br> UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();<br> source.registerCorsConfiguration("/**", config);<br> // 返回新的CorsFilter <br> return new CorsFilter(source);<br>}<br></code></pre></td></tr></table></figure><p>2,nginx服务<br>增加相关头配置</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">add_header</span> <span class="hljs-string">Access-Control-Allow-Origin your.domain.com;</span><br></code></pre></td></tr></table></figure><h2 id="安全无小事!!!"><a href="#安全无小事!!!" class="headerlink" title="安全无小事!!!"></a>安全无小事!!!</h2>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>spring</tag>
<tag>安全</tag>
</tags>
</entry>
<entry>
<title>存储XSS问题与修复</title>
<link href="/2023/06/21/safe-xss-reactive/"/>
<url>/2023/06/21/safe-xss-reactive/</url>
<content type="html"><![CDATA[<h2 id="安全漏洞描述"><a href="#安全漏洞描述" class="headerlink" title="安全漏洞描述"></a>安全漏洞描述</h2><p>恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页时,嵌入其中Web里面的Script代码会被执行,进而窃取到管理员用户权限等。</p><h2 id="风险等级:高"><a href="#风险等级:高" class="headerlink" title="风险等级:高"></a>风险等级:高</h2><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><ol><li>对网页端输入的特殊字符过滤<br>建议过滤的关键字为:<br>[1]’单引号<br>[2]”双引号<br>[3]/斜杠<br>[4]反斜杠<br>[5])括号<br>[6];分号<br>[7][中括号<br>[8]<尖括号<br>[9]>尖括号<br>比如把<编码为< </li><li>在cookie中加入httponly属性可以在一定程度上保护用户的cookie,减少出现XSS时损失。</li></ol><p>一段简易的字符过滤程序</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-keyword">function</span> htmlEncode(str){<br> var s = <span class="hljs-string">""</span>;<br> <span class="hljs-keyword">if</span> (str.length == 0) <span class="hljs-built_in">return</span> <span class="hljs-string">""</span>;<br> s = str.replace(/&/g, <span class="hljs-string">"&"</span>);<br> s = s.replace(//g, <span class="hljs-string">">"</span>);<br> s = s.replace(/ /g, <span class="hljs-string">" "</span>);<br> s = s.replace(/\'/g, <span class="hljs-string">"'"</span>);<br> s = s.replace(/\"/g, <span class="hljs-string">""</span><span class="hljs-string">");</span><br><span class="hljs-string"> return s;</span><br><span class="hljs-string">}</span><br></code></pre></td></tr></table></figure><ol start="3"><li>在服务端增加filter过滤<br>通过自定义filter的方式拦截过滤请求</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">XssFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Filter</span><br>{<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 排除链接</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> List<String> excludes = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">(FilterConfig filterConfig)</span> <span class="hljs-keyword">throws</span> ServletException<br> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">tempExcludes</span> <span class="hljs-operator">=</span> filterConfig.getInitParameter(<span class="hljs-string">"excludes"</span>);<br> <span class="hljs-keyword">if</span> (StringUtils.isNotEmpty(tempExcludes))<br> {<br> String[] url = tempExcludes.split(<span class="hljs-string">","</span>);<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; url != <span class="hljs-literal">null</span> && i < url.length; i++)<br> {<br> excludes.add(url[i]);<br> }<br> }<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doFilter</span><span class="hljs-params">(ServletRequest request, ServletResponse response, FilterChain chain)</span><br> <span class="hljs-keyword">throws</span> IOException, ServletException<br> {<br> <span class="hljs-type">HttpServletRequest</span> <span class="hljs-variable">req</span> <span class="hljs-operator">=</span> (HttpServletRequest) request;<br> <span class="hljs-type">HttpServletResponse</span> <span class="hljs-variable">resp</span> <span class="hljs-operator">=</span> (HttpServletResponse) response;<br> <span class="hljs-keyword">if</span> (handleExcludeURL(req, resp))<br> {<br> chain.doFilter(request, response);<br> <span class="hljs-keyword">return</span>;<br> }<br> <span class="hljs-type">XssHttpServletRequestWrapper</span> <span class="hljs-variable">xssRequest</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">XssHttpServletRequestWrapper</span>((HttpServletRequest) request);<br> chain.doFilter(xssRequest, response);<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">handleExcludeURL</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response)</span><br> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> request.getServletPath();<br> <span class="hljs-type">String</span> <span class="hljs-variable">method</span> <span class="hljs-operator">=</span> request.getMethod();<br> <span class="hljs-comment">// GET DELETE 不过滤</span><br> <span class="hljs-keyword">if</span> (method == <span class="hljs-literal">null</span> || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method))<br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br> }<br> <span class="hljs-keyword">return</span> StringUtils.matches(url, excludes);<br> }<br>}<br></code></pre></td></tr></table></figure><p>在wrapper里过滤参数 </p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">XssHttpServletRequestWrapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">HttpServletRequestWrapper</span><br>{<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> request</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">XssHttpServletRequestWrapper</span><span class="hljs-params">(HttpServletRequest request)</span><br> {<br> <span class="hljs-built_in">super</span>(request);<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> String[] getParameterValues(String name)<br> {<br> String[] values = <span class="hljs-built_in">super</span>.getParameterValues(name);<br> <span class="hljs-keyword">if</span> (values != <span class="hljs-literal">null</span>)<br> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">length</span> <span class="hljs-operator">=</span> values.length;<br> String[] escapesValues = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[length];<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < length; i++)<br> {<br> <span class="hljs-comment">// 防xss攻击和过滤前后空格</span><br> escapesValues[i] = EscapeUtil.clean(values[i]).trim();<br> }<br> <span class="hljs-keyword">return</span> escapesValues;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.getParameterValues(name);<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> ServletInputStream <span class="hljs-title function_">getInputStream</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException<br> {<br> <span class="hljs-comment">// 非json类型,直接返回</span><br> <span class="hljs-keyword">if</span> (!isJsonRequest())<br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.getInputStream();<br> }<br><br> <span class="hljs-comment">// 为空,直接返回</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> IOUtils.toString(<span class="hljs-built_in">super</span>.getInputStream(), <span class="hljs-string">"utf-8"</span>);<br> <span class="hljs-keyword">if</span> (StringUtils.isEmpty(json))<br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.getInputStream();<br> }<br><br> <span class="hljs-comment">// xss过滤</span><br> json = EscapeUtil.clean(json).trim();<br> <span class="hljs-type">byte</span>[] jsonBytes = json.getBytes(<span class="hljs-string">"utf-8"</span>);<br> <span class="hljs-keyword">final</span> <span class="hljs-type">ByteArrayInputStream</span> <span class="hljs-variable">bis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ByteArrayInputStream</span>(jsonBytes);<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ServletInputStream</span>()<br> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isFinished</span><span class="hljs-params">()</span><br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isReady</span><span class="hljs-params">()</span><br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">available</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException<br> {<br> <span class="hljs-keyword">return</span> jsonBytes.length;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setReadListener</span><span class="hljs-params">(ReadListener readListener)</span><br> {<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">read</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException<br> {<br> <span class="hljs-keyword">return</span> bis.read();<br> }<br> };<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 是否是Json请求</span><br><span class="hljs-comment"> * </span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> request</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isJsonRequest</span><span class="hljs-params">()</span><br> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">header</span> <span class="hljs-operator">=</span> <span class="hljs-built_in">super</span>.getHeader(HttpHeaders.CONTENT_TYPE);<br> <span class="hljs-keyword">return</span> StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);<br> }<br>}<br></code></pre></td></tr></table></figure><p>xssUtil</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br><span class="line">604</span><br><span class="line">605</span><br><span class="line">606</span><br><span class="line">607</span><br><span class="line">608</span><br><span class="line">609</span><br><span class="line">610</span><br><span class="line">611</span><br><span class="line">612</span><br><span class="line">613</span><br><span class="line">614</span><br><span class="line">615</span><br><span class="line">616</span><br><span class="line">617</span><br><span class="line">618</span><br><span class="line">619</span><br><span class="line">620</span><br><span class="line">621</span><br><span class="line">622</span><br><span class="line">623</span><br><span class="line">624</span><br><span class="line">625</span><br><span class="line">626</span><br><span class="line">627</span><br><span class="line">628</span><br><span class="line">629</span><br><span class="line">630</span><br><span class="line">631</span><br><span class="line">632</span><br><span class="line">633</span><br><span class="line">634</span><br><span class="line">635</span><br><span class="line">636</span><br><span class="line">637</span><br><span class="line">638</span><br><span class="line">639</span><br><span class="line">640</span><br><span class="line">641</span><br><span class="line">642</span><br><span class="line">643</span><br><span class="line">644</span><br><span class="line">645</span><br><span class="line">646</span><br><span class="line">647</span><br><span class="line">648</span><br><span class="line">649</span><br><span class="line">650</span><br><span class="line">651</span><br><span class="line">652</span><br><span class="line">653</span><br><span class="line">654</span><br><span class="line">655</span><br><span class="line">656</span><br><span class="line">657</span><br><span class="line">658</span><br><span class="line">659</span><br><span class="line">660</span><br><span class="line">661</span><br><span class="line">662</span><br><span class="line">663</span><br><span class="line">664</span><br><span class="line">665</span><br><span class="line">666</span><br><span class="line">667</span><br><span class="line">668</span><br><span class="line">669</span><br><span class="line">670</span><br><span class="line">671</span><br><span class="line">672</span><br><span class="line">673</span><br><span class="line">674</span><br><span class="line">675</span><br><span class="line">676</span><br><span class="line">677</span><br><span class="line">678</span><br><span class="line">679</span><br><span class="line">680</span><br><span class="line">681</span><br><span class="line">682</span><br><span class="line">683</span><br><span class="line">684</span><br><span class="line">685</span><br><span class="line">686</span><br><span class="line">687</span><br><span class="line">688</span><br><span class="line">689</span><br><span class="line">690</span><br><span class="line">691</span><br><span class="line">692</span><br><span class="line">693</span><br><span class="line">694</span><br><span class="line">695</span><br><span class="line">696</span><br><span class="line">697</span><br><span class="line">698</span><br><span class="line">699</span><br><span class="line">700</span><br><span class="line">701</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EscapeUtil</span><br>{<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">RE_HTML_MARK</span> <span class="hljs-operator">=</span> <span class="hljs-string">"(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"</span>;<br><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">char</span>[][] TEXT = <span class="hljs-keyword">new</span> <span class="hljs-title class_">char</span>[<span class="hljs-number">64</span>][];<br><br> <span class="hljs-keyword">static</span><br> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">64</span>; i++)<br> {<br> TEXT[i] = <span class="hljs-keyword">new</span> <span class="hljs-title class_">char</span>[] { (<span class="hljs-type">char</span>) i };<br> }<br><br> <span class="hljs-comment">// special HTML characters</span><br> TEXT[<span class="hljs-string">'\''</span>] = <span class="hljs-string">"&#039;"</span>.toCharArray(); <span class="hljs-comment">// 单引号</span><br> TEXT[<span class="hljs-string">'"'</span>] = <span class="hljs-string">"&#34;"</span>.toCharArray(); <span class="hljs-comment">// 双引号</span><br> TEXT[<span class="hljs-string">'&'</span>] = <span class="hljs-string">"&#38;"</span>.toCharArray(); <span class="hljs-comment">// &符</span><br> TEXT[<span class="hljs-string">'<'</span>] = <span class="hljs-string">"&#60;"</span>.toCharArray(); <span class="hljs-comment">// 小于号</span><br> TEXT[<span class="hljs-string">'>'</span>] = <span class="hljs-string">"&#62;"</span>.toCharArray(); <span class="hljs-comment">// 大于号</span><br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 转义文本中的HTML字符为安全的字符</span><br><span class="hljs-comment"> * </span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> text 被转义的文本</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 转义后的文本</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">escape</span><span class="hljs-params">(String text)</span><br> {<br> <span class="hljs-keyword">return</span> encode(text);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 还原被转义的HTML特殊字符</span><br><span class="hljs-comment"> * </span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> content 包含转义符的HTML内容</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 转换后的字符串</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">unescape</span><span class="hljs-params">(String content)</span><br> {<br> <span class="hljs-keyword">return</span> decode(content);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 清除所有HTML标签,但是不删除标签内的内容</span><br><span class="hljs-comment"> * </span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> content 文本</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 清除标签后的文本</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">clean</span><span class="hljs-params">(String content)</span><br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">HTMLFilter</span>().filter(content);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Escape编码</span><br><span class="hljs-comment"> * </span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> text 被编码的文本</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 编码后的字符</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">encode</span><span class="hljs-params">(String text)</span><br> {<br> <span class="hljs-keyword">if</span> (StringUtils.isEmpty(text))<br> {<br> <span class="hljs-keyword">return</span> StringUtils.EMPTY;<br> }<br><br> <span class="hljs-keyword">final</span> <span class="hljs-type">StringBuilder</span> <span class="hljs-variable">tmp</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>(text.length() * <span class="hljs-number">6</span>);<br> <span class="hljs-type">char</span> c;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < text.length(); i++)<br> {<br> c = text.charAt(i);<br> <span class="hljs-keyword">if</span> (c < <span class="hljs-number">256</span>)<br> {<br> tmp.append(<span class="hljs-string">"%"</span>);<br> <span class="hljs-keyword">if</span> (c < <span class="hljs-number">16</span>)<br> {<br> tmp.append(<span class="hljs-string">"0"</span>);<br> }<br> tmp.append(Integer.toString(c, <span class="hljs-number">16</span>));<br> }<br> <span class="hljs-keyword">else</span><br> {<br> tmp.append(<span class="hljs-string">"%u"</span>);<br> <span class="hljs-keyword">if</span> (c <= <span class="hljs-number">0xfff</span>)<br> {<br> <span class="hljs-comment">// issue#I49JU8@Gitee</span><br> tmp.append(<span class="hljs-string">"0"</span>);<br> }<br> tmp.append(Integer.toString(c, <span class="hljs-number">16</span>));<br> }<br> }<br> <span class="hljs-keyword">return</span> tmp.toString();<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Escape解码</span><br><span class="hljs-comment"> * </span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> content 被转义的内容</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 解码后的字符串</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">decode</span><span class="hljs-params">(String content)</span><br> {<br> <span class="hljs-keyword">if</span> (StringUtils.isEmpty(content))<br> {<br> <span class="hljs-keyword">return</span> content;<br> }<br><br> <span class="hljs-type">StringBuilder</span> <span class="hljs-variable">tmp</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>(content.length());<br> <span class="hljs-type">int</span> <span class="hljs-variable">lastPos</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, pos = <span class="hljs-number">0</span>;<br> <span class="hljs-type">char</span> ch;<br> <span class="hljs-keyword">while</span> (lastPos < content.length())<br> {<br> pos = content.indexOf(<span class="hljs-string">"%"</span>, lastPos);<br> <span class="hljs-keyword">if</span> (pos == lastPos)<br> {<br> <span class="hljs-keyword">if</span> (content.charAt(pos + <span class="hljs-number">1</span>) == <span class="hljs-string">'u'</span>)<br> {<br> ch = (<span class="hljs-type">char</span>) Integer.parseInt(content.substring(pos + <span class="hljs-number">2</span>, pos + <span class="hljs-number">6</span>), <span class="hljs-number">16</span>);<br> tmp.append(ch);<br> lastPos = pos + <span class="hljs-number">6</span>;<br> }<br> <span class="hljs-keyword">else</span><br> {<br> ch = (<span class="hljs-type">char</span>) Integer.parseInt(content.substring(pos + <span class="hljs-number">1</span>, pos + <span class="hljs-number">3</span>), <span class="hljs-number">16</span>);<br> tmp.append(ch);<br> lastPos = pos + <span class="hljs-number">3</span>;<br> }<br> }<br> <span class="hljs-keyword">else</span><br> {<br> <span class="hljs-keyword">if</span> (pos == -<span class="hljs-number">1</span>)<br> {<br> tmp.append(content.substring(lastPos));<br> lastPos = content.length();<br> }<br> <span class="hljs-keyword">else</span><br> {<br> tmp.append(content.substring(lastPos, pos));<br> lastPos = pos;<br> }<br> }<br> }<br> <span class="hljs-keyword">return</span> tmp.toString();<br> }<br>}<br><br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HTMLFilter</span><br>{<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * regex flag union representing /si modifiers in php</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">REGEX_FLAGS_SI</span> <span class="hljs-operator">=</span> Pattern.CASE_INSENSITIVE | Pattern.DOTALL;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_COMMENTS</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"<!--(.*?)-->"</span>, Pattern.DOTALL);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_COMMENT</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"^!--(.*)--$"</span>, REGEX_FLAGS_SI);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_TAGS</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"<(.*?)>"</span>, Pattern.DOTALL);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_END_TAG</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"^/([a-z0-9]+)"</span>, REGEX_FLAGS_SI);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_START_TAG</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"^([a-z0-9]+)(.*?)(/?)$"</span>, REGEX_FLAGS_SI);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_QUOTED_ATTRIBUTES</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"([a-z0-9]+)=([\"'])(.*?)\\2"</span>, REGEX_FLAGS_SI);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_UNQUOTED_ATTRIBUTES</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"([a-z0-9]+)(=)([^\"\\s']+)"</span>, REGEX_FLAGS_SI);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_PROTOCOL</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"^([^:]+):"</span>, REGEX_FLAGS_SI);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_ENTITY</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"&#(\\d+);?"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_ENTITY_UNICODE</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"&#x([0-9a-f]+);?"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_ENCODE</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"%([0-9a-f]{2});?"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_VALID_ENTITIES</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"&([^&;]*)(?=(;|&|$))"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_VALID_QUOTES</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"(>|^)([^<]+?)(<|$)"</span>, Pattern.DOTALL);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_END_ARROW</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"^>"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_BODY_TO_END</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"<([^>]*?)(?=<|$)"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_XML_CONTENT</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"(^|>)([^<]*?)(?=>)"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_STRAY_LEFT_ARROW</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"<([^>]*?)(?=<|$)"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_STRAY_RIGHT_ARROW</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"(^|>)([^<]*?)(?=>)"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_AMP</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"&"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_QUOTE</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"\""</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_LEFT_ARROW</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"<"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_RIGHT_ARROW</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">">"</span>);<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Pattern</span> <span class="hljs-variable">P_BOTH_ARROWS</span> <span class="hljs-operator">=</span> Pattern.compile(<span class="hljs-string">"<>"</span>);<br><br> <span class="hljs-comment">// @xxx could grow large... maybe use sesat's ReferenceMap</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ConcurrentHashMap</span><>();<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ConcurrentHashMap</span><>();<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * set of allowed html elements, along with allowed attributes for each element</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Map<String, List<String>> vAllowed;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * counts of open tags for each (allowable) html element</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Map<String, Integer> vTagCounts = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span><>();<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * html elements which must always be self-closing (e.g. "<img />")</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String[] vSelfClosingTags;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * html elements which must always have separate opening and closing tags (e.g. "<b></b>")</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String[] vNeedClosingTags;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * set of disallowed html elements</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String[] vDisallowed;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * attributes which should be checked for valid protocols</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String[] vProtocolAtts;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * allowed protocols</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String[] vAllowedProtocols;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String[] vRemoveBlanks;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * entities allowed within html markup</span><br><span class="hljs-comment"> **/</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String[] vAllowedEntities;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * flag determining whether comments are allowed in input String.</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">boolean</span> stripComment;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">boolean</span> encodeQuotes;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"</span><br><span class="hljs-comment"> * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">boolean</span> alwaysMakeTags;<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Default constructor.</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">HTMLFilter</span><span class="hljs-params">()</span><br> {<br> vAllowed = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span><>();<br><br> <span class="hljs-keyword">final</span> ArrayList<String> a_atts = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br> a_atts.add(<span class="hljs-string">"href"</span>);<br> a_atts.add(<span class="hljs-string">"target"</span>);<br> vAllowed.put(<span class="hljs-string">"a"</span>, a_atts);<br><br> <span class="hljs-keyword">final</span> ArrayList<String> img_atts = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br> img_atts.add(<span class="hljs-string">"src"</span>);<br> img_atts.add(<span class="hljs-string">"width"</span>);<br> img_atts.add(<span class="hljs-string">"height"</span>);<br> img_atts.add(<span class="hljs-string">"alt"</span>);<br> vAllowed.put(<span class="hljs-string">"img"</span>, img_atts);<br><br> <span class="hljs-keyword">final</span> ArrayList<String> no_atts = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br> vAllowed.put(<span class="hljs-string">"b"</span>, no_atts);<br> vAllowed.put(<span class="hljs-string">"strong"</span>, no_atts);<br> vAllowed.put(<span class="hljs-string">"i"</span>, no_atts);<br> vAllowed.put(<span class="hljs-string">"em"</span>, no_atts);<br><br> vSelfClosingTags = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[] { <span class="hljs-string">"img"</span> };<br> vNeedClosingTags = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[] { <span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"strong"</span>, <span class="hljs-string">"i"</span>, <span class="hljs-string">"em"</span> };<br> vDisallowed = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[] {};<br> vAllowedProtocols = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[] { <span class="hljs-string">"http"</span>, <span class="hljs-string">"mailto"</span>, <span class="hljs-string">"https"</span> }; <span class="hljs-comment">// no ftp.</span><br> vProtocolAtts = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[] { <span class="hljs-string">"src"</span>, <span class="hljs-string">"href"</span> };<br> vRemoveBlanks = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[] { <span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"strong"</span>, <span class="hljs-string">"i"</span>, <span class="hljs-string">"em"</span> };<br> vAllowedEntities = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[] { <span class="hljs-string">"amp"</span>, <span class="hljs-string">"gt"</span>, <span class="hljs-string">"lt"</span>, <span class="hljs-string">"quot"</span> };<br> stripComment = <span class="hljs-literal">true</span>;<br> encodeQuotes = <span class="hljs-literal">true</span>;<br> alwaysMakeTags = <span class="hljs-literal">false</span>;<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Map-parameter configurable constructor.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> conf map containing configuration. keys match field names.</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-meta">@SuppressWarnings("unchecked")</span><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">HTMLFilter</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Map<String, Object> conf)</span><br> {<br><br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vAllowed"</span>) : <span class="hljs-string">"configuration requires vAllowed"</span>;<br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vSelfClosingTags"</span>) : <span class="hljs-string">"configuration requires vSelfClosingTags"</span>;<br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vNeedClosingTags"</span>) : <span class="hljs-string">"configuration requires vNeedClosingTags"</span>;<br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vDisallowed"</span>) : <span class="hljs-string">"configuration requires vDisallowed"</span>;<br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vAllowedProtocols"</span>) : <span class="hljs-string">"configuration requires vAllowedProtocols"</span>;<br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vProtocolAtts"</span>) : <span class="hljs-string">"configuration requires vProtocolAtts"</span>;<br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vRemoveBlanks"</span>) : <span class="hljs-string">"configuration requires vRemoveBlanks"</span>;<br> <span class="hljs-keyword">assert</span> conf.containsKey(<span class="hljs-string">"vAllowedEntities"</span>) : <span class="hljs-string">"configuration requires vAllowedEntities"</span>;<br><br> vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get(<span class="hljs-string">"vAllowed"</span>));<br> vSelfClosingTags = (String[]) conf.get(<span class="hljs-string">"vSelfClosingTags"</span>);<br> vNeedClosingTags = (String[]) conf.get(<span class="hljs-string">"vNeedClosingTags"</span>);<br> vDisallowed = (String[]) conf.get(<span class="hljs-string">"vDisallowed"</span>);<br> vAllowedProtocols = (String[]) conf.get(<span class="hljs-string">"vAllowedProtocols"</span>);<br> vProtocolAtts = (String[]) conf.get(<span class="hljs-string">"vProtocolAtts"</span>);<br> vRemoveBlanks = (String[]) conf.get(<span class="hljs-string">"vRemoveBlanks"</span>);<br> vAllowedEntities = (String[]) conf.get(<span class="hljs-string">"vAllowedEntities"</span>);<br> stripComment = conf.containsKey(<span class="hljs-string">"stripComment"</span>) ? (Boolean) conf.get(<span class="hljs-string">"stripComment"</span>) : <span class="hljs-literal">true</span>;<br> encodeQuotes = conf.containsKey(<span class="hljs-string">"encodeQuotes"</span>) ? (Boolean) conf.get(<span class="hljs-string">"encodeQuotes"</span>) : <span class="hljs-literal">true</span>;<br> alwaysMakeTags = conf.containsKey(<span class="hljs-string">"alwaysMakeTags"</span>) ? (Boolean) conf.get(<span class="hljs-string">"alwaysMakeTags"</span>) : <span class="hljs-literal">true</span>;<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">reset</span><span class="hljs-params">()</span><br> {<br> vTagCounts.clear();<br> }<br><br> <span class="hljs-comment">// ---------------------------------------------------------------</span><br> <span class="hljs-comment">// my versions of some PHP library functions</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">chr</span><span class="hljs-params">(<span class="hljs-keyword">final</span> <span class="hljs-type">int</span> decimal)</span><br> {<br> <span class="hljs-keyword">return</span> String.valueOf((<span class="hljs-type">char</span>) decimal);<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">htmlSpecialChars</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String s)</span><br> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> s;<br> result = regexReplace(P_AMP, <span class="hljs-string">"&amp;"</span>, result);<br> result = regexReplace(P_QUOTE, <span class="hljs-string">"&quot;"</span>, result);<br> result = regexReplace(P_LEFT_ARROW, <span class="hljs-string">"&lt;"</span>, result);<br> result = regexReplace(P_RIGHT_ARROW, <span class="hljs-string">"&gt;"</span>, result);<br> <span class="hljs-keyword">return</span> result;<br> }<br><br> <span class="hljs-comment">// ---------------------------------------------------------------</span><br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * given a user submitted input String, filter out any invalid or restricted html.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> input text (i.e. submitted by a user) than may contain html</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> "clean" version of input, with only valid, whitelisted html elements allowed</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">filter</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String input)</span><br> {<br> reset();<br> <span class="hljs-type">String</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span> input;<br><br> s = escapeComments(s);<br><br> s = balanceHTML(s);<br><br> s = checkTags(s);<br><br> s = processRemoveBlanks(s);<br><br> <span class="hljs-comment">// s = validateEntities(s);</span><br><br> <span class="hljs-keyword">return</span> s;<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isAlwaysMakeTags</span><span class="hljs-params">()</span><br> {<br> <span class="hljs-keyword">return</span> alwaysMakeTags;<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isStripComments</span><span class="hljs-params">()</span><br> {<br> <span class="hljs-keyword">return</span> stripComment;<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">escapeComments</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String s)</span><br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> P_COMMENTS.matcher(s);<br> <span class="hljs-keyword">final</span> <span class="hljs-type">StringBuffer</span> <span class="hljs-variable">buf</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuffer</span>();<br> <span class="hljs-keyword">if</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">match</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>); <span class="hljs-comment">// (.*?)</span><br> m.appendReplacement(buf, Matcher.quoteReplacement(<span class="hljs-string">"<!--"</span> + htmlSpecialChars(match) + <span class="hljs-string">"-->"</span>));<br> }<br> m.appendTail(buf);<br><br> <span class="hljs-keyword">return</span> buf.toString();<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">balanceHTML</span><span class="hljs-params">(String s)</span><br> {<br> <span class="hljs-keyword">if</span> (alwaysMakeTags)<br> {<br> <span class="hljs-comment">//</span><br> <span class="hljs-comment">// try and form html</span><br> <span class="hljs-comment">//</span><br> s = regexReplace(P_END_ARROW, <span class="hljs-string">""</span>, s);<br> <span class="hljs-comment">// 不追加结束标签</span><br> s = regexReplace(P_BODY_TO_END, <span class="hljs-string">"<$1>"</span>, s);<br> s = regexReplace(P_XML_CONTENT, <span class="hljs-string">"$1<$2"</span>, s);<br><br> }<br> <span class="hljs-keyword">else</span><br> {<br> <span class="hljs-comment">//</span><br> <span class="hljs-comment">// escape stray brackets</span><br> <span class="hljs-comment">//</span><br> s = regexReplace(P_STRAY_LEFT_ARROW, <span class="hljs-string">"&lt;$1"</span>, s);<br> s = regexReplace(P_STRAY_RIGHT_ARROW, <span class="hljs-string">"$1$2&gt;<"</span>, s);<br><br> <span class="hljs-comment">//</span><br> <span class="hljs-comment">// the last regexp causes '<>' entities to appear</span><br> <span class="hljs-comment">// (we need to do a lookahead assertion so that the last bracket can</span><br> <span class="hljs-comment">// be used in the next pass of the regexp)</span><br> <span class="hljs-comment">//</span><br> s = regexReplace(P_BOTH_ARROWS, <span class="hljs-string">""</span>, s);<br> }<br><br> <span class="hljs-keyword">return</span> s;<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">checkTags</span><span class="hljs-params">(String s)</span><br> {<br> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> P_TAGS.matcher(s);<br><br> <span class="hljs-keyword">final</span> <span class="hljs-type">StringBuffer</span> <span class="hljs-variable">buf</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuffer</span>();<br> <span class="hljs-keyword">while</span> (m.find())<br> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">replaceStr</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>);<br> replaceStr = processTag(replaceStr);<br> m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));<br> }<br> m.appendTail(buf);<br><br> <span class="hljs-comment">// these get tallied in processTag</span><br> <span class="hljs-comment">// (remember to reset before subsequent calls to filter method)</span><br> <span class="hljs-keyword">final</span> <span class="hljs-type">StringBuilder</span> <span class="hljs-variable">sBuilder</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>(buf.toString());<br> <span class="hljs-keyword">for</span> (String key : vTagCounts.keySet())<br> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">ii</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; ii < vTagCounts.get(key); ii++)<br> {<br> sBuilder.append(<span class="hljs-string">"</"</span>).append(key).append(<span class="hljs-string">">"</span>);<br> }<br> }<br> s = sBuilder.toString();<br><br> <span class="hljs-keyword">return</span> s;<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">processRemoveBlanks</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String s)</span><br> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> s;<br> <span class="hljs-keyword">for</span> (String tag : vRemoveBlanks)<br> {<br> <span class="hljs-keyword">if</span> (!P_REMOVE_PAIR_BLANKS.containsKey(tag))<br> {<br> P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile(<span class="hljs-string">"<"</span> + tag + <span class="hljs-string">"(\\s[^>]*)?></"</span> + tag + <span class="hljs-string">">"</span>));<br> }<br> result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), <span class="hljs-string">""</span>, result);<br> <span class="hljs-keyword">if</span> (!P_REMOVE_SELF_BLANKS.containsKey(tag))<br> {<br> P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile(<span class="hljs-string">"<"</span> + tag + <span class="hljs-string">"(\\s[^>]*)?/>"</span>));<br> }<br> result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), <span class="hljs-string">""</span>, result);<br> }<br><br> <span class="hljs-keyword">return</span> result;<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">regexReplace</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Pattern regex_pattern, <span class="hljs-keyword">final</span> String replacement, <span class="hljs-keyword">final</span> String s)</span><br> {<br> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> regex_pattern.matcher(s);<br> <span class="hljs-keyword">return</span> m.replaceAll(replacement);<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">processTag</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String s)</span><br> {<br> <span class="hljs-comment">// ending tags</span><br> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> P_END_TAG.matcher(s);<br> <span class="hljs-keyword">if</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>).toLowerCase();<br> <span class="hljs-keyword">if</span> (allowed(name))<br> {<br> <span class="hljs-keyword">if</span> (!inArray(name, vSelfClosingTags))<br> {<br> <span class="hljs-keyword">if</span> (vTagCounts.containsKey(name))<br> {<br> vTagCounts.put(name, vTagCounts.get(name) - <span class="hljs-number">1</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"</"</span> + name + <span class="hljs-string">">"</span>;<br> }<br> }<br> }<br> }<br><br> <span class="hljs-comment">// starting tags</span><br> m = P_START_TAG.matcher(s);<br> <span class="hljs-keyword">if</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>).toLowerCase();<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">body</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">2</span>);<br> <span class="hljs-type">String</span> <span class="hljs-variable">ending</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">3</span>);<br><br> <span class="hljs-comment">// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );</span><br> <span class="hljs-keyword">if</span> (allowed(name))<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">StringBuilder</span> <span class="hljs-variable">params</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>();<br><br> <span class="hljs-keyword">final</span> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m2</span> <span class="hljs-operator">=</span> P_QUOTED_ATTRIBUTES.matcher(body);<br> <span class="hljs-keyword">final</span> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m3</span> <span class="hljs-operator">=</span> P_UNQUOTED_ATTRIBUTES.matcher(body);<br> <span class="hljs-keyword">final</span> List<String> paramNames = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br> <span class="hljs-keyword">final</span> List<String> paramValues = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br> <span class="hljs-keyword">while</span> (m2.find())<br> {<br> paramNames.add(m2.group(<span class="hljs-number">1</span>)); <span class="hljs-comment">// ([a-z0-9]+)</span><br> paramValues.add(m2.group(<span class="hljs-number">3</span>)); <span class="hljs-comment">// (.*?)</span><br> }<br> <span class="hljs-keyword">while</span> (m3.find())<br> {<br> paramNames.add(m3.group(<span class="hljs-number">1</span>)); <span class="hljs-comment">// ([a-z0-9]+)</span><br> paramValues.add(m3.group(<span class="hljs-number">3</span>)); <span class="hljs-comment">// ([^\"\\s']+)</span><br> }<br><br> String paramName, paramValue;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">ii</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; ii < paramNames.size(); ii++)<br> {<br> paramName = paramNames.get(ii).toLowerCase();<br> paramValue = paramValues.get(ii);<br><br> <span class="hljs-comment">// debug( "paramName='" + paramName + "'" );</span><br> <span class="hljs-comment">// debug( "paramValue='" + paramValue + "'" );</span><br> <span class="hljs-comment">// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );</span><br><br> <span class="hljs-keyword">if</span> (allowedAttribute(name, paramName))<br> {<br> <span class="hljs-keyword">if</span> (inArray(paramName, vProtocolAtts))<br> {<br> paramValue = processParamProtocol(paramValue);<br> }<br> params.append(<span class="hljs-string">' '</span>).append(paramName).append(<span class="hljs-string">"=\\\""</span>).append(paramValue).append(<span class="hljs-string">"\\\""</span>);<br> }<br> }<br><br> <span class="hljs-keyword">if</span> (inArray(name, vSelfClosingTags))<br> {<br> ending = <span class="hljs-string">" /"</span>;<br> }<br><br> <span class="hljs-keyword">if</span> (inArray(name, vNeedClosingTags))<br> {<br> ending = <span class="hljs-string">""</span>;<br> }<br><br> <span class="hljs-keyword">if</span> (ending == <span class="hljs-literal">null</span> || ending.length() < <span class="hljs-number">1</span>)<br> {<br> <span class="hljs-keyword">if</span> (vTagCounts.containsKey(name))<br> {<br> vTagCounts.put(name, vTagCounts.get(name) + <span class="hljs-number">1</span>);<br> }<br> <span class="hljs-keyword">else</span><br> {<br> vTagCounts.put(name, <span class="hljs-number">1</span>);<br> }<br> }<br> <span class="hljs-keyword">else</span><br> {<br> ending = <span class="hljs-string">" /"</span>;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"<"</span> + name + params + ending + <span class="hljs-string">">"</span>;<br> }<br> <span class="hljs-keyword">else</span><br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;<br> }<br> }<br><br> <span class="hljs-comment">// comments</span><br> m = P_COMMENT.matcher(s);<br> <span class="hljs-keyword">if</span> (!stripComment && m.find())<br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"<"</span> + m.group() + <span class="hljs-string">">"</span>;<br> }<br><br> <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">processParamProtocol</span><span class="hljs-params">(String s)</span><br> {<br> s = decodeEntities(s);<br> <span class="hljs-keyword">final</span> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> P_PROTOCOL.matcher(s);<br> <span class="hljs-keyword">if</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">protocol</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>);<br> <span class="hljs-keyword">if</span> (!inArray(protocol, vAllowedProtocols))<br> {<br> <span class="hljs-comment">// bad protocol, turn into local anchor link instead</span><br> s = <span class="hljs-string">"#"</span> + s.substring(protocol.length() + <span class="hljs-number">1</span>);<br> <span class="hljs-keyword">if</span> (s.startsWith(<span class="hljs-string">"#//"</span>))<br> {<br> s = <span class="hljs-string">"#"</span> + s.substring(<span class="hljs-number">3</span>);<br> }<br> }<br> }<br><br> <span class="hljs-keyword">return</span> s;<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">decodeEntities</span><span class="hljs-params">(String s)</span><br> {<br> <span class="hljs-type">StringBuffer</span> <span class="hljs-variable">buf</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuffer</span>();<br><br> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> P_ENTITY.matcher(s);<br> <span class="hljs-keyword">while</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">match</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>);<br> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">decimal</span> <span class="hljs-operator">=</span> Integer.decode(match).intValue();<br> m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));<br> }<br> m.appendTail(buf);<br> s = buf.toString();<br><br> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuffer</span>();<br> m = P_ENTITY_UNICODE.matcher(s);<br> <span class="hljs-keyword">while</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">match</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>);<br> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">decimal</span> <span class="hljs-operator">=</span> Integer.valueOf(match, <span class="hljs-number">16</span>).intValue();<br> m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));<br> }<br> m.appendTail(buf);<br> s = buf.toString();<br><br> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuffer</span>();<br> m = P_ENCODE.matcher(s);<br> <span class="hljs-keyword">while</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">match</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>);<br> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">decimal</span> <span class="hljs-operator">=</span> Integer.valueOf(match, <span class="hljs-number">16</span>).intValue();<br> m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));<br> }<br> m.appendTail(buf);<br> s = buf.toString();<br><br> s = validateEntities(s);<br> <span class="hljs-keyword">return</span> s;<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">validateEntities</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String s)</span><br> {<br> <span class="hljs-type">StringBuffer</span> <span class="hljs-variable">buf</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuffer</span>();<br><br> <span class="hljs-comment">// validate entities throughout the string</span><br> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> P_VALID_ENTITIES.matcher(s);<br> <span class="hljs-keyword">while</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">one</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>); <span class="hljs-comment">// ([^&;]*)</span><br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">two</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">2</span>); <span class="hljs-comment">// (?=(;|&|$))</span><br> m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));<br> }<br> m.appendTail(buf);<br><br> <span class="hljs-keyword">return</span> encodeQuotes(buf.toString());<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">encodeQuotes</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String s)</span><br> {<br> <span class="hljs-keyword">if</span> (encodeQuotes)<br> {<br> <span class="hljs-type">StringBuffer</span> <span class="hljs-variable">buf</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuffer</span>();<br> <span class="hljs-type">Matcher</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> P_VALID_QUOTES.matcher(s);<br> <span class="hljs-keyword">while</span> (m.find())<br> {<br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">one</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">1</span>); <span class="hljs-comment">// (>|^)</span><br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">two</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">2</span>); <span class="hljs-comment">// ([^<]+?)</span><br> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">three</span> <span class="hljs-operator">=</span> m.group(<span class="hljs-number">3</span>); <span class="hljs-comment">// (<|$)</span><br> <span class="hljs-comment">// 不替换双引号为&quot;,防止json格式无效 regexReplace(P_QUOTE, "&quot;", two)</span><br> m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));<br> }<br> m.appendTail(buf);<br> <span class="hljs-keyword">return</span> buf.toString();<br> }<br> <span class="hljs-keyword">else</span><br> {<br> <span class="hljs-keyword">return</span> s;<br> }<br> }<br><br> <span class="hljs-keyword">private</span> String <span class="hljs-title function_">checkEntity</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String preamble, <span class="hljs-keyword">final</span> String term)</span><br> {<br><br> <span class="hljs-keyword">return</span> <span class="hljs-string">";"</span>.equals(term) && isValidEntity(preamble) ? <span class="hljs-string">'&'</span> + preamble : <span class="hljs-string">"&amp;"</span> + preamble;<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isValidEntity</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String entity)</span><br> {<br> <span class="hljs-keyword">return</span> inArray(entity, vAllowedEntities);<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">inArray</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String s, <span class="hljs-keyword">final</span> String[] array)</span><br> {<br> <span class="hljs-keyword">for</span> (String item : array)<br> {<br> <span class="hljs-keyword">if</span> (item != <span class="hljs-literal">null</span> && item.equals(s))<br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br> }<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">allowed</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String name)</span><br> {<br> <span class="hljs-keyword">return</span> (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">allowedAttribute</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String name, <span class="hljs-keyword">final</span> String paramName)</span><br> {<br> <span class="hljs-keyword">return</span> allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));<br> }<br>}<br></code></pre></td></tr></table></figure><p>前后端组合使用,可解决xss问题</p><h2 id="安全无小事!!!"><a href="#安全无小事!!!" class="headerlink" title="安全无小事!!!"></a>安全无小事!!!</h2><p><a href="https://security.tencent.com/index.php/blog/msg/53">存储型XSS漏洞解决方案</a></p>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>spring</tag>
<tag>安全</tag>
</tags>
</entry>
<entry>
<title>网站任意文件下载或读取</title>
<link href="/2023/06/20/safe-http-any-file-download/"/>
<url>/2023/06/20/safe-http-any-file-download/</url>
<content type="html"><![CDATA[<p>项目中下载方法没有安全校验,导致脚本注入,可以下载服务器任意路径下的文件。<br>对此总结了一下修复办法,方便大家参考,修复。</p><p>以下是漏洞的一些内容:</p><h2 id="安全漏洞描述"><a href="#安全漏洞描述" class="headerlink" title="安全漏洞描述"></a>安全漏洞描述</h2><p>任意文件下载或读取漏洞主要是由于应用系统在提供文件下载或读取功能时,在文件路径参数中直接指定文件路径的同时并没有对文件路径的合法性进行校验,<br>导致攻击者可通过目录跳转(..\或../)的方式下载或读取到原始指定路径之外的文件。<br>攻击者最终可通过该漏洞下载或读取系统上的任意文件,如数据库文件、应用系统源代码、密码配置信息等重要敏感信息,造成系统的敏感信息泄露。</p><h3 id="重现方法"><a href="#重现方法" class="headerlink" title="重现方法"></a>重现方法</h3><p>对存在文件下载或文件读取功能的页面进行测试,查看所提交的参数中是否包含文件名或文件目录,尝试提交参数值查看是否可下载或读取其他目录的文件内容; </p><p>如:</p><p>原始下载功能路径为<br><a href="http://www.example.com/donwload.jsp?filename=test123456789.pdf">http://www.example.com/donwload.jsp?filename=test123456789.pdf</a></p><p>其中文件路径参数为filename,通过../对路径进行跳转尝试下载其他目录下的文件,修改filename参数为../../WEB-INF/web.xml尝试下载JSP网站的配置文件(测试过程中需适当增加../跳转字符串);如提交<br><a href="http://www.example.com/donwload.jsp?filename=../../WEB-INF/web.xml">http://www.example.com/donwload.jsp?filename=../../WEB-INF/web.xml</a></p><p>查看是否成功下载web.xml文件。</p><p>另一种情况如下:<br><a href="http://www.example.com/donwload.jsp?filepath=uploadfile&filename=test123.pdf">http://www.example.com/donwload.jsp?filepath=uploadfile&filename=test123.pdf</a><br>该功能通过filepath以及filename指定下载目录以及下载文件名,可修改filepath参数值进行路径跳转,<br>同时修改filename值指定文件名;如提交<br><a href="http://www.example.com/donwload.jsp?filepath=../../WEB-INF&filename=web.xml">http://www.example.com/donwload.jsp?filepath=../../WEB-INF&filename=web.xml</a></p><p>查看是否成功下载web.xml文件。</p><h2 id="风险等级:高"><a href="#风险等级:高" class="headerlink" title="风险等级:高"></a>风险等级:高</h2><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>知道了问题,一般可以通过在代码层级增加关键字符过滤来修复。<br>比如:</p><ol><li>对path参数进行过滤,依次过滤“.”、“..”、“/”、“”等字符。</li><li>或者对于下载文件的目录做好限制,只能下载指定目录下的文件,</li><li>或者将要下载的资源文件路径存入数据库,附件下载时指定数据库中的id即可,id即对应的资源。<br>服务器级建议</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs code">private Pair<Boolean,String> checkFileAttack(String filePath) {<br> if (StringUtils.isEmpty(filePath)) {<br> return Pair.of(false, "文件名为空");<br> }<br><br> if (filePath.startsWith("/")) {<br> return Pair.of(false, "文件名不能以/开始");<br> }<br><br> // 禁止文件名中包含 “%00”,“..”,“./”,“#/” ,"~/" , "^/" Pattern pattern = Pattern.compile("(\\.\\.|~/|^/|\\./|#/|%00)");<br> if (pattern.matcher(filePath).matches()) {<br> return Pair.of(false, "文件名不合法");<br> }<br><br> if (!ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(filePath)))<br> {<br> return Pair.of(false, "非法格式");<br> }<br><br> return Pair.of(true, "校验成功");<br>}<br></code></pre></td></tr></table></figure><p>或者,在服务部署的时候,通过下面的目录固化部署路径,令web请求不能逃逸到整个服务器。</p><ul><li>使用chroot 命令,固化服务路径</li></ul><h2 id="安全无小事!!!"><a href="#安全无小事!!!" class="headerlink" title="安全无小事!!!"></a>安全无小事!!!</h2>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>spring</tag>
<tag>安全</tag>
</tags>
</entry>
<entry>
<title>URL存在http host头攻击漏洞的解决办法</title>
<link href="/2023/06/20/safe-http-url-header-attack/"/>
<url>/2023/06/20/safe-http-url-header-attack/</url>
<content type="html"><![CDATA[<p>项目中遇到http host头攻击的解决办法,支持nginx,apache 两种办法。</p><p>以下是漏洞的一些内容:</p><h2 id="安全漏洞描述"><a href="#安全漏洞描述" class="headerlink" title="安全漏洞描述"></a>安全漏洞描述</h2><p>为了方便的获得网站域名,开发人员一般依赖于HTTP Host header。例如,在php里用_SERVER[“HTTP_HOST”]。<br>但是这个header是不可信赖的,如果应用程序没有对host header值进行处理,就有可能造成恶意代码的传入。</p><p><img src="/images/tech/safe/safe-http-host-attack.png" alt="http-host-attack"></p><h2 id="风险等级:高"><a href="#风险等级:高" class="headerlink" title="风险等级:高"></a>风险等级:高</h2><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>web应用程序应该使用SERVER_NAME而不是host header。 在Apache和Nginx里可以通过设置一个虚拟机来记录所有的非法host header。<br>在Nginx里还可以通过指定一个SERVER_NAME名单,Apache也可以通过指定一个SERVER_NAME名单并开启UseCanonicalName选项。</p><p>本质上就是在服务侧鉴定请求来源, 非法来源直接过滤掉。</p><h3 id="nginx-解决方案"><a href="#nginx-解决方案" class="headerlink" title="nginx 解决方案"></a>nginx 解决方案</h3><p>打开nginx的网站配置文件,一般为 <code>/etc/nginx/conf.d/xxx.conf</code><br>添加如下配置</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-section">server</span> {<br> <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;<br> <span class="hljs-attribute">server_name</span> <span class="hljs-number">127.0.0.1</span>;<br> <span class="hljs-comment"># 防止http host 攻击的配置: 仅允许 指定域名访问,其他域名返回403</span><br> <span class="hljs-attribute">if</span> (<span class="hljs-variable">$http_Host</span> !<span class="hljs-regexp">~* ^domain.com|127.0.0.1:80$)</span> {<br> <span class="hljs-attribute">return</span> <span class="hljs-number">403</span>;<br> }<br> <span class="hljs-comment"># 其他配置</span><br>}<br></code></pre></td></tr></table></figure><p>重启nginx即可</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">nginx -s reload<br></code></pre></td></tr></table></figure><h3 id="Apache解决方案"><a href="#Apache解决方案" class="headerlink" title="Apache解决方案"></a>Apache解决方案</h3><p>打开Apache的配置 , 一般为 <code>/conf/httpd.conf</code><br>添加如下配置:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs conf">RewriteEngine on<br>RewriteCond %{HTTP_HOST} !^www.domain.com$ [NC]<br>RewriteRule ^(.*)$ /403.html<br><br></code></pre></td></tr></table></figure><p>重启apache即可</p><h2 id="安全无小事!!!"><a href="#安全无小事!!!" class="headerlink" title="安全无小事!!!"></a>安全无小事!!!</h2><p><a href="https://m.freebuf.com/articles/web/336709.html">Web漏洞之HOST头攻击</a></p><p><a href="https://www.wangan.com/p/7fy7475771d68df5">host头攻击</a></p>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>nginx</tag>
<tag>安全</tag>
<tag>apache</tag>
</tags>
</entry>
<entry>
<title>中间件版本号泄露问题修复</title>
<link href="/2023/06/19/safe-dev-info-leak/"/>
<url>/2023/06/19/safe-dev-info-leak/</url>
<content type="html"><![CDATA[<p>开发过程中的一些信息带到了线上,要及时修复,不如坏人就顺藤摸瓜找来了。</p><p>以下是漏洞的一些内容:</p><h2 id="安全漏洞描述"><a href="#安全漏洞描述" class="headerlink" title="安全漏洞描述"></a>安全漏洞描述</h2><p>信息泄露主要是由于开发人员或运维管理人员的疏忽所导致。如未及时删除调试页面、未关闭程序调试功能、未屏蔽程序错误信息、备份文件未删除、数据库备份文件未删除、未屏蔽敏感数据信息等多个方面所导致的不同严重程度的信息泄露。攻击者可通过所掌握的信息进一步分析攻击目标,从而有效发起下一步的有效攻击。</p><p>常见的问题有:</p><ol><li>swagger 接口未关闭</li><li>druid监控台未关闭</li><li>等</li></ol><h2 id="风险等级:低"><a href="#风险等级:低" class="headerlink" title="风险等级:低"></a>风险等级:低</h2><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>关闭相关接口的线上权限,或增加权限控制 </p><p>1,swagger 关闭入口的方式<br>方法a: 使用注解 @Value() 推荐使用, 然后在不同的配置文件里指定开关</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs code">@Value("${swagger.enabled}")<br>private boolean enabled;<br><br>@Bean<br>public Docket createRestApi()<br>{<br> return new Docket(DocumentationType.V1)<br> // 是否启用Swagger <br> .enable(enabled)<br> // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) <br> .apiInfo(apiInfo())<br> // 设置哪些接口暴露给Swagger展示 <br> .select()<br> // 扫描所有有注解的api,用这种方式更灵活 <br> .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))<br> // 扫描指定包中的swagger注解 <br> // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) <br> // 扫描所有 .apis(RequestHandlerSelectors.any()) <br> .paths(PathSelectors.any())<br> .build()<br> /* 设置安全模式,swagger可以设置访问token */ <br> .securitySchemes(securitySchemes())<br> .securityContexts(securityContexts())<br> .pathMapping(pathMapping);<br>}<br></code></pre></td></tr></table></figure><p>方法b: 使用注解@Profile({“dev”,“test”}) 表示在开发或测试环境开启,而在生产关闭<br><img src="/images/tech/safe/swagger-switch2.png" alt="swagger-switch"><br>其实ab 的方式是一样的,都是不同的环境开关不同。</p><p>2, druid关闭监控台的方式</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">spring.datasource.druid.webStatFilter.enabled</span>=<span class="hljs-string">false</span><br><span class="hljs-attr">spring.datasource.druid.statViewServlet.enabled</span>=<span class="hljs-string">false</span><br></code></pre></td></tr></table></figure><p>3, 一些其他接口增加权限认证<br>通过spring的httpSecurity 增加路径鉴权</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs code">protected void configure(HttpSecurity httpSecurity) throws Exception {<br> // something<br> // druid 接口要鉴权<br> httpSecurity..antMatchers("/druid/**").authenticated()<br> // something<br>}<br></code></pre></td></tr></table></figure><h2 id="安全无小事!!!"><a href="#安全无小事!!!" class="headerlink" title="安全无小事!!!"></a>安全无小事!!!</h2>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>spring</tag>
<tag>安全</tag>
</tags>
</entry>
<entry>
<title>AntPathMatcher VS PathPattern</title>
<link href="/2023/05/21/spring-spring-path-matcher/"/>
<url>/2023/05/21/spring-spring-path-matcher/</url>
<content type="html"><![CDATA[<p>spring路径匹配,在spring5以后,有了AntPathMatcher 和 PathPattern 两种方式。</p><p>PathPattern是在spring 5 以后新增的。本文就介绍 AntPathMatcher 和 PathPattern 的异同,以及这两者的使用场景和方式。</p><h1 id="创建实例"><a href="#创建实例" class="headerlink" title="创建实例"></a>创建实例</h1><p>AntPathMatcher在1.x版本就存在了,在spring5以前,一直作为spring的路径匹配器存在。</p><p>AntPathMatcher在spring中是作为工具类存在的,所以直接<code>new</code>对象即可。</p><p>PathPattern出现在spring5.x版本中, 旨在用于替换掉较为“古老”的AntPathMatcher。</p><p>PathPattern是spring一个内部类,不能通过public的方式来创建。不过spring 官方提供了一个<code>parser</code>开提供PathPattern的默认实例。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 构建AntPathMatcher 实例</span><br><span class="hljs-type">AntPathMatcher</span> <span class="hljs-variable">antPathMatcher</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AntPathMatcher</span>();<br><span class="hljs-comment">// 构建PathPattern 实例</span><br><span class="hljs-type">PathPatternParser</span> <span class="hljs-variable">patternParser</span> <span class="hljs-operator">=</span> PathPatternParser.defaultInstance;<br></code></pre></td></tr></table></figure><h1 id="匹配规则"><a href="#匹配规则" class="headerlink" title="匹配规则"></a>匹配规则</h1><p>AntPathMatcher 官方文档:</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs awk">映射使用以下规则匹配 URL: <br>? 匹配一个字符<br>* 匹配零个或多个字符<br>** 匹配路径中的零 个 或多个目录<br>{spring:[a-z]+} 将正则表达 [a-z]+ 式匹配为名为“spring”的路径变量<br> <br>例子<br>com<span class="hljs-regexp">/t?st.jsp — 匹配 com/</span>test.jsp ,但也 com<span class="hljs-regexp">/tast.jsp 匹配或 com/</span>txst.jsp<br>com/*.jsp— 匹配目录中com的所有.jsp文件<br>com<span class="hljs-regexp">/**/</span>test.jsp— 匹配路径下com的所有test.jsp文件<br>org<span class="hljs-regexp">/springframework/</span>**<span class="hljs-regexp">/*.jsp— 匹配路径下org/</span>springframework的所有.jsp文件<br>org<span class="hljs-regexp">/**/</span>servlet<span class="hljs-regexp">/bla.jsp — 匹配 org/</span>springframework<span class="hljs-regexp">/servlet/</span>bla.jsp ,但也 org<span class="hljs-regexp">/springframework/</span>testing<span class="hljs-regexp">/servlet/</span>bla.jsp 和 org<span class="hljs-regexp">/servlet/</span>bla.jsp<br>com<span class="hljs-regexp">/{filename:\\w+}.jsp将匹配com/</span>test.jsp该值testfilename并将其分配给变量<br>注意: 模式和路径必须都是绝对的,或者必须都是相对的,才能使两者匹配。因此,建议此实现的用户清理模式,以便在使用模式的上下文中为“/”作为前缀。<br></code></pre></td></tr></table></figure><p>PathPattern 官方文档</p><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs jboss-cli">PathPattern 使用以下规则匹配 URL 路径: <br>? 匹配一个字符<br>* 匹配路径段中的零个或多个字符<br>** 匹配零个或多个 路径段 ,直到路径结束<br>{spring} 匹配 路径段 并将其捕获为名为“spring”的变量<br>{spring:[a-z]+} 将正则表达 [a-z]+ 式匹配为名为“spring”的路径变量<br>{*spring} 匹配零个或多个 路径段 ,直到路径的末尾,并将其捕获为名为“spring”的变量<br>注意: 与 org.springframework.util.AntPathMatcher相反, ** 仅在模式的末尾受支持。例如 <span class="hljs-string">/pages/</span>{**} ,是有效的,但不是 <span class="hljs-string">/pages/</span>{**}<span class="hljs-string">/details</span> 。这同样适用于捕获变体 {*spring}。目的是在比较模式的特异性时消除歧义。<br><br>例子<br><span class="hljs-string">/pages/t</span>?st.html — 匹配 <span class="hljs-string">/pages/test.html</span> , <span class="hljs-string">/pages/tXst.html</span> 但不是 <span class="hljs-string">/pages/toast.html</span><br><span class="hljs-string">/resources/</span>*<span class="hljs-string">.png</span>— 匹配目录中resources的所有<span class="hljs-string">.png</span>文件<br><span class="hljs-string">/resources/</span>** — 匹配路径下 <span class="hljs-string">/resources/</span> 的所有文件,包括 <span class="hljs-string">/resources/image.png</span> 和 <span class="hljs-string">/resources/css/spring.css</span><br><span class="hljs-string">/resources/</span>{*path} — 匹配 和 下的所有文件 <span class="hljs-string">/resources/</span>, <span class="hljs-string">/resources</span>并在名为 “path” 的变量中捕获它们的相对路径; <span class="hljs-string">/resources/image.png</span> 将与 “path” 匹配 → “<span class="hljs-string">/image.png</span>”,并将 <span class="hljs-string">/resources/css/spring.css</span> 与 “path” 匹配 → “<span class="hljs-string">/css/spring.css</span>”<br><span class="hljs-string">/resources/</span>{filename:\\w+}<span class="hljs-string">.dat</span>将匹配<span class="hljs-string">/resources/spring.dat</span>该值<span class="hljs-string">"spring"</span>filename并将其分配给变量<br></code></pre></td></tr></table></figure><p>可见,AntPathMatcher 和 PathPattern 的匹配规则是差不多的,都支持<code>?</code> <code>*</code> <code>**</code> 已经部分正则匹配。</p><p>但PathPattern增加了路径匹配并提取结果的能力,同时为了消除歧义,不再支持路径中间的<code>**</code>匹配,其他一样。</p><p>demo: </p><p>*匹配</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TestPathMatch</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">AntPathMatcher</span> <span class="hljs-variable">antPathMatcher</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AntPathMatcher</span>();<br> <span class="hljs-type">PathPatternParser</span> <span class="hljs-variable">patternParser</span> <span class="hljs-operator">=</span> PathPatternParser.defaultInstance;<br><br> List<String> list = List.of(<span class="hljs-string">"aaa_1.csv"</span>, <span class="hljs-string">"aaa_2.csv"</span>, <span class="hljs-string">"aaa_3.csv"</span>, <span class="hljs-string">"aaa_1.xlsx"</span>, <span class="hljs-string">"aaa"</span>, <span class="hljs-string">"aaa.pdf"</span>);<br> <span class="hljs-type">String</span> <span class="hljs-variable">pattern1</span> <span class="hljs-operator">=</span> <span class="hljs-string">"a*.csv"</span>;<br><br> System.out.println(<span class="hljs-string">"by antPathMatcher.."</span>);<br> <span class="hljs-keyword">for</span> (String s : list) {<br> System.out.printf(<span class="hljs-string">"pattern %s match %s is %s %n"</span>, pattern1,s, antPathMatcher.match(pattern1,s));<br> }<br> System.out.println(<span class="hljs-string">"by patternParser.."</span>);<br> <span class="hljs-keyword">for</span> (String s : list) {<br> System.out.printf(<span class="hljs-string">"pattern %s match %s is %s %n"</span>, pattern1,s, patternParser.parse(pattern1).matches(PathContainer.parsePath(s)));<br> }<br> }<br>}<br><br><span class="hljs-comment">// 结果</span><br><span class="hljs-comment">// by antPathMatcher..</span><br><span class="hljs-comment">// pattern a*.csv match aaa_1.csv is true</span><br><span class="hljs-comment">// pattern a*.csv match aaa_2.csv is true</span><br><span class="hljs-comment">// pattern a*.csv match aaa_3.csv is true</span><br><span class="hljs-comment">// pattern a*.csv match aaa_1.xlsx is false</span><br><span class="hljs-comment">// pattern a*.csv match aaa is false</span><br><span class="hljs-comment">// pattern a*.csv match aaa.pdf is false</span><br><span class="hljs-comment">// by patternParser..</span><br><span class="hljs-comment">// pattern a*.csv match aaa_1.csv is true</span><br><span class="hljs-comment">// pattern a*.csv match aaa_2.csv is true</span><br><span class="hljs-comment">// pattern a*.csv match aaa_3.csv is true</span><br><span class="hljs-comment">// pattern a*.csv match aaa_1.xlsx is false</span><br><span class="hljs-comment">// pattern a*.csv match aaa is false</span><br><span class="hljs-comment">// pattern a*.csv match aaa.pdf is false </span><br></code></pre></td></tr></table></figure><p>PathPattern 路径提取</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TestPathMatch</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">AntPathMatcher</span> <span class="hljs-variable">antPathMatcher</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AntPathMatcher</span>();<br> <span class="hljs-type">PathPatternParser</span> <span class="hljs-variable">patternParser</span> <span class="hljs-operator">=</span> PathPatternParser.defaultInstance;<br><br> <span class="hljs-type">String</span> <span class="hljs-variable">pattern</span> <span class="hljs-operator">=</span> <span class="hljs-string">"/api/hancher/{*spring}"</span>;<br> List<String> list = List.of(<span class="hljs-string">"/api/hancher/a/b/c"</span>, <span class="hljs-string">"/api/hancher/a"</span>, <span class="hljs-string">"/api/hancher/a/"</span>, <span class="hljs-string">"/api/hancher/a/b/c.csv"</span>,<span class="hljs-string">"/api/test/a/b/c"</span>);<br><br> <span class="hljs-keyword">for</span> (String s : list) {<br> System.out.printf(<span class="hljs-string">"pattern %s match %s is %s %n"</span>, pattern,s, patternParser.parse(pattern).matches(PathContainer.parsePath(s)));<br> System.out.printf(<span class="hljs-string">"pattern %s match %s result %s %n"</span>, pattern,s, patternParser.parse(pattern).matchAndExtract(PathContainer.parsePath(s)));<br> }<br> }<br>}<br><br><span class="hljs-comment">// 结果</span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a/b/c is true </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a/b/c result PathMatchInfo[uriVariables={spring=/a/b/c}, matrixVariables={}] </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a is true </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a result PathMatchInfo[uriVariables={spring=/a}, matrixVariables={}] </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a/ is true </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a/ result PathMatchInfo[uriVariables={spring=/a/}, matrixVariables={}] </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a/b/c.csv is true </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/hancher/a/b/c.csv result PathMatchInfo[uriVariables={spring=/a/b/c.csv}, matrixVariables={}] </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/test/a/b/c is false </span><br><span class="hljs-comment">//pattern /api/hancher/{*spring} match /api/test/a/b/c result null </span><br></code></pre></td></tr></table></figure><h1 id="性能"><a href="#性能" class="headerlink" title="性能"></a>性能</h1><p>官方说PathPattern 比 AntPathMatcher 更快一些,所以咱们验证一下:</p><p>测试代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TestPathMatch</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">AntPathMatcher</span> <span class="hljs-variable">antPathMatcher</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AntPathMatcher</span>();<br> <span class="hljs-type">PathPatternParser</span> <span class="hljs-variable">patternParser</span> <span class="hljs-operator">=</span> PathPatternParser.defaultInstance;<br><br> <span class="hljs-type">long</span> <span class="hljs-variable">start</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> <span class="hljs-type">String</span> <span class="hljs-variable">pattern</span> <span class="hljs-operator">=</span> <span class="hljs-string">"/api/hanc?er/**"</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">100000</span>; i++) {<br> <span class="hljs-type">String</span> <span class="hljs-variable">path</span> <span class="hljs-operator">=</span> <span class="hljs-string">"/api/hancher/"</span> + i + <span class="hljs-string">"/"</span> + i + <span class="hljs-string">"/a.txt"</span>;<br> System.out.println(antPathMatcher.match(pattern, path));<br><span class="hljs-comment">// System.out.println(patternParser.parse(pattern).matches(PathContainer.parsePath(path)));</span><br> }<br> System.out.println(System.currentTimeMillis() - start);<br> }<br>}<br></code></pre></td></tr></table></figure><p>分别跑了5次。<br>AntPathMatcher 平均耗时:392ms<br>PathPattern 平均耗时:323.4ms<br>官方所言不虚! </p><h1 id="选择"><a href="#选择" class="headerlink" title="选择"></a>选择</h1><ol><li>在满足需求的情况下,尽量选PathPattern,更快</li><li>如果需要通过*<em>匹配中间路径,或者通过</em>匹配文件扩展名这种场景,用AntPathMatcher, 因为PathPattern不支持。</li></ol>]]></content>
<categories>
<category>spring</category>
</categories>
<tags>
<tag>spring</tag>
</tags>
</entry>
<entry>
<title>spring服务form表单内容过大导致后端参数接收为null情况</title>
<link href="/2023/04/28/exception-spring-http-form-too-big-err/"/>
<url>/2023/04/28/exception-spring-http-form-too-big-err/</url>
<content type="html"><![CDATA[<h2 id="问题现状"><a href="#问题现状" class="headerlink" title="问题现状"></a>问题现状</h2><p>今天开发遇到一个奇怪的问题,前端的form表单数据提交的时候,数据量比较小的时候,内容能正常保存。 </p><p>当数据量达到1.6M的时候,后端就开始报npe异常,参数字段就开始接收不到了。</p><h2 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h2><p>经过排查,发现这个和编程无关。<br>springboot 默认集成了tomcat容器,tomcat对form表单的大小有限制,默认2M。</p><p>知道了问题所在,解决起来就很简单了。<br>方案1: 增加tomcat的form表单容量配置</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">server.tomcat.max-http-form-post-size</span>=<span class="hljs-string">10MB</span><br></code></pre></td></tr></table></figure><p>方案2:去掉tomcat的form容量配置</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">server.tomcat.max-http-form-post-size</span>=<span class="hljs-string">-1</span><br></code></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.server.server.tomcat.max-http-form-post-size">spring官方配置</a></p>]]></content>
<categories>
<category>编程人生</category>
</categories>
<tags>
<tag>java</tag>
<tag>异常</tag>
<tag>spring</tag>
</tags>
</entry>
<entry>
<title>如何给bash脚本进行加密?</title>
<link href="/2023/04/20/bash-shc/"/>
<url>/2023/04/20/bash-shc/</url>
<content type="html"><![CDATA[<p>当我们写的bash脚本,有的时候需要分享给别人用,但是又不太想让别人看到实现逻辑。没到这个时候,就想着要是能给这个脚本加密一下就好了。<br>仅加密脚本的内容,又不影响脚本的运行,多好。要知道在计算机的世界,只有想不到,没有做不到!<br>你不是第一个遇到这个问题的人,而且有比你更有行动力的人把这个问题解决了。 </p><p>今天我们就介绍两个工具来解决这个问题。</p><h1 id="gzexe"><a href="#gzexe" class="headerlink" title="gzexe"></a>gzexe</h1><p>mac系统上自带的一个工具,可以在mac系统上将一个可执行的bash脚本加密。<br>使用方式:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">usage: gzexe [-d] file ...<br></code></pre></td></tr></table></figure><p>我们以hello.sh 脚本为例。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br><span class="hljs-built_in">echo</span> <span class="hljs-string">"Hello World "</span><br></code></pre></td></tr></table></figure><p>操作:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs bash">mac % gzexe hello.sh <br>gzexe: cannot compress hello.sh, it is not executable <span class="hljs-comment"># hello.sh 必须是一个可执行文件</span><br><br><span class="hljs-comment"># 修改hello.sh 为可执行文件</span><br>mac % <span class="hljs-built_in">chmod</span> 744 hello.sh <br><br>mac % gzexe hello.sh <br>hello.sh: -4.3% <br><br><span class="hljs-comment"># 成功,并且体检压缩了4.3% 我们执行一下ls 看看发生了什么</span><br>mac % <span class="hljs-built_in">ls</span><br>hello.shhello.sh~<br><br><span class="hljs-comment"># 这个时候我们发现hello.sh已经加密了,但我们发现多了一个hello.sh~文件。 这个就是我们的加密前的源文件。</span><br><span class="hljs-comment"># 源文件还在?我们把hello.sh~删了 , 看看还不能运行。</span><br>mac % <span class="hljs-built_in">rm</span> hello.sh~<br>mac % sh hello.sh <br>Hello World <span class="hljs-comment"># 依然可以运行</span><br></code></pre></td></tr></table></figure><p>但是我们把hello.sh放到其他系统上执行时,会发现失败。但是在mac本地系统执行时,还是可以的.</p><h1 id="shc-Shell-Script-Compiler"><a href="#shc-Shell-Script-Compiler" class="headerlink" title="shc (Shell Script Compiler)"></a>shc (Shell Script Compiler)</h1><p>shell(bash) 脚本编译指令。mac系统原生不支持,可以通过下面方式安装。 </p><h3 id="安装步骤"><a href="#安装步骤" class="headerlink" title="安装步骤"></a>安装步骤</h3><ol><li>检查本地是否装有shc, 直接输入shc命令</li><li>安装</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># linux </span><br>yum install shc<br><br><span class="hljs-comment"># mac</span><br>brew install shc<br></code></pre></td></tr></table></figure><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p>使用方式:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs bash">mac % shc -h<br>shc Version 4.0.3, Generic Shell Script Compiler<br>shc GNU GPL Version 3 Md Jahidul Hamid <[email protected]><br>shc Usage: shc [-e <span class="hljs-built_in">date</span>] [-m addr] [-i iopt] [-x cmnd] [-l lopt] [-o outfile] [-rvDSUHCABh] -f script<br><br> -e %s Expiration <span class="hljs-built_in">date</span> <span class="hljs-keyword">in</span> <span class="hljs-built_in">dd</span>/mm/yyyy format [none]<br> -m %s Message to display upon expiration [<span class="hljs-string">"Please contact your provider"</span>]<br> -f %s File name of the script to compile<br> -i %s Inline option <span class="hljs-keyword">for</span> the shell interpreter i.e: -e<br> -x %s eXec <span class="hljs-built_in">command</span>, as a <span class="hljs-built_in">printf</span> format i.e: <span class="hljs-built_in">exec</span>(<span class="hljs-string">'%s'</span>,@ARGV);<br> -l %s Last shell option i.e: --<br> -o %s output filename<br> -r Relax security. Make a redistributable binary<br> -v Verbose compilation<br> -S Switch ON setuid <span class="hljs-keyword">for</span> root callable programs [OFF]<br> -D Switch ON debug <span class="hljs-built_in">exec</span> calls [OFF]<br> -U Make binary untraceable [no]<br> -H Hardening : extra security protection [no]<br> Require bourne shell (sh) and parameters are not supported<br> -C Display license and <span class="hljs-built_in">exit</span><br> -A Display abstract and <span class="hljs-built_in">exit</span><br> -B Compile <span class="hljs-keyword">for</span> busybox<br> -h Display <span class="hljs-built_in">help</span> and <span class="hljs-built_in">exit</span><br></code></pre></td></tr></table></figure><p>同样以hello.sh脚本为例</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 普通加密</span><br>mac % shc -v -f hello.sh<br>mac % <span class="hljs-built_in">ls</span> <br>hello.sh <span class="hljs-comment"># 原始脚本</span><br>hello.sh.x<span class="hljs-comment"># 可执行脚本</span><br>hello.sh.x.c <span class="hljs-comment"># 可执行脚本c语音文件</span><br><br><span class="hljs-comment"># 设置过期时间的加密脚本</span><br>mac % shc -v -e 20/04/2023 -m <span class="hljs-string">"过期了"</span> -f hello.sh -o hello_expir.sh <br>shc -e Thu Apr 20 00:00:00 2023<br>shc -e Thu Apr 20 00:00:00 2023<br>shc shll=bash<br>shc [-i]=-c<br>shc [-x]=<span class="hljs-built_in">exec</span> <span class="hljs-string">'%s'</span> <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span><br>shc [-l]=<br>shc opts=<br>shc: cc hello.sh.x.c -o hello_expir.sh<br>shc: strip hello_expir.sh<br>shc: <span class="hljs-built_in">chmod</span> ug=rwx,o=rx hello_expir.sh<br><br><span class="hljs-comment"># 查看结果</span><br>mac % <span class="hljs-built_in">ls</span> <br>hello.shhello.sh.x.chello_expir.sh<br><br><span class="hljs-comment"># 过期效果</span><br>mac % ./hello_expir.sh <br>./hello_expir.sh: has expired!<br>过期了<br><br></code></pre></td></tr></table></figure><p>同gzexe一样,加密后的脚本一样不支持跨平台运行,即使是编译成c语言的二进制脚本。因为可执行文件会依赖不同系统的动态链接库。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://opensource.apple.com/source/file_cmds/file_cmds-272/gzip/gzexe.auto.html">gzexe 源码</a></p><p><a href="https://www.geeksforgeeks.org/gzexe-command-in-linux-with-examples/">gzexe examples</a></p><p><a href="https://www.linux-magazine.com/Online/Features/SHC-Shell-Compiler">SHC-Shell-Compiler</a></p><p><a href="https://github.com/neurobin/shc">shc官网</a></p>]]></content>
<categories>
<category>linux</category>
</categories>
<tags>
<tag>linux</tag>
<tag>bash</tag>
</tags>
</entry>
<entry>
<title>JDK 20新特性简介</title>
<link href="/2023/04/04/java-jdk-v20/"/>
<url>/2023/04/04/java-jdk-v20/</url>
<content type="html"><![CDATA[<p><img src="/images/tech/java/jdk20-new-feature.jpeg" alt="jdk20"><br>按照JDK每半年发布一次版本的节奏, JDK20在2023年3月发布了, 本文就是简单介绍一下这次更新的新特性与改动.</p><p>一句话总结, 这仍然是jdk的一个短期支持版本,此版本包括7个 JEP(jdk增强建议),以及数百个较小的功能增强和数千个错误修复.</p><h2 id="新特性汇总"><a href="#新特性汇总" class="headerlink" title="新特性汇总"></a>新特性汇总</h2><h3 id="预览特性"><a href="#预览特性" class="headerlink" title="预览特性"></a>预览特性</h3><h4 id="语言特性"><a href="#语言特性" class="headerlink" title="语言特性:"></a>语言特性:</h4><ul><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8294078">JEP 432</a> Record Patterns</strong>(记录模式) : 第2预览阶段. 记录模式是java语言的一个新语言特性.其目的是将我们从java复杂的类构造中解放出来,更关注于数据. 一般用于一组数据构成的一个整体,比如坐标point(x,y).</li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8294285">JEP 433</a> Pattern Matching for switch</strong>(switch记录模式):第4次预览, switch语法支持记录模式.</li></ul><h4 id="API库扩充"><a href="#API库扩充" class="headerlink" title="API库扩充:"></a>API库扩充:</h4><ul><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8293649">JEP 434</a> Foreign Function &Memory AP</strong>(访问jvm外部函数或内存的API) : 引入一个API,通过该API,Java程序可以在Java运行时之外与代码和数据进行互操作。通过有效地调用外部函数(即JVM外部的代码),并通过安全访问外部内存(即不由JVM管理的内存),API使Java程序能够调用本机库并处理本机数据,而没有JNI的脆性和危险性。</li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8303262">JEP 438</a> Vector API</strong>(向量API): 第5孵化阶段.向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算,该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令,从而实现优于等效标量计算的性能。向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算.</li></ul><h4 id="并发增强"><a href="#并发增强" class="headerlink" title="并发增强:"></a>并发增强:</h4><ul><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8263012">JEP 429</a> Scoped Values</strong>(作用域值) : 孵化阶段.引入范围值,允许在线程内和线程之间共享不可变的数据。它们优于线程本地变量,特别是在使用大量虚拟线程时。因为变量是不可变的,也就避免了并发问题.</li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8295817">JEP 436</a> Virtual Threads</strong>(虚拟线程) : 第2预览阶段. 将虚拟线程引入Java平台。虚拟线程是轻量级线程,大大减少了编写、维护和观察高吞吐量并发应用程序的工作量. 本次主要是一些核心功能升级.</li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8296037">JEP 437</a> Structured Concurrency</strong>(结构化并发) : 第2孵化阶段. 通过引入结构化并发的API来简化多线程编程。结构化并发将在不同线程中运行的多个任务视为单个工作单元,从而简化错误处理和取消,提高可靠性并提高可观察性。</li></ul><h3 id="主要特性"><a href="#主要特性" class="headerlink" title="主要特性"></a>主要特性</h3><h4 id="核心库"><a href="#核心库" class="headerlink" title="核心库"></a>核心库</h4><ul><li><strong>Support Unicode 15.0</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8284842">JDK-8284842</a>) : 支持Unicode15.0 .</li><li><strong>Print Warning to Standard Error If Bad java.io.tmpdir Setting Is Detected</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8290313">JDK-8290313</a>) : 新增一种警告输出场景</li></ul><h4 id="安全"><a href="#安全" class="headerlink" title="安全"></a>安全</h4><ul><li><strong>New JFR Event: jdk.InitialSecurityProperty</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8292177">JDK-8292177</a>) : 新增一个新的JFR(Java Flight Recorder)事件.</li><li><strong>New JFR Event: jdk.SecurityProviderService</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8254711">JDK-8254711</a>) : 新增一个新的JFR(Java Flight Recorder)事件.</li><li><strong>Provide Poly1305 Intrinsic on x86_64 platforms with AVX512 instructions</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8288047">JDK-8288047</a>) : 此功能在x86_64平台上使用AVX512指令为SunJCE提供商的Poly1305消息身份验证代码算法提供优化。</li><li><strong>Provide ChaCha20 Intrinsics on x86_64 and aarch64 Platforms</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8247645">JDK-8247645</a>) : 此功能为SunJCE提供商提供的ChaCha20密码提供了优化的内在实现。这些优化的例程专为支持AVX、AVX2和/或AVX512指令集的x86_64芯片组以及支持高级SIMD指令集的aarch64芯片组而设计。</li><li><strong>(D)TLS Key Exchange Named Groups</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8281236">JDK-8281236</a>) : 加密功能增强.</li><li><strong>DTLS Resumption Uses HelloVerifyRequest Messages</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8287411">JDK-8287411</a>) : 加密功能增强.</li></ul><h4 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h4><ul><li><strong>Javac Warns about Type Casts in Compound Assignments with Possible Lossy Conversions</strong>(<a href="https://bugs.openjdk.org/browse/JDK-8244681">JDK-8244681</a>) : javac命令增加了有损类型强转警告.</li><li><strong>New ‘jmod –compress’ Command Line Option</strong>(<a href="https://bugs.openjdk.org/browse/JDK-8293499">JDK-8293499</a>) : 在jmod工具中添加了一个新的–compress命令行选项,以便在创建JMOD存档时指定压缩级别。接受的值是zip-[0-9],其中zip-0不提供压缩,zip-9提供最佳压缩。默认是zip-6。</li></ul><p>jdk20 工具箱全集<br><img src="/images/tech/java/java-bin-desc.png" alt="java-bin"></p><h4 id="虚拟机"><a href="#虚拟机" class="headerlink" title="虚拟机"></a>虚拟机</h4><ul><li><strong>add bean for Remark and Cleanup Pause Time in G1</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8297247">JDK-8297247</a>) : G1垃圾回收在标记和清理阶段引入了一个新的回收bean : “G1 Concurrent GC”</li></ul><h3 id="移出功能"><a href="#移出功能" class="headerlink" title="移出功能"></a>移出功能</h3><ul><li><strong>Thread.suspend/resume Changed to Throw UnsupportedOperationException</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8249627">JDK-8249627</a>) : 在此版本中,使用Thread.suspend()和Thread.resume()方法暂停或恢复线程的能力已被删除。方法已更改为抛出UnsupportedOperationException。这些方法本质上容易陷入僵局,自JDK 1.2(1998年)以来已被弃用。ThreadGroup中的相应方法,即暂停或恢复一组线程,已更改为在Java 19中抛出UnsupportedOperationException。</li><li><strong>Thread.Stop Changed to Throw UnsupportedOperationException</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8289610">JDK-8289610</a>) : 在此版本中,使用Thread.stop()方法“停止”线程的功能已被删除。该方法已更改为抛出UnsupportedOperationException。通过使其抛出java.lang.ThreadDeath来阻止线程本质上是不安全的。自JDK 1.2(1998)以来,停止方法已被弃用。ThreadGroup中的相应方法“停止”一组线程,已更改为在Java 19中抛出UnsupportedOperationException。同时, java.lang.ThreadDeath也被标记为废弃.</li><li><strong>Remove Support for javac -source/-target/–release 7</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8173605">JDK-8173605</a>) : javac移出了一些选项</li><li><strong>Improved Control of G1 Concurrent Refinement Threads</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8137022">JDK-8137022</a>): G1并发细化线程的控制已被完全取代。新控制器通常分配更少的线程。它在细化线程活动中的峰值往往较少。它还倾向于延迟细化,当对相同或附近位置有多个写入时,允许通过写入屏障进行更多过滤,从而提高屏障的效率。</li></ul><h3 id="废弃功能"><a href="#废弃功能" class="headerlink" title="废弃功能"></a>废弃功能</h3><ul><li><strong>java.net.URL Constructors Are Deprecated</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8294241">JDK-8294241</a>) : 废弃URL构造方法,推荐使用 java.net.URI 来构建URL.</li><li><strong>Deprecate JMX Management Applets for Removal</strong> (<a href="https://bugs.openjdk.org/browse/DK-8297794">DK-8297794</a>) : 废弃JMX</li></ul><h2 id="小优化"><a href="#小优化" class="headerlink" title="小优化"></a>小优化</h2><p>这里主要介绍一些小的优化, 太长可以不看.</p><ul><li><strong>HTTP Response Input Streams Will Throw an IOException on Interrupt</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8294047">JDK-8294047</a>) : http请求中断抛出异常</li><li><strong>HttpClient Default Keep Alive Time is 30 Seconds</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8297030">JDK-8297030</a>) : 由java.net.http.HttpClient创建的HTTP/1.1和HTTP/2连接的默认空闲连接超时值已从1200秒减少到30秒</li><li><strong>Idle Connection Timeouts for HTTP/2</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8288717">JDK-8288717</a>) : 此版本中添加了HTTP/2的空闲连接超时。</li><li><strong>FileChannel Positional Write Is Unspecified in APPEND Mode</strong> (<a href="https://bugs.openjdk.org/browse/JDK-6924219">JDK-6924219</a>) : append模式下,FileChannel写入位置因操作系统而定</li><li><strong>Update Timezone Data to 2022c</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8292579">JDK-8292579</a>) : 此版本包括2022b的更改,将1970年后具有相同时间戳数据的多个区域合并到单个时区数据库中。所有时区ID保持不变,但合并的时区将指向共享区域数据库。</li><li><strong>IdentityHashMap’s Remove and Replace Methods Use Object Identity</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8178355">JDK-8178355</a>) : IdentityHashMap的这两个方法, 对象比较由 <code>equals</code> 改为 <code>==</code></li><li><strong>Support for CLDR Version 42</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8284840">JDK-8284840</a>) : 基于Unicode Consortium的CLDR的本地数据已升级到版本42</li><li><strong>Introduce LDAP and RMI Protocol Specific Object Factory Filters to JNDI Implementation</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8290368">JDK-8290368</a>) : 引入了新的系统和安全属性,以允许对允许从JNDI/LDAP和JNDI/RMI上下文重建Java对象的JNDI对象工厂集进行更精细的控制</li><li><strong>G1: Disable Preventive GCs by Default</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8293861">JDK-8293861</a>) : G1默认禁用Preventive垃圾回收器</li><li><strong>appendToClassPathForInstrumentation Must Be Used in a Thread-Safe Manner</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8296472">JDK-8296472</a>) : 如题, 当时用java agent时,自定义类加载器中的appendToClassPathForInstrumentation方法必须以线程安全的方式添加到类搜索路径中。</li><li><strong>Deprecate and Disable Legacy Parallel Class Loading Workaround for Non-Parallel-Capable Class Loaders</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8295673">JDK-8295673</a>) : 一些用户定义的旧类加载器会通过在加载过程中释放类加载器锁来解决死锁问题。为了防止这些加载程序在通过并行线程加载同一类时遇到“java.lang.LinkageError:尝试重复类定义”,HotSpot虚拟机在JDK 6中引入了一种变通方法,将加载尝试序列化,导致随后的尝试等待第一个完成. 现在将这种变通 方法废弃了</li><li><strong>Disabled DTLS 1.0</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8256660">JDK-8256660</a>) : 废弃 DTLS 1.0</li><li><strong>Remove Thread Text from Subject.current</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8297276">JDK-8297276</a>) : 在此版本中,Subject.current的规范已更改,以降低创建线程时继承主题的期望。此时,主题存储在AccessControlContext中,并在创建平台线程时被继承。虚拟线程在线程创建时不会捕获调用者上下文,并且不会继承AccessControlContext。在删除对SecurityManager和继承的AccessControlContext的支持之前,将在未来的版本中重新审查继承。</li><li><strong>Generalize ‘see’ and ‘link’ Tags for User-Defined Anchors</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8200337">JDK-8200337</a>) : {@link}、{@linkplain}和@see标签已增强,允许链接到JavaDoc生成的元素文档中的任意锚点。为了区分这些引用和成员引用,使用双散列标记(<code>##</code>)将元素名称与URI片段分开。</li></ul><h2 id="名词解析"><a href="#名词解析" class="headerlink" title="名词解析"></a>名词解析</h2><ul><li><p>HarfBuzz: 一个开源的用于文字塑形的软件开发库,亦即用于转换Unicode文本到字形指标及方位的过程</p></li><li><p>FreeType: 同HarfBuzz一样, 也是一个开源字体库. 它是一个用C语言实现的一个字体光栅化库。它可以用来将字符栅格化并映射成位图以及提供其他字体相关业务的支持</p></li><li><p>Preview(预览): 功能已经基本完整, 可以试用了. 可以简单理解为beta公测版. 来源于 <a href="https://openjdk.org/jeps/12">JEP12</a></p><blockquote><p>功能以预览版的形式发布,以收集有关它们的反馈而不承诺保持其向后兼容性——这意味着鼓励每个人尝试它们,但同时不鼓励在生产中使用它们。</p><p>预览功能不是开箱即用的,为了访问它们,需要使用*–enable-preview*编译器标志。</p></blockquote></li><li><p>Incubator(孵化) : 实验性 API已经到了一定阶段, 已经计划开发出一整套完整的功能.以独立模块的形式发布. 来源于 <a href="https://openjdk.org/jeps/11">JEP11</a></p></li><li><p>Experimental(实验) : vm级的早期功能, 不稳定, 功能不完整. 实验性质.</p><blockquote><p>实验性功能代表(主要是)VM 级功能的早期版本,这些功能可能是有风险的、不完整的,甚至是不稳定的。在大多数情况下,需要使用专用标志启用它们</p><p>出于比较的目的,如果一个实验功能被认为是 25%“完成”,那么一个预览功能应该至少 95%“完成”。</p><p>预览,孵化,实验三者的关系大致: 实验 => 孵化 => 预览 => 合并jdk主体功能.</p></blockquote></li><li><p>JEP : JDK Enhancement Proposal , jdk增强建议. 也就是我们常说的jdk新特性的来源. <a href="https://openjdk.org/jeps/0">JEP大全</a></p></li></ul><h2 id="往期文章"><a href="#往期文章" class="headerlink" title="往期文章"></a>往期文章</h2><p><a href="jdk-v19">jdk19新特性</a></p><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="https://docs.oracle.com/en/java/javase/20/">jdk20官方文档</a><br><a href="https://www.oracle.com/java/technologies/javase/20-relnote-issues.html">jdk20新特性文档</a><br><a href="https://docs.oracle.com/en/java/javase/20/migrate/getting-started.html">jdk20升级指南</a><br><a href="https://docs.oracle.com/en/java/javase/20/install/index.html">jdk20安装指南</a><br><a href="https://cr.openjdk.org/~iris/se/20/latestSpec/">jdk20的开发计划</a><br><a href="https://docs.oracle.com/en/java/javase/20/vm/index.html">jdk20虚拟机简介</a><br><a href="https://docs.oracle.com/en/java/javase/20/gctuning/index.html">jdk20垃圾回收机制</a><br><a href="https://docs.oracle.com/en/java/javase/20/security/index.html">jdk20安全机制</a></p><p><a href="https://bugs.openjdk.org/secure/Dashboard.jspa">openjdk bug反馈系统</a><br><a href="https://openjdk.org/">openjdk官网</a><br><a href="https://4comprehension.com/preview-experimental-and-incubating-features-in-java/">jdk中预览,实验,孵化的关系</a> </p>]]></content>
<categories>
<category>java</category>
<category>java版本特性</category>
</categories>
<tags>
<tag>java</tag>
</tags>
</entry>
<entry>
<title>通过frp实现内网穿透功能</title>
<link href="/2023/03/26/net-intranet-frp/"/>
<url>/2023/03/26/net-intranet-frp/</url>
<content type="html"><![CDATA[<p>让我们从0开始搭建一个很酷的内网穿透功能.</p><p>作为一名程序员,谁不想拥有一个自己的云服务器呢. 但是当我们看了各大云服务器厂商的价格后, 不禁陷入了沉思:我好像对云服务器的诉求也没那么大!<br>等我们回家看到家里闲置的老旧电脑时,又会想,我为啥要用云服务器呢,家里的电脑改吧改吧, 不也就可以用了. 我们又不用做大流量的网站,<br>最多搞搞博客,跑跑爬虫啥的,只要能让我们可以随时在外面访问我家里的电脑,控制家里的电脑就行了.<br>好的,说干就干, 经过一番调研, 我们发现, 好像没自己想象的那么简单. 一个最直接的问题就是, 我们没有一个自己的ip,因为众所周知的ipv4资源耗尽的问题,<br>我们家里实际上网的ip其实不是固定的, 而是多家用户共享一个ip地址,也就是NAT(Network Address Translation)技术. 所以,从外网我们是无法直接访问家里的电脑的.<br>这个问题怎么解决呢? 就是我们今天要聊的话题, 内网穿透技术.</p><h1 id="什么是内网穿透"><a href="#什么是内网穿透" class="headerlink" title="什么是内网穿透"></a>什么是内网穿透</h1><p>内网穿透,也即NAT穿透,进行NAT穿透是为了使具有某一个特定源IP地址和源端口号的数据包不被NAT设备屏蔽而正确路由到内网主机.<br>大致流程如图:<br><img src="/images/tech/net/nat-through.png" alt="内网穿透图"></p><h1 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h1><ol><li>一台云服务器, 最便宜的即可,我们主要用其来注册内网服务器, 转发请求</li><li>frp工具</li><li>家用电脑,台式,笔记本都行,操作系统任意.</li></ol><h1 id="开始内网穿透"><a href="#开始内网穿透" class="headerlink" title="开始内网穿透"></a>开始内网穿透</h1><h2 id="云服务器搭建frp服务"><a href="#云服务器搭建frp服务" class="headerlink" title="云服务器搭建frp服务"></a>云服务器搭建frp服务</h2><p>参考<a href="https://gofrp.org/docs/setup/">官方安装frp说明</a>, 下载合适自己服务器的frp版本, 解压到自定义的frp服务目录.<br>因为是服务器部分, 我们直接启用frps 即可启动成功frp的服务部分. </p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 启动frps, 并指定配置文件为frps.ini </span><br>./frps -c ./frps.ini<br></code></pre></td></tr></table></figure><p>配置部分参考<a href="https://gofrp.org/docs/examples/">官方配置样例</a>. 我们demo样例只使用ssh基本服务, 就用了默认配置.</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[common]</span><br><span class="hljs-attr">bind_port</span> = <span class="hljs-number">7000</span><br></code></pre></td></tr></table></figure><h4 id="配置frps为系统服务-选做"><a href="#配置frps为系统服务-选做" class="headerlink" title="配置frps为系统服务(选做)"></a>配置frps为系统服务(选做)</h4><p>为了方便, 我们一般会将frps配置成系统重启自动加载. 这样我们就不用担心服务器系统重启导致内网穿透服务掉线了.<br>方法如下:</p><ol><li>先确认系统支持<code>systemd</code>, 如果不支持请安装</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># yum</span><br>yum install systemd<br><span class="hljs-comment"># apt</span><br>apt install systemd<br></code></pre></td></tr></table></figure><ol start="2"><li>配置frps.service服务</li></ol><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">vim <span class="hljs-regexp">/etc/</span>systemd<span class="hljs-regexp">/system/</span>frps.service<br></code></pre></td></tr></table></figure><p>写入下面内容</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[Unit]</span><br><span class="hljs-comment"># 服务名称,可自定义</span><br><span class="hljs-attr">Description</span> = frp server<br><span class="hljs-attr">After</span> = network.target syslog.target<br><span class="hljs-attr">Wants</span> = network.target<br><br><span class="hljs-section">[Service]</span><br><span class="hljs-attr">Type</span> = simple<br><span class="hljs-comment"># 启动frps的命令,需修改为您的frps的安装路径</span><br><span class="hljs-attr">ExecStart</span> = /path/to/frps -c /path/to/frps.ini<br><br><span class="hljs-section">[Install]</span><br><span class="hljs-attr">WantedBy</span> = multi-user.target<br></code></pre></td></tr></table></figure><ol start="3"><li>开机自动启动</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl <span class="hljs-built_in">enable</span> frps<br></code></pre></td></tr></table></figure><h2 id="家用电脑安装frp客户端"><a href="#家用电脑安装frp客户端" class="headerlink" title="家用电脑安装frp客户端"></a>家用电脑安装frp客户端</h2><p>我家里的电脑是windows10, 当然, Linux更好. 本次内网穿透的目的是可以通过远程ssh控制我的家用电脑.</p><h4 id="win10的一些准备功能"><a href="#win10的一些准备功能" class="headerlink" title="win10的一些准备功能"></a>win10的一些准备功能</h4><p>windows系统作为家用本, 对网络权限限制的比较多, 需要我们额外做一些操作.<br>a. 安装windows的ssh服务端<br>win10默认是不支持ssh的服务端的, 需要我们安装相应的OpenSSH服务器.<br>操作步骤: 设置->应用-> 应用与功能 -> 可选功能 -> 添加OpenSSH服务器<br><img src="/images/tech/windows/windows_openssh_step1.png" alt="openssh-s1"><br><img src="/images/tech/windows/windows_openssh_step2.png" alt="openssh-s2"><br><img src="/images/tech/windows/windows_openssh_step3.png" alt="openssh-s3"><br>最后开启ssh服务,以管理员身份打开cmd终端,执行如下命令 </p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">net start sshd<br></code></pre></td></tr></table></figure><p>b. 防火墙可能会将frpc识别为危险文件, 记得去防火墙那里将工具恢复. </p><h4 id="配置frp客户端"><a href="#配置frp客户端" class="headerlink" title="配置frp客户端"></a>配置frp客户端</h4><p>从官网下载下frpc客户端后,且成功从防火墙那里逃生, 就可以继续后面的操作了<br>首先同样先修改配置文件</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[common]</span><br><span class="hljs-comment"># 你的云服务器实际ip</span><br><span class="hljs-attr">server_addr</span> = x.x.x.x <br><span class="hljs-comment"># 云服务配置的端口</span><br><span class="hljs-attr">server_port</span> = <span class="hljs-number">7000</span><br><br><span class="hljs-section">[ssh]</span><br><span class="hljs-attr">type</span> = tcp<br><span class="hljs-attr">local_ip</span> = <span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span><br><span class="hljs-comment"># 本地要暴露的端口, windows网络限制比较严格,记得去防火墙打开相应端口</span><br><span class="hljs-attr">local_port</span> = <span class="hljs-number">22</span><br><span class="hljs-comment"># 远程服务器监听的端口. 意思就是frps服务器监听6000的端口, 然后把请求转发的本地的22端口上.</span><br><span class="hljs-attr">remote_port</span> = <span class="hljs-number">6000</span><br></code></pre></td></tr></table></figure><p>然后打开cmd终端使用命令行的方式启动frpc工具<br>最后就可以在外网使用ssh命令访问你的系统了</p><figure class="highlight gml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gml">ssh -oPort=<span class="hljs-number">6000</span> 你的系统用户名@<span class="hljs-variable language_">x</span>.<span class="hljs-variable language_">x</span>.<span class="hljs-variable language_">x</span>.<span class="hljs-variable language_">x</span><br></code></pre></td></tr></table></figure><p>frp 会将请求 x.x.x.x:6000 的流量转发到内网机器的 22 端口</p><h4 id="修改window默认终端为powerShell-选做"><a href="#修改window默认终端为powerShell-选做" class="headerlink" title="修改window默认终端为powerShell(选做)"></a>修改window默认终端为powerShell(选做)</h4><p>windows的默认终端为cmd, 很多命令不与linux兼容. 所以建议将windows的默认终端改为powerShell, 更方便.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">New-ItemProperty -Path <span class="hljs-string">"HKLM:\SOFTWARE\OpenSSH"</span> -Name DefaultShell -Value <span class="hljs-string">"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"</span> -PropertyType String -Force<br></code></pre></td></tr></table></figure><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><a href="https://gofrp.org/">frp官网</a> </li><li><a href="https://learn.microsoft.com/zh-cn/windows-server/administration/openssh/openssh_server_configuration?source=recommendations">windows配置默认 shell</a></li></ul>]]></content>
<categories>
<category>编程人生</category>
</categories>
<tags>
<tag>内网穿透</tag>
</tags>
</entry>
<entry>
<title>mac系统通过homebrew安装nginx</title>
<link href="/2023/03/24/homebrew-install-nginx/"/>
<url>/2023/03/24/homebrew-install-nginx/</url>
<content type="html"><![CDATA[<h1 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h1><ul><li>mac系统</li><li>homebrew 环境已经配置好</li></ul><h1 id="操作步骤"><a href="#操作步骤" class="headerlink" title="操作步骤"></a>操作步骤</h1><ol><li>查看本地是否安装nginx</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">brew search nginx<br></code></pre></td></tr></table></figure><p><img src="/images/tech/nginx/brew-search-nginx-not-find.png" alt="brew-search-nginx-not-find"><br>nginx上面没有显示✓,表示nginx没有安装</p><ol start="2"><li>查看nginx信息</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">brew info nginx<br></code></pre></td></tr></table></figure><p><img src="/images/tech/nginx/brew-info-nginx-not-find.png" alt="brew-info-nginx-not-find"><br>如图所示,显示了nginx的版本. 也明确说明了系统没有安装nginx<br>3. 安装nginx</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">brew install nginx<br></code></pre></td></tr></table></figure><p><img src="/images/tech/nginx/brew-install-nginx-success.png" alt="brew-install-nginx-success"><br>安装nginx成功.<br>nginx默认监听8080端口, 配置文件路径也给出来了 <em>/opt/homebrew/etc/nginx/nginx.conf</em><br>同时也给了启动nginx的命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">brew services start nginx<br></code></pre></td></tr></table></figure><p>4.验证安装成功<br>此时,nginx命令已经生效了.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">nginx -v<br></code></pre></td></tr></table></figure><p><img src="/images/tech/nginx/nginx-version.png" alt="nginx-version"></p><p>当然, 也可以执行下面命令查看更详细的信息</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">nginx -V<br></code></pre></td></tr></table></figure><p>至此,nginx已经安装成功了. 可以通过去配置文件修改相应配置, 启动nginx了</p><h1 id="启动nginx"><a href="#启动nginx" class="headerlink" title="启动nginx"></a>启动nginx</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 启动</span><br>nginx<br>brew services start nginx<br></code></pre></td></tr></table></figure><p>启动成功了,此时可以通过 <a href="http://localhost:8080/">http://localhost:8080</a> 验证一下<br><img src="/images/tech/nginx/nginx-http-success.png" alt="nginx-http"></p><h1 id="补充nginx的常用命令"><a href="#补充nginx的常用命令" class="headerlink" title="补充nginx的常用命令"></a>补充nginx的常用命令</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 查看版本</span><br>nginx -v<br>nginx -V<br><br><span class="hljs-comment"># 启动</span><br>nginx <span class="hljs-comment"># 加载默认配置</span><br>nginx -c 配置文件路径 <span class="hljs-comment"># 加载指定配置文件</span><br>brew services start nginx<br><br><br><span class="hljs-comment"># 停止</span><br>nginx -s stop <span class="hljs-comment"># 快速停止</span><br>nginx -s quit <span class="hljs-comment"># 安全停止</span><br><br><span class="hljs-comment"># 不重启加载配置</span><br>nginx -s reload <br><br><span class="hljs-comment"># 验证配置</span><br>nginx -t <br><br></code></pre></td></tr></table></figure><p>好的, 接下来尽情使用自己的nginx服务器吧!</p>]]></content>
<categories>
<category>编程人生</category>
<category>nginx</category>
</categories>
<tags>
<tag>nginx</tag>
</tags>
</entry>
<entry>
<title>es在java工程中如何支持自定义的json格式的DSL</title>
<link href="/2022/11/17/es-query-dsl-wrapper/"/>
<url>/2022/11/17/es-query-dsl-wrapper/</url>
<content type="html"><![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><h3 id="版本"><a href="#版本" class="headerlink" title="版本"></a>版本</h3><ul><li>Elasticsearch : v 6.6</li><li>elasticsearch-rest-high-level-client.jar : v 6.6.2</li></ul><h3 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h3><p>想提供一个web接口, 入参就是es的查询DSL json. 然后服务器用这个DSL透传转发到Elasticsearch服务期, 将数据返回给接口.<br>简单来说, 就是想在自己服务器上实现类似kibana上查询Elasticsearch数据的功能. </p><p>比如, 我们正常的查询DSL如下:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"size"</span><span class="hljs-punctuation">:</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"query"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"bool"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"filter"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">[</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"terms"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"userCode"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">[</span><br> <span class="hljs-string">"110111"</span><br> <span class="hljs-punctuation">]</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"term"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"address"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"beijing"</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">]</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><h1 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h1><h3 id="查询"><a href="#查询" class="headerlink" title="查询"></a>查询</h3><p>如果是单纯的查询, 官方提供了一个透传查询DSL的实现类 <code>QueryBuilders.wrapperQuery()</code>, 可以通过这个QueryBuilders来实现我们自定义DSL的透传.<br>让我们先看看官方怎么说</p><blockquote><p>A query that accepts any other query as base64 encoded string.<br>This query is more useful in the context of the Java high-level REST client or transport client to also accept queries as json formatted string. In these cases queries can be specified as a json or yaml formatted string or as a query builder (which is a available in the Java high-level REST client).</p></blockquote><p>官方示例:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -X GET <span class="hljs-string">"localhost:9200/_search?pretty"</span> -H <span class="hljs-string">'Content-Type: application/json'</span> -d<span class="hljs-string">'</span><br><span class="hljs-string">{</span><br><span class="hljs-string"> "query" : {</span><br><span class="hljs-string"> "wrapper": {</span><br><span class="hljs-string"> "query" : "eyJ0ZXJtIiA6IHsgInVzZXIiIDogIktpbWNoeSIgfX0=" </span><br><span class="hljs-string"> }</span><br><span class="hljs-string"> }</span><br><span class="hljs-string">}</span><br><span class="hljs-string">'</span><br></code></pre></td></tr></table></figure><p>由此可见, 官方是将wrapper当做一个关键字类型来处理的, 将我们的查询DSL转成base64,交给es服务器解析处理.<br>好,下面就是我们将上述DSL 通过<code>QueryBuilders.wrapperQuery()</code>处理后的sql.</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"wrapper"</span> <span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"query"</span> <span class="hljs-punctuation">:</span> <span class="hljs-string">"eyJzaXplIjoxLCJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19fQ=="</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>大家可以找个base64解码网站处理一下, 发现和我们的DSL一样. 然后用官方的方式处理下</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"query"</span> <span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"wrapper"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"query"</span> <span class="hljs-punctuation">:</span> <span class="hljs-string">"eyJzaXplIjoxLCJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19fQ=="</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>唉, 发现查询报错了, 为什么呢?<br><img src="/images/tech/es/es_size_query_malformed.png" alt="size malformed"></p><p>其实仔细分析一下,很容易发现问题. 我们将base64还原,看看最后的查询DSL的样子</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"query"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"wrapper"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"query"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"size"</span><span class="hljs-punctuation">:</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"query"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"bool"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"filter"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">[</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"terms"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"userCode"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">[</span><span class="hljs-string">"110111"</span><span class="hljs-punctuation">]</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"term"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"address"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"beijing"</span><span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">]</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>发现没, query里套query, 明显查询有问题. 其实这里es解析的时候, 发现wrapper关键字, 就会将下面的query查询替换原始query. </p><p>所以我们要将wrapper的不是原始完整的DSL,而是query下面的DSL. 我们将bool关键字的JSON重新单独拿出来处理下</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"query"</span> <span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"wrapper"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"query"</span> <span class="hljs-punctuation">:</span> <span class="hljs-string">"eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19"</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>搞定, 把上述DSL放到任何一个es的web客户端查询, 都有效. </p><h3 id="聚合"><a href="#聚合" class="headerlink" title="聚合"></a>聚合</h3><p>关于聚合的功能, 官方没有提供现成的解析方法.<br>不过我觉得, 既然es的web端可以调用任意DSL, 通过java肯定能, 大不了就跳过<em>RestHighLevelClient</em>, <em>在RestLowLevelClient</em>上想办法. </p><p>// todo 等我找到合适的办法再补充.</p><h3 id="java源码"><a href="#java源码" class="headerlink" title="java源码"></a>java源码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">search</span><span class="hljs-params">(String boolJson)</span> <span class="hljs-keyword">throws</span> IOException {<br><br> <span class="hljs-type">SearchSourceBuilder</span> <span class="hljs-variable">searchSourceBuilder</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SearchSourceBuilder</span>();<br> <span class="hljs-comment">// query dsl 包装</span><br> searchSourceBuilder.query(QueryBuilders.wrapperQuery(boolJson));<br> searchSourceBuilder.size(<span class="hljs-number">1</span>);<br> <br> Optional<SearchResponse> response = restHighLevelClient.search(searchSourceBuilder);<br> } <br>}<br></code></pre></td></tr></table></figure><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><a href="https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.6/java-rest-overview.html">java-rest-client 6.6</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.6/java-rest-high-query-builders.html">java-rest-high-query-builders 6.6</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/query-dsl-wrapper-query.html">query-dsl-wrapper-query 6.6</a></li></ul>]]></content>
<categories>
<category>编程人生</category>
<category>Elasticsearch</category>
</categories>
<tags>
<tag>elasticsearch</tag>
</tags>
</entry>
<entry>
<title>macOS升级13 ventura后git无法使用的解决办法</title>
<link href="/2022/11/01/git-in-mac13-invalid/"/>
<url>/2022/11/01/git-in-mac13-invalid/</url>
<content type="html"><![CDATA[<h1 id="现象"><a href="#现象" class="headerlink" title="现象"></a>现象</h1><p>最近, mac推送了新版本系统, macOS 13 ventura版本. 然后忍不住升级了. 升级过程不赘述, 等着就行了.</p><p>升级完成后, 打开idea, 发现<code>git</code>无法使用了, 提示如下:</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">xcrun: error: invalid active developer path (<span class="hljs-regexp">/Library/</span>Developer<span class="hljs-regexp">/CommandLineTools), missing xcrun at: /</span>Library<span class="hljs-regexp">/Developer/</span>CommandLineTools<span class="hljs-regexp">/usr/</span>bin/xcrun<br></code></pre></td></tr></table></figure><p>不用说, 一定是升级导致的问题.</p><h1 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h1><p>经过一番排查, 发现是mac的开发工具Xcode需要升级到14才行. 可以使用如下命令解决, 实测有效</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">xcode-select --install<br></code></pre></td></tr></table></figure><p>基本到这里, 问题就解决了.<br>如何还不行, 可以自行到<a href="https://developer.apple.com/download/more/">官网</a>下载软件安装即可.<br><img src="/images/others/mac_xcode14.png" alt="xcode14图片"></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>没事别乱升级系统</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://stackoverflow.com/questions/52522565/git-is-not-working-after-macos-update-xcrun-error-invalid-active-developer-pa">解决方案</a></p>]]></content>
<categories>
<category>编程人生</category>
<category>git</category>
</categories>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title>JDK 19新特性简介</title>
<link href="/2022/10/20/java-jdk-v19/"/>
<url>/2022/10/20/java-jdk-v19/</url>
<content type="html"><![CDATA[<p>JDK 19在2022年10月18日发布了, 本文就是简单介绍一下这次更新的新特性与改动. </p><p>一句话总结, 这仍然是jdk的一个短期支持版本, 而且官方承诺,这次升级将尽可能的保证版本的向下兼容.</p><h2 id="新特性汇总"><a href="#新特性汇总" class="headerlink" title="新特性汇总"></a>新特性汇总</h2><h3 id="新特性"><a href="#新特性" class="headerlink" title="新特性"></a>新特性</h3><h4 id="1-预览版"><a href="#1-预览版" class="headerlink" title="1 预览版"></a>1 预览版</h4><ul><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8277131">JEP 425</a> Virtual Threads</strong>(虚拟线程) : 类似go的协程. 就是在线程上面在开辟出一个更细粒度独立运行的单元. 这个新单元就是虚拟线程, 这个线程官方叫carrier thead(承载线程?)</li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8260244">JEP 405</a> Record Patterns</strong> (记录模式) : Record Patterns 可对 record 的值进行解构,Record patterns 和 Type patterns 通过嵌套能够实现强大的、声明性的、可组合的数据导航和处理形式. </li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8282272">JEP 427</a> Pattern Matching for switch</strong> (switch模式匹配) : 第三次预览. 用 switch 表达式和语句的模式匹配,以及对模式语言的扩展来增强 Java 编程语言。将模式匹配扩展到 switch 中,允许针对一些模式测试表达式,这样就可以简明而安全地表达复杂的面向数据的查询。</li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8282048">JEP 424</a> Foreign Function & Memory API</strong> (外边函数&内存调用API): 提供了一套API 方便的调用 Java 运行时之外的代码或内存。通过高效地调用外部函数(即 JVM 之外的代码)和安全地访问外部内存(即不受 JVM 管理的内存),该 API 使 Java 程序能够调用本机库并处理本机数据,而不会像 JNI 那样危险和脆弱. (Java 在堆外内存使用的路上越走越远 🤪)</li></ul><h4 id="2-孵化器"><a href="#2-孵化器" class="headerlink" title="2 孵化器"></a>2 孵化器</h4><ul><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8277129">JEP 428</a> Structured Concurrency</strong>(结构化并发) : 用来简化并发的功能. 可以将底层多个线程视为一个工作单元, 从而实现像单线程那样的异常处理, 结果收集.</li><li><strong><a href="https://bugs.openjdk.org/browse/JDK-8280173">JEP 426</a> Vector API</strong> (向量API) : 第四孵化阶段. 向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算,该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令,从而实现优于等效标量计算的性能。向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算</li></ul><h4 id="3-主要特性"><a href="#3-主要特性" class="headerlink" title="3 主要特性"></a>3 主要特性</h4><ul><li><p><strong>Support Unicode 14.0</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8268081">JDK-8268081</a>) : 支持Unicode14.0 .</p></li><li><p><strong>New system properties for <code>System.out</code> and <code>System.err</code></strong> (<a href="https://bugs.openjdk.org/browse/JDK-8283620">JDK-8283620</a>) : 新增了<code>stdout.encoding</code>和 <code>stderr.encoding</code> 两个属性来支持标准输出和错误输出的字符集编码. 可以有效解决标准输出的乱码问题.</p></li><li><p><strong>HTTPS Channel Binding Support for Java GSS/Kerberos</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8279842">JDK-8279842</a>) : 在https连接的时候, 支持绑定token来增强安全.</p></li><li><p><strong>Additional Date-Time Formats</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8176706">JDK-8176706</a>) : <code>java.time.format.DateTimeFormatter</code> 增加了一些新的默认格式</p></li><li><p><strong>New Methods to Create Preallocated HashMaps and HashSets</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8186958">JDK-8186958</a>) : HashMap, HashSet新增了一些静态创建方法. 比如</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">HashMap.newHashMap<br>LinkedHashMap.newLinkedHashMap<br>WeakHashMap.newWeakHashMap<br>HashSet.newHashSet<br>LinkedHashSet.newLinkedHashSet<br></code></pre></td></tr></table></figure></li><li><p><strong>upport for PAC-RET Protection on Linux/AArch64</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8277204">JDK-8277204</a>) : 通过支持ARMv8.3 的PAC功能来防御RET的攻击</p><blockquote><p>When enabled, OpenJDK will use hardware features from the ARMv8.3 Pointer Authentication Code (PAC) extension to protect against Return Orientated Programming (ROP) attacks. For more information on the PAC extension see <a href="https://documentation-service.arm.com/static/602a81dbbc293d2cd05e6b09">“Providing protection for complex software”</a> or the “Pointer authentication in AArch64 state” section in the <a href="https://developer.arm.com/documentation/ddi0487/latest/">Arm ARM</a>.</p></blockquote></li><li><p><strong>Automatic Generation of the CDS Archive</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8261455">JDK-8261455</a>) : CDS的自动打包功能</p></li><li><p><strong>Windows KeyStore Updated to Include Access to the Local Machine Location</strong> (<a href="https://bugs.openjdk.org/browse/JDK-6782021">JDK-6782021</a>) : Windows新增了一些密钥储库支持访问本地位置.</p></li><li><p><strong>Break Up SEQUENCE in X509Certificate and X509Certificate in otherName</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8277976">JDK-8277976</a>) : 如题, 这两个方法的增强.</p><blockquote><p>The JDK implementation of <code>X509Certificate::getSubjectAlternativeNames</code> and <code>X509Certificate::getIssuerAlternativeNames</code> has been enhanced to additionally return the <code>type-id</code> and <code>value</code> fields of an <code>otherName</code>. The <code>value</code> field is returned as a String if it is encoded as a character string or otherwise as a byte array, which is helpful as it avoids having to parse the ASN.1 DER encoded form of the name.</p></blockquote></li><li><p><strong>(D)TLS Signature Schemes</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8280494">JDK-8280494</a>): 增加了一套新的获取TLS签名的方法.</p></li><li><p><strong>Add a -providerPath Option to jarsigner</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8281175">JDK-8281175</a>) : jarsigner新增了一个选项, 用来指定备用密钥库路径</p></li><li><p><strong>New Options for ktab to Provide Non-default Salt</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8279064">JDK-8279064</a>) : ktab命令增加了一些选项来支持自定义’盐’值.</p><blockquote><p>ktab -a username password -s altsalt</p></blockquote></li><li><p><strong>New XML Processing Limits</strong> (JDK-8270504 (not public)) : xml库解析执行增加了3个限制</p><blockquote><p><code>jdk.xml.xpathExprOpLimit</code> : XPath表达式中group数量的限制</p><p><code>jdk.xml.xpathExprOpLimit</code> : XPath表达式中operator数量的限制</p><p><code>jdk.xml.xpathTotalOpLimit</code> : XPath文件中operator总数量的限制</p></blockquote></li></ul><h3 id="bug修复"><a href="#bug修复" class="headerlink" title="bug修复"></a>bug修复</h3><ul><li><a href="https://bugs.openjdk.org/browse/JDK-8289853">JDK-8289853</a> : <a href="https://harfbuzz.github.io/">HarfBuzz</a>库升级 到 to 4.4.1, 支持最新的字体</li><li><a href="https://bugs.openjdk.org/browse/JDK-8290334">JDK-8290334</a> : <a href="https://freetype.org/">FreeType</a> 升级到 2.12.1</li><li><a href="https://bugs.openjdk.org/browse/JDK-8291897">JDK-8291897</a> : 解决TerminatingThreadLocal在virtual thread(虚拟线程)某些场景下无法注册的问题</li><li><a href="https://bugs.openjdk.org/browse/JDK-8292240">JDK-8292240</a> : 虚拟线程的承载线程, 某些情况下阻塞时无法重置状态的问题</li><li><a href="https://bugs.openjdk.org/browse/JDK-8287917">JDK-8287917</a> : macOS SDK 10.15 及更早版本下System.loadLibrary不生效的问题</li><li><a href="https://bugs.openjdk.org/browse/JDK-8292654">JDK-8292654</a> : JDK-8286115问题修复导致的G1垃圾回收器 remembered set memory footprint问题</li></ul><h3 id="移出功能"><a href="#移出功能" class="headerlink" title="移出功能"></a>移出功能</h3><ul><li><strong>Diagnostic Flag GCParallelVerificationEnabled</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8286304">JDK-8286304</a>) : 这个开关被移出了. 因为自从创建以来一直都是默认开启, 且并发效率比关闭高很多.</li><li><strong>Finalizer Implementation in SSLSocketImpl</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8212136">JDK-8212136</a>) : SSLSocket的finalizer被移除, 因为基础方法(native)已经实现相关功能.</li><li><strong>Alternate ThreadLocal Implementation of the Subject::current and Subject::callAs APIs</strong> (JDK-8282676 (not public)) : 所述类里的相应方法的备用threadlocal实现被移除.</li></ul><h3 id="废弃功能"><a href="#废弃功能" class="headerlink" title="废弃功能"></a>废弃功能</h3><ul><li><strong>java.lang.ThreadGroup</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8284161">JDK-8284161</a>) : 线程组被标记为废弃. 最大的原因是虚拟线程来了.</li><li><strong>Locale Class Constructors</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8282819">JDK-8282819</a>) : Locale的构造器被 <code>Locale.of()</code> 方法替代</li><li><strong>PSSParameterSpec(int) Constructor and DEFAULT Static Constant</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8254935">JDK-8254935</a>)</li><li><strong>OAEPParameterSpec.DEFAULT Static Constant</strong> (<a href="https://bugs.openjdk.org/browse/JDK-8284553">JDK-8284553</a>) : 因为密码学的进步, 这个Default常量已经落后了.</li></ul><h2 id="小优化"><a href="#小优化" class="headerlink" title="小优化"></a>小优化</h2><p>这里主要介绍一些小的优化, 太长可以不看.</p><ol><li><p>JDK19中, 在macOS上将Metal作为图形渲染的默认配置.</p></li><li><p>像mac或linux系统中, <code>user.home</code> 存在就用系统提供的配置 , 如果不存在, 则使用<code>$HOME</code>的配置设置这个系统属性.</p></li><li><p>线程的ClassLoader 被处理为一个特殊的可继承的Thread-local</p></li><li><p><strong>java.lang.Thread</strong> 新增了一些方法, 如果现在代码继承了Thread, 可能会导致不兼容.</p><blockquote><p>Thread.Builder模式</p><p>Thread.isVirtual()</p><p>Thread.threadId()</p><p>Thread.join(Duration)</p></blockquote></li><li><p>Double.toString(double) and Float.toString(float) 针对科学记数法的一些优化</p></li><li><p>注解的toString方法针对常量和枚举, 做了一些优化, 输出更友好.</p></li><li><p>HTTP签名鉴权时, MD5和SHA-1 因为不太安全默认被禁用.</p></li><li><p>Windows 上http多代理选择的优化</p></li><li><p><strong>java.net.InetAddress</strong> 针对一些有歧义的IPv4输入, 精细了异常提示.</p></li><li><p><strong>HttpURLConnection</strong> 的默认keep alive 可配置</p></li><li><p><strong>FileChannel.transferFrom</strong> 返回的bytes 可能会小于预订值, 这是允许的.</p><blockquote><p>FileChannel.transferFrom()的性能在Linux内核4.5及更高版本上得到了显著提高</p></blockquote></li><li><p><strong>InputStream and FilterInputStream</strong> 的mark和reset 移出了 synchronized 关键字</p></li><li><p><strong>Files.copy</strong> 方法支持将 <code>POSIX</code>属性跨文件系统复制, 前提是两个系统都支持 <code>POSIX</code></p></li><li><p><strong>FileChannel.lock(long position, long size, boolean shared)</strong> 当size指定为0时, 表示锁定 整个文件, 无论这个文件流被扩展或继承</p></li><li><p>java.time.chrono 新增了3个标准时间</p></li><li><p><strong>ForkJoinPool</strong> 和 <strong>ThreadPoolExecutor</strong> 线程池不再使用Thread.start来启动工作线程.</p></li><li><p>正则 <code>\b</code> 可以匹配ASCII 字符, 像 <code>\w</code> 一样.</p></li><li><p>支持 CLDR Version 41</p></li><li><p>LDAP, DNS, 和 RMI 的URL解析更加严格</p></li></ol><pre><code class="hljs">> -Dcom.sun.jndi.ldapURLParsing="legacy" | "compat" | "strict" (to control "ldap:" URLs)>> -Dcom.sun.jndi.dnsURLParsing="legacy" | "compat" | "strict" (to control "dns:" URLs)> -Dcom.sun.jndi.rmiURLParsing="legacy" | "compat" | "strict" (to control "rmi:" URLs)</code></pre><ol start="20"><li><p>VM Tool Interface (JVM TI) 支持虚拟线程</p></li><li><p>cpu计算时不再考虑<code>cpu.share</code> , 因为会导致jvm对当前cpu使用情况的误算, 进而影响cpu使用率.</p><blockquote><p>之前的JDK版本对Linux cgroups参数“cpu.shares”使用了错误的解释。这可能会导致JVM使用的CPU少于可用,导致JVM在容器内使用时CPU资源利用率不足。</p><p>从此JDK版本开始,默认情况下,JVM在决定各种线程池使用的线程数量时不再考虑“cpu.shares”。-XX:+UseContainerCpuShares命令行选项可用于恢复到之前的行为。此选项已被弃用,可能会在未来的JDK版本中删除。</p></blockquote></li><li><p>此后, macOS中,相同大版本的jdk将默认安装到同一目录</p><blockquote><p> Oracle JDK安装目录从</p><p><code>/Library/Java/JavaVirtualMachines/jdk-${VERSION}.jdk</code> 迁移到</p><p> <code>/Library/Java/JavaVirtualMachines/jdk-${FEATURE}.jdk</code></p><p>比如: 19.0.1 and 19.0.2 版本都将安装到 <code>/Library/Java/JavaVirtualMachines/jdk-19.jdk</code> 中</p></blockquote></li><li><p>在macOS上,只有用户钥匙串中具有正确信任设置的证书才会在钥匙串类型的钥匙库中作为受信任证书条目公开</p></li><li><p>RC2和ARCFOUR(RC4)算法已添加到java.security配置文件中的jdk.security.legacyAlgorithms安全属性中。当弱RC2或ARCFOUR算法用于与密钥存储中的秘密密钥条目关联的命令时,密钥工具会发出警告。</p></li><li><p>加密算法位数增强</p></li></ol><blockquote><p>RSA, RSASSA-PSS, DH: from 2048 to 3072</p><p>EC: from 256 to 384</p><p>AES: from 128 to 256 (if permitted by crypto policy), falls back to 128 otherwise.</p></blockquote><ol start="26"><li>ECDSA 算法的 Signature::getParameters 永远返回<code>null</code> . 参考 <a href="https://datatracker.ietf.org/doc/html/rfc5758#section-3.2">RFC 5758 Section 3.2</a></li><li>DES、DESede和MD5算法已添加到java.security配置文件中的jdk.security.legacyAlgorithms安全属性中。当弱DES或DESede算法用于与密钥库中的秘密密钥条目关联的命令时,keytool工具会发出警告。</li><li>加密算法完成支持 RFC 6125</li><li>移出了一些过时的3DES加密组件</li></ol><blockquote><p>TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA</p><p>TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA</p><p>SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA</p><p>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</p><p>TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA</p><p>TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA</p><p>SSL_RSA_WITH_3DES_EDE_CBC_SHA</p></blockquote><p>30 修复了一个StringBuilder bug. 例如, 下面代码输出”foofoobar” 而不是 “foobarfoobar”</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">StringBuilder</span> <span class="hljs-variable">builder</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>(<span class="hljs-string">"foo"</span>);<br>System.err.println(<span class="hljs-string">""</span> + builder + builder.append(<span class="hljs-string">"bar"</span>));<br></code></pre></td></tr></table></figure><ol start="31"><li><p>JavaDoc生成的API文档现在提供了一个独立的搜索页面,搜索语法已得到增强,允许多个搜索词。</p></li><li><p>JShell现在标记不建议使用的元素,并在控制台中突出显示变量和关键字。</p></li><li><p>实际的java线程堆栈大小可能与-Xss命令行选项指定的值不同;当操作系统要求时,它可以四舍五入到系统页面大小的倍数。</p></li></ol><h2 id="名词解析"><a href="#名词解析" class="headerlink" title="名词解析"></a>名词解析</h2><ul><li><p>HarfBuzz: 一个开源的用于文字塑形的软件开发库,亦即用于转换Unicode文本到字形指标及方位的过程</p></li><li><p>FreeType: 同HarfBuzz一样, 也是一个开源字体库. 它是一个用C语言实现的一个字体光栅化库。它可以用来将字符栅格化并映射成位图以及提供其他字体相关业务的支持</p></li><li><p>Preview(预览): 功能已经基本完整, 可以试用了. 可以简单理解为beta公测版. 来源于 <a href="https://openjdk.org/jeps/12">JEP12</a></p><blockquote><p>功能以预览版的形式发布,以收集有关它们的反馈而不承诺保持其向后兼容性——这意味着鼓励每个人尝试它们,但同时不鼓励在生产中使用它们。</p><p>预览功能不是开箱即用的,为了访问它们,需要使用*–enable-preview*编译器标志。</p></blockquote></li><li><p>Incubator(孵化) : 实验性 API已经到了一定阶段, 已经计划开发出一整套完整的功能.以独立模块的形式发布. 来源于 <a href="https://openjdk.org/jeps/11">JEP11</a></p></li><li><p>Experimental(实验) : vm级的早期功能, 不稳定, 功能不完整. 实验性质.</p><blockquote><p>实验性功能代表(主要是)VM 级功能的早期版本,这些功能可能是有风险的、不完整的,甚至是不稳定的。在大多数情况下,需要使用专用标志启用它们</p><p>出于比较的目的,如果一个实验功能被认为是 25%“完成”,那么一个预览功能应该至少 95%“完成”。</p><p>预览,孵化,实验三者的关系大致: 实验 => 孵化 => 预览 => 合并jdk主体功能.</p></blockquote></li><li><p>JEP : JDK Enhancement Proposal , jdk增强建议. 也就是我们常说的jdk新特性的来源. <a href="https://openjdk.org/jeps/0">JEP大全</a></p></li></ul><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="https://docs.oracle.com/en/java/javase/19/">jdk19官方文档</a></p><p><a href="https://www.oracle.com/java/technologies/javase/19-relnote-issues.html#NewFeature">jdk19 新特性文档</a></p><p><a href="https://docs.oracle.com/en/java/javase/19/migrate/preface.html#GUID-DFC2A9CB-070F-4880-9069-BD7FA8606EA2">jdk19升级指南</a></p><p><a href="https://cr.openjdk.java.net/~iris/se/19/latestSpec/">jdk19的开发计划</a></p><p><a href="https://bugs.openjdk.org/secure/Dashboard.jspa">openjdk bug反馈系统</a></p><p><a href="https://openjdk.org/">openjdk官网</a></p><p><a href="https://4comprehension.com/preview-experimental-and-incubating-features-in-java/">jdk中预览,实验,孵化的关系</a></p><p><a href="https://www.oschina.net/p/freetype">FreeType开源中国</a></p><p><a href="https://www.oschina.net/p/harfbuzz?hmsr=aladdin1e1">HarfBuzz开源中国</a></p>]]></content>
<categories>
<category>java</category>
<category>java版本特性</category>
</categories>
<tags>
<tag>java</tag>
</tags>
</entry>
<entry>
<title>dubbo 在线平滑变更zookeeper</title>
<link href="/2022/10/06/dubbo-zk-change/"/>
<url>/2022/10/06/dubbo-zk-change/</url>
<content type="html"><![CDATA[<p>记一次dubbo zk集群平滑迁移的操作方案,也可以用户dubbo双注册中心的实现方案。</p><h2 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h2><ol><li>zk 服务器版本:3.4.6</li><li>dubbo 版本:2.7.3</li><li>dubbo-spring-boot-starter 版本:2.7.3</li></ol><h2 id="技术方案"><a href="#技术方案" class="headerlink" title="技术方案"></a>技术方案</h2><ol><li>运维同学搭建好新的zk集群,保证高可用</li><li>dubbo 服务提供者通过双注册中心的方式,将服务注册到new,old 两套集群上</li><li>dubbo 消费者 通过配置双注册中心的方式,同时消费两个注册中心的服务</li><li>步骤2,步骤3可以同时进行,直到全部服务平滑注册到双注册中心</li><li>等全部到双中心后(需要运维同学确认),再次更改消费者配置,改为new 注册中心</li><li>关于即是生产者,又是消费者的项目,有两种方案:<ul><li>等依赖上游切换后再切换,或者最后切换</li><li>差异化配置,消费者单注册中心,生产者双注册中心</li></ul></li><li>消费者全部切换到新的zk 集群后,生产者逐步全部改为新的zk集群</li><li>关闭下线old zk 集群</li></ol><h2 id="关键配置"><a href="#关键配置" class="headerlink" title="关键配置"></a>关键配置</h2><p>pom 依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.dubbo<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>dubbo-spring-boot-starter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>2.7.3<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.curator<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>curator-framework<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>4.2.0<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">exclusions</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">exclusion</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>zookeeper<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.zookeeper<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">exclusion</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">exclusions</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.curator<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>curator-recipes<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>4.2.0<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.zookeeper<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>zookeeper<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.4.13<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">exclusions</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">exclusion</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>slf4j-log4j12<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.slf4j<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">exclusion</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">exclusions</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>dubbo配置:</p><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs nestedtext"><span class="hljs-attribute">dubbo</span><span class="hljs-punctuation">:</span><br> <span class="hljs-attribute">application</span><span class="hljs-punctuation">:</span><br> <span class="hljs-attribute">name</span><span class="hljs-punctuation">:</span> <span class="hljs-string">zk-move-test</span><br> <span class="hljs-attribute">consumer</span><span class="hljs-punctuation">:</span><br> <span class="hljs-attribute">registries</span><span class="hljs-punctuation">:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">id: oldzk</span><br> <span class="hljs-attribute">protocol</span><span class="hljs-punctuation">:</span> <span class="hljs-string">zookeeper</span><br> <span class="hljs-attribute">address</span><span class="hljs-punctuation">:</span> <span class="hljs-string">127.0.0.1:2181(此处随意配)</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">id: newzk</span><br> <span class="hljs-attribute">protocol</span><span class="hljs-punctuation">:</span> <span class="hljs-string">zookeeper</span><br> <span class="hljs-attribute">address</span><span class="hljs-punctuation">:</span> <span class="hljs-string">127.1.1.1:2181,127.2.2.2:2181(此处随意配)</span><br><br></code></pre></td></tr></table></figure><p>然后就能正常启动了,通过日志,会发现正常注册了两个zk中心,并且zk上也能发现注册的服务。</p><h2 id="技术验证点"><a href="#技术验证点" class="headerlink" title="技术验证点"></a>技术验证点</h2><ol><li>某服务注册到old中心,new 中心没有,消费者双注册中心是否能发现?答:可以</li><li>某服务注册到new 中心,old中心没有,消费者双注册中心是否能发现?答:可以</li><li>某服务双注册中心,然后下线某注册中心,消费者双注册中心是否收影响,能否自动转移?<br>答:经测试,new中心全部挂掉后,服务依旧会从old服务发现,功能不受影响。<br>但是,dubbo客户端会一直尝试重新连接zk中心(可配置),直到new中心重启后,zk能够自动重连成功为止。所以,我们这个场景,需要生产、消费者全部迁移到new zk 上后才能下线old zk。</li></ol><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="http://dubbo.apache.org/zh-cn/docs/user/references/registry/zookeeper.html">http://dubbo.apache.org/zh-cn/docs/user/references/registry/zookeeper.html</a></p>]]></content>
<categories>
<category>dubbo</category>
</categories>
<tags>
<tag>java</tag>
<tag>dubbo</tag>
</tags>
</entry>
<entry>
<title>鸿蒙应用开发:"request data error"问题解决</title>
<link href="/2022/10/05/exception-harmony-qa-http-request-err/"/>
<url>/2022/10/05/exception-harmony-qa-http-request-err/</url>
<content type="html"><![CDATA[<h1 id="背景介绍"><a href="#背景介绍" class="headerlink" title="背景介绍"></a>背景介绍</h1><p>本人初次接触鸿蒙系统, 想着自己在手机上开发个app玩玩, 结果第一步就遇到坑了~~</p><h1 id="开发环境"><a href="#开发环境" class="headerlink" title="开发环境"></a>开发环境</h1><ul><li>ide: DevEco Studio 3.0 Release</li><li>harmony SDK: 8</li><li>语言: ets/ts</li><li>调试: 本地真机调试</li></ul><h1 id="请求源码"><a href="#请求源码" class="headerlink" title="请求源码"></a>请求源码</h1><p>因为是个demo, 其实就是从官方demo中copy过来的, 然后换成自己的域名地址</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">'@ohos.net.http'</span>;<br><br><span class="hljs-comment">// 每一个httpRequest对应一个http请求任务,不可复用</span><br><span class="hljs-keyword">let</span> httpRequest = http.<span class="hljs-title function_">createHttp</span>();<br><span class="hljs-comment">// 用于订阅http响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息</span><br><span class="hljs-comment">// 从API 8开始,使用on('headersReceive', Callback)替代on('headerReceive', AsyncCallback)。 8+</span><br>httpRequest.<span class="hljs-title function_">on</span>(<span class="hljs-string">'headersReceive'</span>, <span class="hljs-function">(<span class="hljs-params">header</span>) =></span> {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'header: '</span> + <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(header));<br>});<br>httpRequest.<span class="hljs-title function_">request</span>(<br> <span class="hljs-comment">// 填写http请求的url地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定</span><br> <span class="hljs-string">"http://hancher.top/xxx/xxx"</span>,<br> {<br> <span class="hljs-attr">method</span>: http.<span class="hljs-property">RequestMethod</span>.<span class="hljs-property">POST</span>, <span class="hljs-comment">// 可选,默认为http.RequestMethod.GET</span><br> <span class="hljs-comment">// 开发者根据自身业务需要添加header字段</span><br> <span class="hljs-attr">header</span>: {<br> <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span><br> },<br> <span class="hljs-comment">// 当使用POST请求时此字段用于传递内容</span><br> <span class="hljs-attr">extraData</span>: {<br> <span class="hljs-string">"data"</span>: <span class="hljs-string">"data to send"</span>,<br> },<br> <span class="hljs-attr">connectTimeout</span>: <span class="hljs-number">60000</span>, <span class="hljs-comment">// 可选,默认为60s</span><br> <span class="hljs-attr">readTimeout</span>: <span class="hljs-number">60000</span>, <span class="hljs-comment">// 可选,默认为60s</span><br> }, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =></span> {<br> <span class="hljs-keyword">if</span> (!err) {<br> <span class="hljs-comment">// data.result为http响应内容,可根据业务需要进行解析</span><br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'http请求结果:'</span> + data.<span class="hljs-property">result</span>);<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'code:'</span> + data.<span class="hljs-property">responseCode</span>);<br> <span class="hljs-comment">// data.header为http响应头,可根据业务需要进行解析</span><br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'header:'</span> + <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(data.<span class="hljs-property">header</span>));<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'cookies:'</span> + data.<span class="hljs-property">cookies</span>); <span class="hljs-comment">// 8+</span><br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'http请求异常:'</span> + <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(err));<br> <span class="hljs-comment">// 当该请求使用完毕时,调用destroy方法主动销毁。</span><br> httpRequest.<span class="hljs-title function_">destroy</span>();<br> }<br> }<br>);<br></code></pre></td></tr></table></figure><p>大家可以从参考里的官方demo里比对一下, 啥都没变, 然后本地调试, 报</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-number">03B00</span>/JSApp: app Log: http请求异常:{<span class="hljs-string">"code"</span>:<span class="hljs-number">200</span>,<span class="hljs-string">"data"</span>:<span class="hljs-string">"request data error"</span>}<br></code></pre></td></tr></table></figure><p>多说一句, 因为是初次使用这个ide, 日志的打印入口都找了半天. 下面是日志的路径 </p><blockquote><p>底部导航log-> HiLog -> 选中自己的设备 -> 选择日志级别<br><img src="/images/tech/harmony/HiLog-position.png" alt="HiLog 入口"></p></blockquote><h1 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h1><ol><li>首先, 请求肯定没有发出去. 因为我没有在我的后台里发现对应的请求</li><li>有没有可能是请求参数体的问题呢?<br>这个我查了半天官方文档, 以及自己试了字符串, 对应, 数组等各种方式, 都不行. 感觉应该不是这个问题.</li><li>是不是权限的问题呢?<br>之所以会怀疑到权限的问题上, 因为在查看<a href="https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-http-0000001281201030">接口文档</a>的时候发现了这么一句话<blockquote><p>需要权限:ohos.permission.INTERNET</p></blockquote></li></ol><p>然后就去查<a href="https://developer.harmonyos.com/cn/docs/documentation/doc-guides/package-structure-0000001333321033#ZH-CN_TOPIC_0000001333321033__deviceconfig%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84">权限相关的文档</a>, 在模块的config.json下面把权限加上, 还是不行. 配置如下:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"module"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"reqPermissions"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ohos.permission.GET_NETWORK_INFO"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"reason"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"流量网络请求"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ohos.permission.INTERNET"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"reason"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"流量网络请求"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ohos.permission.SET_NETWORK_INFO"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"reason"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"流量网络请求"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ohos.permission.MANAGE_WIFI_CONNECTION"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"reason"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"WLAN网络请求"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ohos.permission.SET_WIFI_INFO"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"reason"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"WLAN网络请求"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ohos.permission.GET_WIFI_INFO"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"reason"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"WLAN网络请求"</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">]</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br><br></code></pre></td></tr></table></figure><p>请求结果还是老样子.<br>4. 最终定位: http明文请求被限制了<br>这是我在一位<a href="https://developer.huawei.com/consumer/cn/forum/topic/0202749465240950831?fid=0101587866109860105">老哥的帖子</a>里发现的一个关键节点.<br>原来正常的http请求会被系统默认禁掉, 而且官方还不给个提示, 太坑了. 官方更推荐使用https的请求方式.<br><img src="/images/tech/harmony/http_cleartextTraffic_disable.png" alt="http被禁用"><br>知道了问题所在, 解决问题就好了.config.json添加如下配置即可</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-attr">"deviceConfig"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"default"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"network"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"cleartextTraffic"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>注意, 3里的网络权限也要配置哈, 不然会报没有权限的错误. 不过这个还好, 官方有异常提示了, 就很好定位了.</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-http-0000001281201030">http请求官方文档</a><br><a href="https://developer.harmonyos.com/cn/docs/documentation/doc-guides/package-structure-0000001333321033#ZH-CN_TOPIC_0000001333321033__deviceconfig%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84">应用配置官方文档</a><br><a href="https://developer.huawei.com/consumer/cn/forum/topic/0202749465240950831?fid=0101587866109860105">感谢老哥</a> </p>]]></content>
<categories>
<category>鸿蒙</category>
<category>app</category>
</categories>
<tags>
<tag>鸿蒙</tag>
<tag>异常</tag>
</tags>
</entry>
<entry>
<title>redis常用命令总结</title>
<link href="/2022/09/30/db-redis-common-cmd/"/>
<url>/2022/09/30/db-redis-common-cmd/</url>
<content type="html"><![CDATA[<h1 id="k-v操作"><a href="#k-v操作" class="headerlink" title="k-v操作"></a>k-v操作</h1><p>普通的k-v操作</p><h2 id="批量操作"><a href="#批量操作" class="headerlink" title="批量操作"></a>批量操作</h2><h3 id="1-通过lua在redis客户端批量删除key"><a href="#1-通过lua在redis客户端批量删除key" class="headerlink" title="1.通过lua在redis客户端批量删除key"></a>1.通过lua在redis客户端批量删除key</h3><p>redis是不支持批量删除指令的, 因为redis是单线程的. 批量删除会占用redis的主线程.影响性能.</p><p>但是我认为现在redis既然已经支持异步线程操作一些后台数据了, 也就可以支持在不影响主线程性能的情况下实现 正则批量删除数据的命令了. 不知为何还是没有.</p><p>这里记录一下使用lua脚本, 在redis命令行里 通过正则批量删除缓存的功能. 核心是使用 keys命令. 这个会阻塞主线程, 如果量比较大, 慎用.</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs lua">eval <span class="hljs-string">"local keys=redis.call('keys',KEYS[1]);local count=0;for i,v in ipairs(keys) do redis.call('del',v); count=i end; return count"</span> <span class="hljs-number">1</span> key正则表达式<br></code></pre></td></tr></table></figure><p>将最后的’key正则表达式’换成自己想删除的keys的表达式即可.</p><p>关于量的问题, 我删除线上5w条缓存,秒删,无任何影响.各位同学自行参考量级.</p><p>不定期更新中…</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://redis.io/commands/eval/">redis官网</a></p>]]></content>
<categories>
<category>redis</category>
</categories>
<tags>
<tag>redis</tag>
</tags>
</entry>
<entry>
<title>redis脚本命令</title>
<link href="/2022/09/29/db-redis-cmd-script/"/>
<url>/2022/09/29/db-redis-cmd-script/</url>
<content type="html"><![CDATA[<p>redis内置了<a href="https://redis.io/docs/manual/programmability/lua-api/">Lua 5.1</a>引擎. 可以很方便的执行lua脚本.<br>有了这个利器, 我们就可以在redis命令的基础上自己组合命令并原子性执行了.</p><h2 id="eval命令执行lua脚本"><a href="#eval命令执行lua脚本" class="headerlink" title="eval命令执行lua脚本"></a>eval命令执行lua脚本</h2><p>如果要调用</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs lua">EVAL script numkeys [key [key ...]] [<span class="hljs-built_in">arg</span> [<span class="hljs-built_in">arg</span> ...]]<br><br></code></pre></td></tr></table></figure><ul><li>script lua脚本, 字符串</li><li>numkeys 后置keys的数量. 0表示没有参数</li><li>key 的参数. 可以在lua脚本里通过 “KEYS[index]”来指定key. 会在脚本执行前替换</li><li>arg 参数. 和keys类似. keys数量之后的参数都是args. 可以用ARGV[index]指定.</li></ul><p>demo</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs redis"># 执行一段lua脚本, 返回输入的各个参数. 前2个是key, 后面的都是args<br>redis> EVAL "return { KEYS[1], KEYS[2], ARGV[1], ARGV[2], ARGV[3] }" 2 key1 key2 arg1 arg2 arg3<br>1) "key1"<br>2) "key2"<br>3) "arg1"<br>4) "arg2"<br>5) "arg3"<br></code></pre></td></tr></table></figure><h1 id="lua脚本"><a href="#lua脚本" class="headerlink" title="lua脚本"></a>lua脚本</h1><h2 id="lua脚本调redis指令"><a href="#lua脚本调redis指令" class="headerlink" title="lua脚本调redis指令"></a>lua脚本调redis指令</h2><p>最常用的很是就是call方法了</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">redis.call(<span class="hljs-built_in">command</span> [,arg...])<br><br></code></pre></td></tr></table></figure><ul><li>command redis命令</li><li>args redis命令所需参数</li></ul><p>demo</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 执行lua脚本, 设置key=a , v = 1</span><br>redis> <span class="hljs-built_in">eval</span> <span class="hljs-string">"redis.call('set','a',1)"</span> 0<br><br><span class="hljs-comment"># 验证</span><br>redis> get a <span class="hljs-comment"># 结果 1</span><br></code></pre></td></tr></table></figure><p>不定期更新中…</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://redis.io/docs/manual/programmability/eval-intro/">redis lua</a></p>]]></content>
<categories>
<category>redis</category>
</categories>
<tags>
<tag>redis</tag>
</tags>
</entry>
<entry>
<title>有意思的小问题,java代码过长</title>
<link href="/2022/09/22/exception-java-method-too-big/"/>
<url>/2022/09/22/exception-java-method-too-big/</url>
<content type="html"><![CDATA[<p>最近在练习编程的时候,发现一个问题. 就是在测试一个排序功能时, 如果写入大批量的参数, 会报 “java: 代码过长” 的问题. </p><p>编程这么多年了, 第一次遇到这种问题, 还挺神奇的. 记录和总结一下.</p><h1 id="现象重现"><a href="#现象重现" class="headerlink" title="现象重现"></a>现象重现</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> a = <span class="hljs-string">"2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2"</span>;<br> <span class="hljs-comment">// 上面的a 多复制几遍</span><br><br> System.out.println(a);<br> }<br>}<br></code></pre></td></tr></table></figure><p>很简单的代码, 没事走两步</p><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs avrasm"><span class="hljs-symbol">java:</span> 代码过长<br></code></pre></td></tr></table></figure><h1 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h1><p>首先, 顾名思义, 很明细就是字符长度超过了java 一个类文件的最大长度限制导致的.</p><p>那么java类文件的长度是多少呢?<br>经过一番不是很辛苦的查询, 定位到了原因: </p><blockquote><p>There is a 64K byte-code size limit on a method<br>java中一个方法的最大长度是64Kb</p></blockquote><p>很明细, 我们的这个方法长度超了.</p><h1 id="问题解决"><a href="#问题解决" class="headerlink" title="问题解决"></a>问题解决</h1><p>原因找到了, 要解决也就很简单了. 只要做到规避方法的最大长度就行了. </p><ul><li>方法1 : 将一个大方法拆成多个小方法, 将一个大类拆成多个小类. 说实话, 一个64kb的类确实有点大了.</li><li>方法2 : 将测试数据不要放到java类里, 而是放到配置文件中, 使用的时候通过流加载到内存中. 文件的存储量是无限的</li></ul><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>其实只要符合正常的java编码规范, 我们基本上是遇不到上面的问题的.<br>只有在一些极限测试的时候, 我们通常会将测试内容放到代码里, 才可能碰到. </p><p>只能说: 没用的小知识又增多了🐶</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://stackoverflow.com/questions/2407912/code-too-large-compilation-error-in-java">java code too large</a></p>]]></content>
<categories>
<category>java异常</category>
</categories>
<tags>
<tag>java</tag>
<tag>异常</tag>
</tags>
</entry>
<entry>
<title>使用私有仓库发布 GitHub Pages</title>
<link href="/2022/09/13/site-github-repo-private/"/>
<url>/2022/09/13/site-github-repo-private/</url>
<content type="html"><![CDATA[<h2 id="验证私有库发布文章"><a href="#验证私有库发布文章" class="headerlink" title="验证私有库发布文章"></a>验证私有库发布文章</h2><p>相信已经成功使用github建站的朋友们肯定会有一个想法, 就是可不可以将文章仓库私有也能实现建站的功能呢?</p><p>答案是可以的, 本文就是一个将一个公共仓库站点私有化后的例子. 并将过程总结一下, 方便大家参考使用.</p><h2 id="站点私有化过程的的变与不变"><a href="#站点私有化过程的的变与不变" class="headerlink" title="站点私有化过程的的变与不变"></a>站点私有化过程的的变与不变</h2><p>首先,使用github pages建站, 项目名就固定死了,必然是<用户名>.github.io<br>其次,如果有域名的话,像域名配置等一些配置也是需要放到 外露站点下的.</p><p>除此之外, 其他的内容就就是可以变的了.因为github.io站点最终暴露的就是一个编译好的web静态网站.<br>至于源码是不是在这里,无所谓的.之所以源码与静态站点放到一起,核心还是方便github的 action自动编译.</p><p>好了, 了解了这些核心要素, 我们可以操作的部分也就有了.<br>就像我们正常的开发流程一样,我们可以把整个网站的发布流程拆分为两部分: <strong>打包</strong> 与 <strong>发布</strong>.</p><p>其中打包可以在我们的私有仓库中进行, 然后将打包后的静态网站 发布到github.io公仓中.<br>这里就用到了github的 action 自动编译功能. 其会在我们提交代码的时候触发一个操作,<br>调用一段脚本命令执行.</p><p>而这段脚本的核心就是通过git 将编译后的静态站点push 到github.io公仓中.</p><p>流程如下:<br><img src="/images/site/private_repo_flow.png" alt="发布流程图"></p><h2 id="操作步骤"><a href="#操作步骤" class="headerlink" title="操作步骤"></a>操作步骤</h2><ol><li>先在github上创建一个私有仓库: private_repo.</li><li>将github.io的源码迁移到新的仓库中.</li><li>准备github跨仓库git 提交代码权限, 参考下一节github申请person token</li><li>在private_repo 的action 中创建打包脚本, 脚本内容参考附录1<br><img src="/images/site/private_repo_new_action.png" alt="创建新action"></li></ol><h2 id="github申请person-token"><a href="#github申请person-token" class="headerlink" title="github申请person token"></a>github申请person token</h2><h3 id="第一步-获取token"><a href="#第一步-获取token" class="headerlink" title="第一步, 获取token"></a>第一步, 获取token</h3><ol><li>个人设置里找到入口<br><img src="/images/site/github_person_token_1.png" alt="person token 1"></li><li>找到开发者设置<br><img src="/images/site/github_person_token_2.png" alt="person token 2"></li><li>创建token<br><img src="/images/site/github_person_token_3.png" alt="person token 3"></li></ol><p><strong>这一步一定要记住创建好的token, 因为后面要用,而且这个页面刷新后就没了</strong>.</p><p>其实有过使用ssh经验的朋友们应该已经意识到了, 这一步其实就是一个创建ssh公钥的过程. 只不过这个公钥是放到github服务器上了.</p><h3 id="第二步-私有库配置"><a href="#第二步-私有库配置" class="headerlink" title="第二步, 私有库配置"></a>第二步, 私有库配置</h3><p>有了token, 正常来说就能提交代码了. 但是要知道这个东西是放到脚本里的, 明文暴露还是不太安全.<br>所以我们要在私有库里将这个token配置成一个环境变量,通过环境变量引用的方式使用,这样就安全多了.</p><ol><li>找到private_repo, 配置token环境变量<br><img src="/images/site/github_person_token_4.png" alt="person token 4"><br><img src="/images/site/github_person_token_5.png" alt="person token 5"> </li><li>配置完后, 我们就可以在脚本里通过环境变量来使用token了.</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs doc">${ { secrets.YOUR_REPO_SECRET_NAME } }<br></code></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>可以尝试在私有库private_repo里提交文章了.</p><p>同时你会发现, github.io公仓里的内部全部被静态网站内容覆盖了. 不用担心,这是正常的.</p><h2 id="附录1-完整脚本"><a href="#附录1-完整脚本" class="headerlink" title="附录1 : 完整脚本"></a>附录1 : 完整脚本</h2><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><code class="hljs dockerfile"><span class="hljs-comment"># ci name</span><br><span class="hljs-comment"># ci 脚本名称</span><br>name: Deploy Jekyll site to public github repos<br><br>on:<br> <span class="hljs-comment"># Runs on pushes targeting the default branch</span><br> <span class="hljs-comment"># 当代码 push 到 main 分支时, 执行该脚本</span><br> push:<br> branches: [<span class="hljs-string">"main"</span>]<br><br> <span class="hljs-comment"># Allows you to run this workflow manually from the Actions tab</span><br> <span class="hljs-comment"># 允许你 手动执行 这个脚本</span><br> workflow_dispatch:<br><br><span class="hljs-comment"># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</span><br><span class="hljs-comment"># GITHUB_TOKEN 的操作权限</span><br>permissions:<br> contents: read<br> pages: write<br> id-token: write<br><br><span class="hljs-comment"># Allow one concurrent deployment</span><br>concurrency:<br> group: <span class="hljs-string">"pages"</span><br> cancel-in-progress: true<br><br><span class="hljs-comment"># job 执行任务, 可以有多个, 默认并行运行, 可以通过 needs 关键字来设置依赖的其他 jobs</span><br><span class="hljs-comment"># step : 任务下的执行步骤, 一个job 会有多个步骤</span><br><span class="hljs-comment"># 操作: 一个step 下可以执行多个操作, 通常一行脚本为一个操作</span><br>jobs:<br> <span class="hljs-comment"># Build job</span><br> build:<br> runs-on: ubuntu-latest<br> steps:<br> - name: Checkout<br> uses: actions/checkout@v3<br> <span class="hljs-comment"># 准备ruby 环境</span><br> - name: Setup Ruby<br> uses: ruby/setup-ruby@<span class="hljs-number">0</span>a29871fe2b0200a17a4497bae54fe5df0d973aa <span class="hljs-comment"># v1.115.3</span><br> with:<br> ruby-version: <span class="hljs-string">'3.0'</span> <span class="hljs-comment"># Not needed with a .ruby-version file</span><br> bundler-cache: true <span class="hljs-comment"># runs 'bundle install' and caches installed gems automatically</span><br> cache-version: <span class="hljs-number">0</span> <span class="hljs-comment"># Increment this number if you need to re-download cached gems</span><br><span class="hljs-comment"># - name: Setup Pages</span><br><span class="hljs-comment"># id: pages</span><br><span class="hljs-comment"># uses: actions/configure-pages@v2</span><br> <span class="hljs-comment"># 打包 jekyll</span><br> - name: Build with Jekyll<br> <span class="hljs-comment"># Outputs to the './_site' directory by default</span><br><span class="hljs-comment"># run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"</span><br> <span class="hljs-keyword">run</span><span class="language-bash">: bundle <span class="hljs-built_in">exec</span> jekyll build --verbose</span><br> <span class="hljs-keyword">env</span>:<br> JEKYLL_ENV: production<br> <span class="hljs-comment"># 更新 打tar包</span><br><span class="hljs-comment"># - name: Upload artifact</span><br><span class="hljs-comment"># # Automatically uploads an artifact from the './_site' directory by default</span><br><span class="hljs-comment"># uses: actions/upload-pages-artifact@v1</span><br><br> - name: 增加外露github文件<br> working-directory: ./_site <span class="hljs-comment"># jekyll 默认 build 到 _site 目录,因此设置命令执行的目录为 ./_site</span><br> <span class="hljs-keyword">run</span><span class="language-bash">: | <span class="hljs-comment"># run 后面加个 ‘|’ 然后换行可以同时执行多个命令,每行一个</span></span><br> cp ../README.md .<br> <br> - name: 部署_site到博客网站仓库<br> working-directory: ./_site<br> <span class="hljs-keyword">run</span><span class="language-bash">: |</span><br> git init<br> git checkout -b main<br> git <span class="hljs-keyword">add</span><span class="language-bash"> -A</span><br> git -c <span class="hljs-keyword">user</span>.name=<span class="hljs-string">'your_name'</span> -c <span class="hljs-keyword">user</span>.email=<span class="hljs-string">'your_email'</span> commit -m <span class="hljs-string">'anything'</span> <br> git push <span class="hljs-string">"https://${ { github.actor } }:${ { secrets.YOUR_REPO_SECRET_NAME } }@github.com/YOUR_GITHUB_IO_REPO"</span> HEAD:main -f -q<br></code></pre></td></tr></table></figure><ul><li>将上面的your_name,your_email,anything 替换成自己的内容</li><li>YOUR_REPO_SECRET_NAME 替换成privat_repo的secret_token_name</li><li>YOUR_GITHUB_IO_REPO 替换成自己的github.io公仓</li></ul><h2 id="附录2-github-action-脚本简介"><a href="#附录2-github-action-脚本简介" class="headerlink" title="附录2 : github action 脚本简介"></a>附录2 : github action 脚本简介</h2><p>在github的action模块里创建了一个脚本后, 就会在项目的.github/workflows里创建一个对应的文件.</p><p>通过workflow 大致可以理解这个模块的功能, 就是一个工作流.<br>在一些操作执行(通常是提交代码)后, 触发一系列的后续自动化操作, 这些操作可以是并行的,也可以是串行的. </p><p>而这些后续操作是通过一些action脚本组织起来的. 整体上看,一个action由以下部分组成.</p><ul><li>jobs: 任务. 最大的逻辑单元. 可以有多个. jobs 默认是并行运行,可以通过 needs 关键字来设置依赖的其他 jobs。</li><li>steps: 步骤. job的执行步骤. 顺序执行. 可以job可以有多个步骤.</li><li>runs: 操作. 也可以理解为指令(bash命令等). 通常一行脚本就是一个操作. 并非所有步骤都会运行操作,但是操作都是在步骤中运行的</li></ul><p>无论是job,还是step, 都可以理解一组指令集合. 他们都是可以配置id的. 可以通过id来互相依赖, 互相调用.</p><p>其他的可以参考里的官方文档.</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://github.com/peaceiris/actions-gh-pages">常见脚本</a></p><p><a href="https://docs.github.com/cn/actions/learn-github-actions/contexts#github-context">github action 官方文档</a></p><p><a href="https://www.xheldon.com/tech/the-using-of-github-pages.html">免费使用私有仓库发布 GitHub Pages</a></p><p><a href="https://juejin.cn/post/7008847699919241229">通过 GitHub Actions 实现私有仓库的免费 Github Pages 部署</a></p>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>建站</tag>
</tags>
</entry>
<entry>
<title>Elasticsearch重建索引</title>
<link href="/2022/09/12/es-reindex/"/>
<url>/2022/09/12/es-reindex/</url>
<content type="html"><![CDATA[<h2 id="为什么要重建索引"><a href="#为什么要重建索引" class="headerlink" title="为什么要重建索引"></a>为什么要重建索引</h2><p>众所周知, es是一种高效的数据查询,检索引擎。 可以对海量的数据进行快速的查询。但是在使用es的时候,经常会遇到一个比较尴尬的问题,那就是es索引里的字段类型是固定的,不可修改的。而es的索引又是可以自动扩展字段的。 这个时候自动扩展出来的字段就是使用了默认的字段类型(通常是text类型)。</p><p>惊不惊喜!</p><p>查看索引字段的方法:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><code class="hljs json">GET /my_index<br><br><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"my_index"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"mappings"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"_doc"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"dynamic_templates"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">[</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"string_fields"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"match_mapping_type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"mapping"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"keyword"</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"field_a"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"keyword"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"field_b"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"date"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"format"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"yyyy-MM-dd HH:mm:ss"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"field_c"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"text"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"norms"</span><span class="hljs-punctuation">:</span><span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"fields"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"keyword"</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"keyword"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"ignore_above"</span><span class="hljs-punctuation">:</span><span class="hljs-number">256</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br><br></code></pre></td></tr></table></figure><p>聪明的你肯定想到了, 如果我们忘记加字段了, 或者字段加错了要修改字段类型咋办? 这个时候场景的办法就只有一个了:</p><p>重建索引!</p><p>从数据库角度来看,也就是新增新表, 迁移数据, 删除老表.</p><h2 id="背景-与-目标"><a href="#背景-与-目标" class="headerlink" title="背景 与 目标"></a>背景 与 目标</h2><p>Elasticsearch 版本 : v 6.6<br>目标: 在my_index 索引上新增一个integer字段. 且中间不间断服务.</p><h2 id="S1-使用别名代替实体索引"><a href="#S1-使用别名代替实体索引" class="headerlink" title="S1: 使用别名代替实体索引"></a>S1: 使用别名代替实体索引</h2><p>使用<a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/indices-aliases.html">aliases api</a><br>创建一个别名my_index 指向实际的索引my_idex.<br>这样,我们通过my_index访问数据时, 就不会直接访问my_index索引了, 而是通过别名指向对应的索引.</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ada">POST /_aliases<br>{<br> <span class="hljs-string">"actions"</span> : [<br> { <span class="hljs-string">"add"</span> : { "<span class="hljs-type">index</span><span class="hljs-string">" : "</span>my_index<span class="hljs-string">", "</span>alias<span class="hljs-string">" : "</span>my_index<span class="hljs-string">" } }</span><br><span class="hljs-string"> ]</span><br><span class="hljs-string">}</span><br></code></pre></td></tr></table></figure><h2 id="S2-创建新的索引"><a href="#S2-创建新的索引" class="headerlink" title="S2: 创建新的索引"></a>S2: 创建新的索引</h2><p>使用 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/indices-put-mapping.html">put mapping api</a><br>打算新增一个字段 <strong>fiedl_d</strong>, 数据类型.</p><p>因为默认的string类型是keyword (假设, 意会即可), 所以如果想要新增一个整型类型, 就不能通过自动的扩展字段使用默认类型的方式. 所以必须显式的指定字段类型.</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs applescript"><span class="hljs-keyword">put</span> my_index2<br><br>{<br> <span class="hljs-string">"_doc"</span>:{<br> <span class="hljs-string">"dynamic_templates"</span>:[<br> {<br> <span class="hljs-string">"string_fields"</span>:{<br> <span class="hljs-string">"match_mapping_type"</span>:<span class="hljs-string">"string"</span>,<br> <span class="hljs-string">"mapping"</span>:{<br> <span class="hljs-string">"type"</span>:<span class="hljs-string">"keyword"</span><br> }<br> }<br> }<br> ],<br> <span class="hljs-string">"properties"</span>:{<br> <span class="hljs-string">"field_a"</span>:{<br> <span class="hljs-string">"type"</span>:<span class="hljs-string">"keyword"</span><br> },<br> <span class="hljs-string">"field_b"</span>:{<br> <span class="hljs-string">"type"</span>:<span class="hljs-string">"date"</span>,<br> <span class="hljs-string">"format"</span>:<span class="hljs-string">"yyyy-MM-dd HH:mm:ss"</span><br> },<br> <span class="hljs-string">"field_c"</span>:{<br> <span class="hljs-string">"type"</span>:<span class="hljs-string">"text"</span>,<br> <span class="hljs-string">"norms"</span>:<span class="hljs-literal">false</span>,<br> <span class="hljs-string">"fields"</span>:{<br> <span class="hljs-string">"keyword"</span>:{<br> <span class="hljs-string">"type"</span>:<span class="hljs-string">"keyword"</span>,<br> <span class="hljs-string">"ignore_above"</span>:<span class="hljs-number">256</span><br> }<br> }<br> },<br> <span class="hljs-string">"field_d"</span>:{<br> <span class="hljs-string">"type"</span>:<span class="hljs-string">"integer"</span><br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="S3-迁移数据"><a href="#S3-迁移数据" class="headerlink" title="S3: 迁移数据"></a>S3: 迁移数据</h2><p>使用 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/docs-reindex.html">Reindex api</a><br>将老的my_index 数据迁移到新的my_index2上.<br>这一步比较耗时, 做好心理准备.</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs css">POST _reindex<br>{<br> "<span class="hljs-selector-tag">source</span>": {<br> "index": <span class="hljs-string">"my_index"</span><br> },<br> "dest": {<br> "index": <span class="hljs-string">"my_index2"</span><br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="S4-变更别名"><a href="#S4-变更别名" class="headerlink" title="S4: 变更别名"></a>S4: 变更别名</h2><p>使用<a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/indices-aliases.html">aliases api</a><br>将别名从老的index, 指向新的index</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs ada">POST /_aliases<br>{<br> <span class="hljs-string">"actions"</span> : [<br> { <span class="hljs-string">"remove"</span> : { "<span class="hljs-type">index</span><span class="hljs-string">" : "</span>my_index<span class="hljs-string">", "</span>alias<span class="hljs-string">" : "</span>my_index<span class="hljs-string">" } },</span><br><span class="hljs-string"> { "</span>add<span class="hljs-string">" : { "</span>index<span class="hljs-string">" : "</span>my_index2<span class="hljs-string">", "</span>alias<span class="hljs-string">" : "</span>my_index<span class="hljs-string">" } }</span><br><span class="hljs-string"> ]</span><br><span class="hljs-string">}</span><br></code></pre></td></tr></table></figure><h2 id="S5-将老索引的增量数据迁移到新索引上"><a href="#S5-将老索引的增量数据迁移到新索引上" class="headerlink" title="S5: 将老索引的增量数据迁移到新索引上"></a>S5: 将老索引的增量数据迁移到新索引上</h2><p>处理迁移期间my_index 发生的增量数据</p><figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs fsharp">POST <span class="hljs-keyword">_reindex</span><br>{<br> <span class="hljs-string">"size"</span><span class="hljs-operator">:</span> 10000,<br> <span class="hljs-string">"source"</span><span class="hljs-operator">:</span> {<br> <span class="hljs-string">"index"</span><span class="hljs-operator">:</span> <span class="hljs-string">"my_index"</span>,<br> <span class="hljs-string">"sort"</span><span class="hljs-operator">:</span> { <span class="hljs-string">"date"</span><span class="hljs-operator">:</span> <span class="hljs-string">"desc"</span> }<br> },<br> <span class="hljs-string">"dest"</span><span class="hljs-operator">:</span> {<br> <span class="hljs-string">"index"</span><span class="hljs-operator">:</span> <span class="hljs-string">"my_index2"</span><br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="S6-删除老索引"><a href="#S6-删除老索引" class="headerlink" title="S6: 删除老索引"></a>S6: 删除老索引</h2><p>使用 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/indices-delete-index.html">delete api</a></p><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs actionscript"><span class="hljs-keyword">delete</span> my_index<br></code></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>至此, 整个重建索引过程完毕.<br>很麻烦吧.<br>为了不这么麻烦, 以后要给es 增加新字段时, 一定要先通过maping的方式先新增字段. 然后再在代码里处理相对应的逻辑.</p><p>over!</p><h2 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h2><p>可以通过put mapping的方式, 增量补充索引的字段</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs json">PUT my_index<br><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"mappings"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"_doc"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"manager"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"age"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"integer"</span> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"text"</span> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"employees"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"nested"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"age"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"integer"</span> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"text"</span> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/docs.html">官方文档-api</a><br><a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.6/indices.html">官方文档-api2</a></p>]]></content>
<categories>
<category>编程人生</category>
<category>Elasticsearch</category>
</categories>
<tags>
<tag>elasticsearch</tag>
</tags>
</entry>
<entry>
<title>beaudar评论插件及常见问题解决</title>
<link href="/2022/09/09/site-beaudar-error/"/>
<url>/2022/09/09/site-beaudar-error/</url>
<content type="html"><![CDATA[<h2 id="什么是beaudar"><a href="#什么是beaudar" class="headerlink" title="什么是beaudar"></a>什么是beaudar</h2><blockquote><p>Beaudar 名称源于粤语“表达”的发音,是 Utterances 的中文版本,是一款基于github issue 的一个评论插件</p></blockquote><ul><li>开源</li><li>没有追踪,没有广告,永久免费</li><li>不保留数据,所有数据保存在用户 GitHub issue 中</li><li>有源于 GitHub primer 的多个主题</li><li>轻量化,没有字体下载,没有 JS 框架加载</li></ul><p>本站的评论模块就是使用的 <a href="https://beaudar.lipk.org/">beaudar</a></p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><p>选择 Beaudar 将要连接的仓库, 也就是网站对应的github仓库。</p><ol><li>确保仓库是公开的,否则您的读者将无法查看 Issue(评论)。</li><li>安装并确保 Beaudar app 已在仓库中安装,否则用户将无法发表评论。</li></ol><p><a href="https://github.com/apps/beaudar">点击这里安装beaudar</a></p><p><img src="/images/site/comment/beaudar-app-install.png" alt="安装beaudar"><br>3. 将网站对应的分支配置到配置中,并确保 Issues 功能已打开。<br>4. 将以下脚本标记添加到博客的模板中。 将其放置在要显示注释的位置。 使用 .beaudar 和 .beaudar-frame 选择器自定义布局。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><script src=<span class="hljs-string">"https://beaudar.lipk.org/client.js"</span><br> repo=<span class="hljs-string">"在此处输入仓库"</span><br> branch=<span class="hljs-string">"main"</span><br> issue-term=<span class="hljs-string">"pathname"</span><br> label=<span class="hljs-string">"tag"</span><br> theme=<span class="hljs-string">"github-light"</span><br> keep-theme=<span class="hljs-string">"false"</span><br> loading=<span class="hljs-string">"false"</span><br> comment-order=<span class="hljs-string">"desc"</span><br> input-position=<span class="hljs-string">"top"</span><br> crossorigin=<span class="hljs-string">"anonymous"</span><br> <span class="hljs-keyword">async</span>><br></script><br></code></pre></td></tr></table></figure><p>配置解读</p><ul><li>src : 评论插件客户端地址,不能动</li><li>repo : 网站仓库</li><li>branch : 网站部署分支</li><li>issue-term : 博客文章 与 Issue 的映射<ul><li>pathname : Beaudar 将搜索标题包含博客文章 URL 路径的 Issue。如果未找到匹配的 Issue,则当有人首次对您的信息发表评论时,Beaudar 会自动创建一个 Issue。</li><li>url : Beaudar 将搜索标题包含博客文章 URL 的 Issue。 如果未找到匹配的 Issue,则当有人首次对您的信息发表评论时,Beaudar 会自动创建一个 Issue</li><li>title: Beaudar 将搜索标题包含博客文章标题的 Issue。 如果未找到匹配的 Issue,则当有人首次对您的信息发表评论时,Beaudar 会自动创建一个 Issue</li><li>og:title : Beaudar 将搜索标题包含博客文章页面 meta 元素 og:title 的 Issue。 如果未找到匹配的 Issue,则当有人首次对您的信息发表评论时,Beaudar 会自动创建一个 Issue</li><li>preferred-color-scheme:您可以配置 Beaudar 以按编号加载特定的 Issue。 问题不会自动创建</li><li>可以指定自己的名称: 将 Beaudar 配置为搜索标题包含您选择的特定名称的问题。如果未找到匹配问题,Beaudar 将自动创建第一次有人评论您的帖子时。Issue 的标题将是您选择的名称</li></ul></li><li>theme: 主题</li><li>keep-theme: 将主题设置保存到页面的 sessionStorage,修改主题后刷新,主题设置不会丢失</li><li>label: 将分配给 Beaudar 创建的问题的标签,标签名称区分大小写。该标签必须存在于您的仓库中,无法附加不存在的标签。标签名称支持添加表情符号</li><li>loading: 点击加载图标可跳转至官方页面。</li><li>comment-order: 评论的发表时间排序</li><li>input-position: 评论框位置,当选择将评论顺序设置为“降序”时,建议将评论框放置在“顶部”。因为当评论数量很多时候,发表评论后可以第一时间看到评论发表成功</li><li>crossorigin: 跨域</li></ul><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="缺少-“beaudar-json”-配置"><a href="#缺少-“beaudar-json”-配置" class="headerlink" title="缺少 “beaudar.json” 配置"></a>缺少 “beaudar.json” 配置</h3><p><img src="/images/site/comment/beaudar-error.png" alt="缺少 "beaudar.json" 配置"><br>原因: 现在github的默认主分支是main, 不在是master. 所以分支找不到.<br>配置的时候指定分支为main就可以了.</p><h3 id="hexo-fluid主题下的使用"><a href="#hexo-fluid主题下的使用" class="headerlink" title="hexo-fluid主题下的使用"></a>hexo-fluid主题下的使用</h3><p>将上述的 script脚本 在<code>_config.yml</code>中按如下方式添加即可</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># 自定义底部 HTML 内容(位于 footer 上方),也可用于外部引入 js css 这些操作,注意不要和 post.custom 配置冲突</span><br><span class="hljs-attr">custom_html:</span> <span class="hljs-string">'</span><br><span class="hljs-string"><script src="https://beaudar.lipk.org/client.js"</span><br><span class="hljs-string"> repo="Hanchers/hanchers.github.io"</span><br><span class="hljs-string"> branch="main"</span><br><span class="hljs-string"> issue-term="pathname"</span><br><span class="hljs-string"> label="tag"</span><br><span class="hljs-string"> theme="github-light"</span><br><span class="hljs-string"> keep-theme="false"</span><br><span class="hljs-string"> loading="false"</span><br><span class="hljs-string"> comment-order="desc"</span><br><span class="hljs-string"> input-position="top"</span><br><span class="hljs-string"> crossorigin="anonymous"</span><br><span class="hljs-string"> async></span><br><span class="hljs-string"></script></span><br><span class="hljs-string">'</span><br></code></pre></td></tr></table></figure><p>但是效果比较丑,需要自己去调整一下css才与整个主题适配, 建议使用fluid主题已经集成好的组件。<br><img src="/images/site/comment/beaudar_in_fluid.png" alt="fluid主题下的beaudar 评论插件效果"></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://githubhelp.com/beaudar/beaudar">beaudar Q&A</a></p>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>建站</tag>
<tag>beaudar</tag>
<tag>评论插件</tag>
</tags>
</entry>
<entry>
<title>如何写好java注释</title>
<link href="/2022/09/01/java-java-doc-skill/"/>
<url>/2022/09/01/java-java-doc-skill/</url>
<content type="html"><![CDATA[<h2 id="什么是java注释"><a href="#什么是java注释" class="headerlink" title="什么是java注释"></a>什么是java注释</h2><p>要回答这个问题,先提出一个说法:我们写的代码是给人看的,不是给机器看的.<br>如果你不认可这个说法,下面关于代码注释的一些总结想必对你也没啥帮助.<br>如果你认可这个说法, 那么下面的文章希望会给你一些帮助.</p><p>回到本段的主题,什么是java注释呢?</p><ol><li><p>首先,注释是面向代码维护者的: 注释是对代码逻辑的一段说明, 方便后续的代码维护者能够快速的了解这段代码的含义,并依次为基础能够在现有代码的基础上做修改与维护.</p></li><li><p>其次,注释是面向系统使用者的: 要知道类似于java,或者spring这样偏向于底层,框架类的代码.全世界有数以万计的使用者的,这些人没精力也没有必要在调用api的时候去了解这段代码的底层实现逻辑,所以这段代码的api说明文档就显得很重要了.而代码上的注释就是相关api文档的主要来源.</p></li><li><p>通过javadoc命令,可以将注释抽离生成html文档,比如官方的<a href="https://docs.oracle.com/en/java/javase/16/docs/api/index.html">java16API文档</a></p></li></ol><h2 id="如何写好java注释"><a href="#如何写好java注释" class="headerlink" title="如何写好java注释"></a>如何写好java注释</h2><p>想要写好代码的注释,我们要从注释的面向用户来考虑.不同的用户关注点还是有点差异的.</p><h4 id="面向代码维护者"><a href="#面向代码维护者" class="headerlink" title="面向代码维护者"></a>面向代码维护者</h4><p>作为一名代码的维护者,当我们拿到一份没有任何注释的代码的时候,相信大家内心的感受是相同的.毕竟人类的悲欢并不相通, 除非看到没有注释的代码.</p><p>如果有了解设计模式的朋友,相信一定听说过一个设计原则:<strong>开闭原则</strong>, 对扩展开发,对修改关闭. 那么大家有没有想过为啥会有这个原则呢?</p><p>翻译一下: 修改现有的代码是有风险的,但是如果是扩展写新代码的话,作为开发者的你,是了解当前功能的前后背景的,而且你无论怎么写都不会对历史功能产生影响.所以对我们的代码设计能力提出来要求.</p><p>再翻译一下: 原来的代码既然跑的好好的,为啥要修改它呢, 改出问题来算谁的?</p><p>再翻译一下: 为啥我们不敢改以前的代码呢?谁知道当时为啥要写这段逻辑,谁知道这段逻辑有谁在用?改好了没有夸, 改崩了有锅背.</p><p>这就是我们维护老代码的困境!</p><p>作为一名程序员,我们要有一个概念,我们的代码是我们某个时刻的思维逻辑的固化.<br>如果是一个比较简单的逻辑,注释可以简单写写.<br>如果是一段比较复杂的逻辑,那么我们的注释要能够描述清楚我们当时的这段逻辑的背景(为什么要写这段逻辑), 意图(这段逻辑要实现什么效果), 用法(这段逻辑改如何使用,以及谁在用), 最好能有修改建议.</p><p>在业界开源的代码里有很多比较好的例子,来个spring的:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * AOP Alliance MethodInterceptor for declarative transaction</span><br><span class="hljs-comment"> * management using the common Spring transaction infrastructure</span><br><span class="hljs-comment"> * ({<span class="hljs-doctag">@link</span> org.springframework.transaction.PlatformTransactionManager}/</span><br><span class="hljs-comment"> * {<span class="hljs-doctag">@link</span> org.springframework.transaction.ReactiveTransactionManager}).</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <p>Derives from the {<span class="hljs-doctag">@link</span> TransactionAspectSupport} class which</span><br><span class="hljs-comment"> * contains the integration with Spring's underlying transaction API.</span><br><span class="hljs-comment"> * TransactionInterceptor simply calls the relevant superclass methods</span><br><span class="hljs-comment"> * such as {<span class="hljs-doctag">@link</span> #invokeWithinTransaction} in the correct order.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <p>TransactionInterceptors are thread-safe.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> Rod Johnson</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> Juergen Hoeller</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@see</span> TransactionProxyFactoryBean</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@see</span> org.springframework.aop.framework.ProxyFactoryBean</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@see</span> org.springframework.aop.framework.ProxyFactory</span><br><span class="hljs-comment"> */</span><br></code></pre></td></tr></table></figure><h4 id="面向api使用者"><a href="#面向api使用者" class="headerlink" title="面向api使用者"></a>面向api使用者</h4><p>对于api的使用者,注释文档的要求相对简单些.</p><p>他们只关心这个方法的功能是什么, 以及入参有哪些,出参会有哪些, 会不会抛异常等, 会不会返回null等. 他们不关心这个方法的内部实现逻辑是啥, 比如一个排序方法, 使用者不关心这个方法内部使用冒泡排序,还是快排, 只要能完成诉求就行.</p><p>所以,面向api的使用者的注释,原则就是让他知道这个方法怎么使用就行了.</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">* Returns an Image object that can then be painted on the screen. </span><br><span class="hljs-comment">* The url argument must specify an absolute <a href="#{<span class="hljs-doctag">@link</span>}">{<span class="hljs-doctag">@link</span> URL}</a>. The name</span><br><span class="hljs-comment">* argument is a specifier that is relative to the url argument. </span><br><span class="hljs-comment">* <p></span><br><span class="hljs-comment">* This method always returns immediately, whether or not the </span><br><span class="hljs-comment">* image exists. When this applet attempts to draw the image on</span><br><span class="hljs-comment">* the screen, the data will be loaded. The graphics primitives </span><br><span class="hljs-comment">* that draw the image will incrementally paint on the screen. </span><br><span class="hljs-comment">*</span><br><span class="hljs-comment">* <span class="hljs-doctag">@param</span> url an absolute URL giving the base location of the image</span><br><span class="hljs-comment">* <span class="hljs-doctag">@param</span> name the location of the image, relative to the url argument</span><br><span class="hljs-comment">* <span class="hljs-doctag">@return</span> the image at the specified URL</span><br><span class="hljs-comment">* <span class="hljs-doctag">@see</span> Image</span><br><span class="hljs-comment">*/</span><br><span class="hljs-keyword">public</span> Image <span class="hljs-title function_">getImage</span><span class="hljs-params">(URL url, String name)</span> {<br><span class="hljs-keyword">try</span> {<br><span class="hljs-keyword">return</span> getImage(<span class="hljs-keyword">new</span> <span class="hljs-title class_">URL</span>(url, name));<br>} <span class="hljs-keyword">catch</span> (MalformedURLException e) {<br><span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>}<br>}<br></code></pre></td></tr></table></figure><h2 id="写好java注释常用的技巧"><a href="#写好java注释常用的技巧" class="headerlink" title="写好java注释常用的技巧"></a>写好java注释常用的技巧</h2><h4 id="常用的注释tag"><a href="#常用的注释tag" class="headerlink" title="常用的注释tag"></a>常用的注释tag</h4><ol><li>@param 方法参数</li><li>@return 方法返回值</li><li>@throws 方法抛出的异常, java1.2后添加</li><li>@exception 同@throws</li><li>@see 查看参考代码</li><li>@author 标识作者</li><li>@version 代码版本</li><li>@since 从某个版本开始引入</li><li>@serial( @serialField @serialData)</li><li>@deprecated 废弃某个版本的代码</li></ol><h4 id="常用的内联tag"><a href="#常用的内联tag" class="headerlink" title="常用的内联tag"></a>常用的内联tag</h4><ol><li>{@code} : 代码高亮,等同于 <code>{@literal}</code></li></ol><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dust"><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">code</span>></span></span><span class="hljs-template-variable">{@literal}</span><span class="language-xml"><span class="hljs-tag"></<span class="hljs-name">code</span>></span></span><br></code></pre></td></tr></table></figure><ol start="2"><li>{@docRoot} : 标记文档的根路径,用来实现相对路径</li></ol><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs perl">/**<br>* See the <ahref=<span class="hljs-string">"{<span class="hljs-variable">@docRoot</span>}/copyright.html"</span>>Copyright<<span class="hljs-regexp">/a>.</span><br><span class="hljs-regexp">*/</span><br></code></pre></td></tr></table></figure><ol start="3"><li>{@inheritDoc} : 继承某个文档,嵌套文档使用</li><li>{@link url} : 在注释文本区域内, 内联一个链接</li></ol><figure class="highlight leaf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs leaf">Use the {@link <span class="hljs-punctuation">#</span><span class="hljs-keyword">getComponentAt</span><span class="hljs-params">(<span class="hljs-keyword">in</span><span class="hljs-variable">t</span>, <span class="hljs-keyword">in</span><span class="hljs-variable">t</span>)</span> getComponentAt} method.<br><br></code></pre></td></tr></table></figure><ol start="5"><li>{@linkplain url label} 相对于{@link}, 支持自定义label文案来代指这段url</li></ol><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">Refer <span class="hljs-keyword">to</span> {@linkplain <span class="hljs-keyword">add</span>() the overridden <span class="hljs-keyword">method</span>}.<br>会显示为<br>Refer <span class="hljs-keyword">to</span> <span class="hljs-string">'the overridden method'</span>.<br><br></code></pre></td></tr></table></figure><ol start="6"><li>{@literal} 转义用, 方便显示一些特殊字符</li><li>{@value} 常量时, 会直接显示标注代码的值</li></ol><h3 id="注释语句中的小技巧"><a href="#注释语句中的小技巧" class="headerlink" title="注释语句中的小技巧"></a>注释语句中的小技巧</h3><h4 id="param-return-throws"><a href="#param-return-throws" class="headerlink" title="@param @return @throws"></a>@param @return @throws</h4><p>一个正常方法必然会用到的注释tag,<br>@param 代表方法的入参,<br>@return 代表方法的返回值.<br>@throws 代表可能引发方法中断的异常. 等同与 @exception<br>这里需要特别说明一下, 有些人可能觉得只有那些受检异常(也就是必须在方法签名里声明的异常)才需要在注释里声明@throws. 其实不是的. 所有引发程序中断的异常, 包括运行时异常都可以在注释里说明, 也可以在方法签名里添加. 尤其是自定义的异常.<br>举个例子:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Return the underlying ThreadPoolExecutor for native access.</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> the underlying ThreadPoolExecutor (never {<span class="hljs-doctag">@code</span> null})</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@throws</span> IllegalStateException if the ThreadPoolTaskExecutor hasn't been initialized yet</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> ThreadPoolExecutor <span class="hljs-title function_">getThreadPoolExecutor</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IllegalStateException {<br>Assert.state(<span class="hljs-built_in">this</span>.threadPoolExecutor != <span class="hljs-literal">null</span>, <span class="hljs-string">"ThreadPoolTaskExecutor not initialized"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.threadPoolExecutor;<br>}<br></code></pre></td></tr></table></figure><p>这里的IllegalStateException 是一个RuntimeException异常, 我们在方法里可能抛出这个异常, 最好是在方法签名里声明一下, 然后在代码注释里说明一下.</p><h4 id="see"><a href="#see" class="headerlink" title="@see"></a>@see</h4><p>当前代码可以参考的其他代码, 后面跟代码的全路径,可以是类,方法,属性等.具体参考如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@see</span> #field<br><span class="hljs-meta">@see</span> #Constructor(Type, Type...)<br><span class="hljs-meta">@see</span> #Constructor(Type id, Type id...)<br><span class="hljs-meta">@see</span> #method(Type, Type,...)<br><span class="hljs-meta">@see</span> #method(Type id, Type, id...)<br><span class="hljs-meta">@see</span> Class<br><span class="hljs-meta">@see</span> Class#field<br><span class="hljs-meta">@see</span> Class#Constructor(Type, Type...)<br><span class="hljs-meta">@see</span> Class#Constructor(Type id, Type id)<br><span class="hljs-meta">@see</span> Class#method(Type, Type,...)<br><span class="hljs-meta">@see</span> Class#method(Type id, Type id,...)<br><span class="hljs-meta">@see</span> <span class="hljs-keyword">package</span>.Class<br><span class="hljs-meta">@see</span> <span class="hljs-keyword">package</span>.Class#field<br><span class="hljs-meta">@see</span> <span class="hljs-keyword">package</span>.Class#Constructor(Type, Type...)<br><span class="hljs-meta">@see</span> <span class="hljs-keyword">package</span>.Class#Constructor(Type id, Type id)<br><span class="hljs-meta">@see</span> <span class="hljs-keyword">package</span>.Class#method(Type, Type,...)<br><span class="hljs-meta">@see</span> <span class="hljs-keyword">package</span>.Class#method(Type id, Type, id)<br><span class="hljs-meta">@see</span> <span class="hljs-keyword">package</span><br></code></pre></td></tr></table></figure><h4 id="author"><a href="#author" class="headerlink" title="@author"></a>@author</h4><p>标识当前代码的作者是谁, 可以一个,也可以有多个.</p><h4 id="version-since"><a href="#version-since" class="headerlink" title="@version @since"></a>@version @since</h4><p>都是版本相关的tag<br>@version 标识当前版本好, 编译的时候会用到. 符合SCCS规范.<br>@since 标识这段代码的引入版本</p><h4 id="serial-serialField-serialData"><a href="#serial-serialField-serialData" class="headerlink" title="@serial @serialField @serialData"></a>@serial @serialField @serialData</h4><p>序列化相关的属性, 标识哪些字段可以序列化,哪些不行.</p><h4 id="deprecated"><a href="#deprecated" class="headerlink" title="@deprecated"></a>@deprecated</h4><p>废弃某段代码,表示不再维护,并在一段时间后会被删除. 标记后, 相关的引用位置会被标记为删除线.<br>个人认为这个tag还是很有用的:</p><ol><li>创建代码容易删除难, 这个tag能够很好的帮忙我们去下线不再维护的代码,减轻维护压力.(愿世界上屎山越来越少)</li><li>方便我们代码升级. 如果我们要升级一段代码,先将老代码废弃,然后通过@see,引导用户使用新的方法.</li></ol><figure class="highlight leaf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs leaf">/**<br>* @deprecated As of JDK 1.1, replaced by <br>* <br> setBounds<br>* @see <span class="hljs-punctuation">#</span><span class="hljs-keyword">setBounds</span><span class="hljs-params">(<span class="hljs-keyword">in</span><span class="hljs-variable">t</span>,<span class="hljs-keyword">in</span><span class="hljs-variable">t</span>,<span class="hljs-keyword">in</span><span class="hljs-variable">t</span>,<span class="hljs-keyword">in</span><span class="hljs-variable">t</span>)</span><br>*/<br></code></pre></td></tr></table></figure><h4 id="html标签来排版"><a href="#html标签来排版" class="headerlink" title="html标签来排版"></a>html标签来排版</h4><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ada"><p> : 新起一段<br><br> : 换行<br></code></pre></td></tr></table></figure><h4 id="代码块样式"><a href="#代码块样式" class="headerlink" title="代码块样式"></a>代码块样式</h4><p>想在JAVA的注释中添加一段代码,并且可以优雅的编译出来还是挺麻烦的.下面提供一种方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 下面的代码注释可以按代码格式编译</span><br><span class="hljs-comment"> * <pre class=code></span><br><span class="hljs-comment"> * citys : [</span><br><span class="hljs-comment"> * beijing,</span><br><span class="hljs-comment"> * shanghai</span><br><span class="hljs-comment"> * ]</span><br><span class="hljs-comment"> * </pre></span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> List<String>> citys;<br><br></code></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html">官方文档</a><br><a href="https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html">javadoc - The Java API Documentation Generator</a><br><a href="https://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/deprecation/deprecation.html">How and When To Deprecate APIs</a></p>]]></content>
<categories>
<category>编程人生</category>
</categories>
<tags>
<tag>java</tag>
</tags>
</entry>
<entry>
<title>算法套路</title>
<link href="/2022/08/23/algorithm-skill/"/>
<url>/2022/08/23/algorithm-skill/</url>
<content type="html"><![CDATA[<p>平时在刷算法题的时候经常会遇到一些套路, 也就是一些类似的小技巧. 这类技巧能够快速的帮我们解决某一类的算法问题. 这里就是整理一下自己遇到的,总结的算法技巧, 以及这些技巧可以应用与哪些场景.</p><h2 id="1-滑动窗口-双指针"><a href="#1-滑动窗口-双指针" class="headerlink" title="1 滑动窗口(双指针)"></a>1 滑动窗口(双指针)</h2><p>个人理解,滑动窗口 的基础就是双指针. 即一个快指针代指窗口的头部, 一个慢指针代指窗口的尾部. 滑动窗口经过的地方就是我们要处理的数据. </p><p><img src="/images/tech/algorithm/move-window.png" alt="滑动窗口示意图"></p><p>如图所示,我们的滑动创建运动方向从左往右, 已经经过了区域1,正在经历区域2,3,4. 还没有经历区域5.</p><h3 id="1-1-滑动窗口的特性"><a href="#1-1-滑动窗口的特性" class="headerlink" title="1.1 滑动窗口的特性"></a>1.1 滑动窗口的特性</h3><p>通过上面分析可知, 滑动窗口是一个窗口经过一段连续的区域,并实时计算统计经过区域内的一些值. 所以滑动窗口很适用的场景特点也就出来了:</p><ol><li>单维度数据,比如字符串, 比如一维数组.</li><li>结果值有连续特性要求的. 因为滑动窗口必然是连续经过计算区域的.所遇到的值也必然是连续的.</li></ol><h3 id="1-2-常见场景"><a href="#1-2-常见场景" class="headerlink" title="1.2 常见场景"></a>1.2 常见场景</h3><ol><li><a href="https://leetcode.cn/problems/longest-substring-without-repeating-characters/">计算一个字符串内不含有重复字符的</a></li></ol><h2 id="未完待续"><a href="#未完待续" class="headerlink" title="未完待续"></a>未完待续</h2>]]></content>