Aspose Word Java字节码分析与破解研究

2024-03-02 01:58

本文主要是介绍Aspose Word Java字节码分析与破解研究,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

周六闲来无事,想到之前很容易对aspose.cell v21.4完成破解研究(见博客),但是对aspose.words没有完成破解分析。

这次换个思路,基于License进行class文件分析。

一:从官网下载

下载地址:Java Word Processor API | Aspose.Words for Java;

或者使用maven方式进行jar包下载:

<!-- maven配置 --><repositories><repository><id>AsposeJavaAPI</id><name>Aspose Java API</name><url>https://repository.aspose.com/repo/</url></repository></repositories><dependencies><dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>21.12</version><type>pom</type></dependency></dependencies>

需注意:从官网下载的jar包为实时混淆,混淆的class名各不相同,因此本博客列出关键方法,但是方法名不保证一致。 

二:License安装

见Licensing and Subscription | Documentation

关键代码:

com.aspose.words.License license = new com.aspose.words.License();
license.setLicense("path:\\Aspose.Words.Java.lic");

给出License示例:

<License><Data><LicensedTo>Aspose Scotland Team</LicensedTo><EmailTo>billy.lundie@aspose.com</EmailTo><LicenseType>Developer OEM</LicenseType><LicenseNote>Limited to 1 developer, unlimited physical locations</LicenseNote><OrderID>140408052324</OrderID><UserID>94236</UserID><OEM>This is a redistributable license</OEM><Products><Product>Aspose.Total for Java</Product></Products><EditionType>Enterprise</EditionType><SerialNumber>9a59547c-41f0-428b-ba72-7c4368f151d7</SerialNumber><SubscriptionExpiry>20221231</SubscriptionExpiry><LicenseVersion>3.0</LicenseVersion><LicenseInstructions>http://www.aspose.com/corporate/purchase/license-instructions.aspx</LicenseInstructions></Data><Signature>FO3PHsblgDt8F59sMT1l1amyi9qk2V6E8dQkIP7LdTJSxDibNEFu1zOinQbqFfKv/ruttvcxoROkc1tUe0DtO6cP1Zf6J0VemgSY8i/LZECTGszRqJVQRZ0MoVnBhuPAJk5eli7fhVcF8hWd3E4XQ3LzfmJCuaj2NEteRi5Hrfg=</Signature>
</License>

进入License.class文件,基于IDEA自带反编译功能,我们可以看到源码:下图zzWf9为混淆类,可能每人下载类均不相同,记住该类名。

public class License {public License() {zzZ3T.zzZAj();}public void setLicense(String licenseName) throws Exception {if (licenseName == null) {throw new NullPointerException(zzZ3T.zzZAj().zzMk(new byte[]{105, 108, 101, 99, 115, 110, 78, 101, 109, 97, 101}));} else {(new zzWf9()).zzY98(licenseName, zzZdu.zzXGb());}}public void setLicense(InputStream stream) throws Exception {if (stream == null) {throw new NullPointerException(zzZ3T.zzZAj().zzMk(new byte[]{116, 115, 101, 114, 109, 97}));} else {(new zzWf9()).zzY98(stream);}}
}

可以看到License类中有zzWf9的2个重载方法,关键在于zzY98方法(最后提示混淆方法不保证一致!)。

进入核心验证方法:

void zzY98(InputStream var1) throws Exception {if (var1 == null) {throw new NullPointerException(zzZ3T.zzZAj().zzMk(new byte[]{116, 115, 101, 114, 109, 97}));} else if (!this.zzXvE(var1)) {throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{110, 73, 97, 118, 105, 108, 32, 100, 105, 108, 101, 99, 115, 110, 32, 101, 105, 115, 110, 103, 116, 97, 114, 117, 46, 101, 80, 32, 101, 108, 115, 97, 32, 101, 97, 109, 101, 107, 115, 32, 114, 117, 32, 101, 104, 116, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 105, 102, 101, 108, 119, 32, 115, 97, 110, 32, 116, 111, 109, 32, 100, 111, 102, 105, 101, 105, 46, 100}));} else {zzWhX var2 = zzWcN;if (var2 == null) {var2 = zzYf8(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 76, 46, 99, 105, 110, 101, 101, 115, 66, 46, 97, 108, 107, 99, 105, 76, 116, 115}), (String)null);zzWcN = var2;}zzWhX var3 = zzX3l;if (var3 == null) {var3 = zzYf8(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 76, 46, 99, 105, 110, 101, 101, 115, 66, 46, 97, 108, 107, 99, 105, 76, 116, 115}), zzZDr);zzX3l = var3;}if (zznY.zzZHa() > 0) {throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{110, 73, 97, 118, 105, 108, 32, 100, 105, 108, 101, 99, 115, 110, 32, 101, 105, 115, 110, 103, 116, 97, 114, 117, 46, 101, 80, 32, 101, 108, 115, 97, 32, 101, 97, 109, 101, 107, 115, 32, 114, 117, 32, 101, 104, 116, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 105, 102, 101, 108, 119, 32, 115, 97, 110, 32, 116, 111, 109, 32, 100, 111, 102, 105, 101, 105, 46, 100}));} else if (!var2.contains(this.zzWO4) && !var3.contains(this.zzWO4)) {boolean var4 = false;String[] var5 = this.zzWJJ;int var6 = var5.length;int var7 = 0;while(var7 < var6) {String var8 = var5[var7];if (!var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 32}) + "Java") && !var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 32}) + "Java")) {if ((var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97})) || var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 84, 46, 116, 111, 108, 97}))) && "Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{78, 46, 84, 69}))) {var4 = true;break;}if (!var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 80, 32, 111, 114, 117, 100, 116, 99, 70, 32, 109, 97, 108, 105, 121})) && !var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 84, 46, 116, 111, 108, 97, 80, 32, 111, 114, 117, 100, 116, 99, 70, 32, 109, 97, 108, 105, 121}))) {if (var8.equals("Aspose.Words for Java")) {var4 = true;break;}String var9;String var10;if ("Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{78, 46, 84, 69}))) {var9 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{97, 88, 97, 109, 105, 114, 46, 110, 110, 65, 114, 100, 105, 111, 100});var10 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 88, 32, 109, 97, 114, 97, 110, 105, 65, 46, 100, 110, 111, 114, 100, 105});String var11 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{110, 65, 114, 100, 105, 111, 32, 100, 105, 118, 32, 97, 97, 88, 97, 109, 105, 114, 110});String var12 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 65, 32, 100, 110, 111, 114, 100, 105, 118, 32, 97, 105, 88, 32, 109, 97, 114, 97, 110, 105});String var13 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{79, 105, 32, 83, 105, 118, 32, 97, 97, 88, 97, 109, 105, 114, 110});String var14 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 105, 32, 83, 79, 118, 32, 97, 105, 88, 32, 109, 97, 114, 97, 110, 105});String var15 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{97, 77, 32, 99, 105, 118, 32, 97, 97, 88, 97, 109, 105, 114, 110});String var16 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 77, 32, 99, 97, 118, 32, 97, 105, 88, 32, 109, 97, 114, 97, 110, 105});if (var8.equals(var9) || var8.equals(var10) || var8.equals(var11) || var8.equals(var12) || var8.equals(var13) || var8.equals(var14) || var8.equals(var15) || var8.equals(var16)) {var4 = true;break;}}if ("Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{97, 74, 97, 118, 65, 46, 100, 110, 111, 114, 100, 105}))) {var9 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{110, 65, 114, 100, 105, 111, 100});var10 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 65, 32, 100, 110, 111, 114, 100, 105});if (var8.equals(var9) || var8.equals(var10)) {var4 = true;break;}}if (var8.equals("Aspose.Words") && "Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{78, 46, 84, 69}))) {var4 = true;break;}if (var8.equals("Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{80, 32, 111, 114, 117, 100, 116, 99, 70, 32, 109, 97, 108, 105, 121}))) {var4 = true;break;}++var7;continue;}var4 = true;break;}var4 = true;break;}if (!var4) {throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{104, 84, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 115, 105, 110, 32, 116, 111, 118, 32, 108, 97, 100, 105, 102, 32, 114, 111, 116, 32, 105, 104, 32, 115, 114, 112, 100, 111, 99, 117, 46, 116}));} else {Date var17 = (new SimpleDateFormat("yyyy.MM.dd")).parse("2021.12.01");if (var17.after(this.zzWec)) {throw new IllegalStateException(this.zzY4i.zzXJX(new byte[]{84, 104, 101, 32, 115, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 32, 105, 110, 99, 108, 117, 100, 101, 100, 32, 105, 110, 32, 116, 104, 105, 115, 32, 108, 105, 99, 101, 110, 115, 101, 32, 97, 108, 108, 111, 119, 115, 32, 102, 114, 101, 101, 32, 117, 112, 103, 114, 97, 100, 101, 115, 32, 117, 110, 116, 105, 108, 32}) + (new SimpleDateFormat(this.zzY4i.zzXJX(new byte[]{100, 100, 32, 77, 77, 77, 32, 121, 121, 121, 121}), Locale.ENGLISH)).format(this.zzWec) + this.zzY4i.zzXJX(new byte[]{44, 32}) + this.zzY4i.zzXJX(new byte[]{98, 117, 116, 32, 116, 104, 105, 115, 32, 118, 101, 114, 115, 105, 111, 110, 32, 111, 102, 32, 116, 104, 101, 32, 112, 114, 111, 100, 117, 99, 116, 32, 119, 97, 115, 32, 114, 101, 108, 101, 97, 115, 101, 100, 32, 111, 110, 32}) + (new SimpleDateFormat(this.zzY4i.zzXJX(new byte[]{100, 100, 32, 77, 77, 77, 32, 121, 121, 121, 121}), Locale.ENGLISH)).format(var17) + this.zzY4i.zzXJX(new byte[]{46, 32}) + this.zzY4i.zzXJX(new byte[]{80, 108, 101, 97, 115, 101, 32, 114, 101, 110, 101, 119, 32, 116, 104, 101, 32, 115, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 32, 111, 114, 32, 117, 115, 101, 32, 97, 32, 112, 114, 101, 118, 105, 111, 117, 115, 32, 118, 101, 114, 115, 105, 111, 110, 32, 111, 102, 32, 116, 104, 101, 32, 112, 114, 111, 100, 117, 99, 116, 46}));} else if ((new Date()).after(this.zzYS9)) {throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{104, 84, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 97, 104, 32, 115, 120, 101, 105, 112, 101, 114, 46, 100}));} else if (this.zzWec.getYear() < 2099) {this.zzWnu = zzYjw.zzVS4;zzWhV = this;}}} else {throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{104, 84, 115, 105, 108, 32, 99, 105, 110, 101, 101, 115, 105, 32, 32, 115, 105, 100, 97, 115, 108, 98, 100, 101, 32, 44, 108, 112, 97, 101, 101, 115, 99, 32, 110, 111, 97, 116, 116, 99, 65, 32, 112, 115, 115, 111, 32, 101, 111, 116, 111, 32, 116, 98, 105, 97, 32, 110, 32, 97, 101, 110, 32, 119, 105, 108, 101, 99, 115, 110, 46, 101}));}}}

这个方法有2个注意点:

1、日期判断硬编码:21.12版本注册日期应为2021.12.01日之后,2099年之前;

2、第4行zzXvE方法进行License校验(因为验证不通过提示就是License Invalid)。

本License内容原为.NET,<Product>Aspose.Total for .NET</Product>,这时signature验证通过,但是本博客修改为<Product>Aspose.Total for Java</Product>,因此在校验时会提示License Invalid。

综上,哎~对了,又到了字节码编辑的时候了。但是接下来有个地方是有难度的,

三、class文件编辑

主要在zzWf9.zzXvE校验方法,其中有2个地方修改:

源码:

private boolean zzXvE(InputStream var1) throws Exception {DocumentBuilderFactory var2 = zzXcV.zzWQB();DocumentBuilder var3 = var2.newDocumentBuilder();Document var4 = var3.parse(var1);Element var5 = var4.getDocumentElement();Element var6 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{97, 68, 97, 116}));Element var7 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{105, 83, 110, 103, 116, 97, 114, 117, 101}));boolean var8 = zzWxF((Node)var6, (Node)var7);Element var9 = zzWKk(var6, zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 115, 116}));NodeList var10 = var9.getElementsByTagName(zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 116}));this.zzWJJ = new String[var10.getLength()];for(int var11 = 0; var11 < this.zzWJJ.length; ++var11) {this.zzWJJ[var11] = var10.item(var11).getFirstChild().getNodeValue();}this.zzWO4 = zzZqK(var6, zzZ3T.zzZAj().zzMk(new byte[]{101, 83, 105, 114, 108, 97, 117, 78, 98, 109, 114, 101}));this.zzWec = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{117, 83, 115, 98, 114, 99, 112, 105, 105, 116, 110, 111, 120, 69, 105, 112, 121, 114}));this.zzYS9 = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{105, 76, 101, 99, 115, 110, 69, 101, 112, 120, 114, 105, 121}));return var8;}

1、zzXvE,方法体第7行,

boolean var8 = zzWxF((Node)var6, (Node)var7);

这一行需要跟踪调试,经多次调试发现最后是由ThreadLocal对象进行控制,列出zzWf9关键调用链:

// 进入方法一:
private static boolean zzWxF(Node var0, Node var1) throws Exception {return zzZp8((Node)var0, (Node)var1, (String)null);}// 进入方法二:
private static boolean zzZp8(Node var0, Node var1, String var2) throws Exception {byte[] var3;if (var0 != null) {StringBuilder var4 = new StringBuilder();zzZp8(var4, var0);var3 = var4.toString().getBytes("UTF-16LE");} else {var3 = new byte[0];}byte[] var6;if (var1 != null) {String var5 = var1.getFirstChild().getNodeValue();var6 = zzZbv.zz3m(var5);} else {var6 = new byte[0];}if (var2 == null) {if (zzWjD(var0)) {var2 = var6.length == 128 ? zzZDr : zzWxc;} else {var2 = var6.length == 128 ? zzYAU : zzX1P;}}return zzZp8(var3, var6, var2);}// 进入方法三:
private static boolean zzZp8(byte[] var0, byte[] var1, String var2) throws Exception {String var3 = zzZ3T.zzZAj().zzMk(new byte[]{81, 65, 66, 65});byte[] var4 = zzZbv.zz3m(var3);byte[] var5 = zzZbv.zz3m(var2);return zzxN.zzZp8(var5, var4, var0, var1);}

 发现进入zzxN.zzZp8方法,列出zzxN调用链:

// 进入方法一:
static boolean zzZp8(byte[] var0, byte[] var1, byte[] var2, byte[] var3) throws Exception {return zzZqK(var0, var1, var2, var3);}// 进入方法二:
private static boolean zzZqK(byte[] var0, byte[] var1, byte[] var2, byte[] var3) throws Exception {boolean var4 = false;if (var3.length != var0.length) {zznY.zzWUu(1);var4 = true;}byte[] var5 = zzZbv.zzXvz(var3);zzZto var6;byte[] var7;byte[] var8 = zzZbv.zzZo9(var7 = zzZbv.zzZp8(var6 = new zzZto(var0, var1), var5), var6.getModulus().bitLength() >> 3);byte[] var9 = zzWxF(var0, var2, var8.length);if (var8.length != var9.length) {var4 = true;} else {for(int var10 = 0; var10 < var8.length; ++var10) {var4 = var4 || var8[var10] != var9[var10];}}zzTq var14;(var14 = new zzTq(var7)).zzYBj(var5.length, var9.length, false);zzXUN var11;(var11 = new zzXUN()).write(var7, 0, var7.length);var14.zzZp8(var11, var9, var5.length);if (var14.zzWw9()) {var5[0] = 0;var5[1] = 17;}zzZ3V var12 = new zzZ3V(var14, var8, true, var14.zzWw9());int[] var13 = new int[var7.length];zzXcS.zzZp8(var7, 0, var13, 0, var7.length);var12.zzZp8(var14);var12.zzZ8j();var12.zzbx(true);var14.zzZ7L(true);zzZ3T.zzZAj().zzMk(new byte[]{50, 49, 52, 51, 54, 53, 56, 55, 48, 57, 66, 65, 68, 67, 70, 69});var12.zzZ8j();var12.zzWs3(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 87, 46, 114, 111, 115, 100}));return !var4;}

 在该方法中发现返回校验结果了,再次寻找关键代码,经多次调试终于发现倒数第5行:

var12.zzbx(true);

该方法为关键方法,进入zzZ3V.zzbx方法,列出调用链:

final void zzbx(boolean var1) throws Exception {this.zzJu = 0;if (this.zzWza.zzGe() > 0L) {int var2 = (int)this.zzWza.zzGe() / 2 + 1;byte[] var3 = this.zzWza.zzWnM();if (zznY.zzZxN() == 255) {zznY.zzWWo(128);}int var4 = var3.length - 1;for(int var5 = 0; var5 < var2; ++var5) {if ((var3[var4] & 255) == 255) {++this.zzhe;}if (var3[var4] != (byte)this.zzZ3G.zz8A()[var4]) {zznY.zzWUu(128);//注意这行}--var4;}}}

 特别注意这行:zznY.zzWUu(128);

终于来到最后的zznY类:

static void zzWUu(int var0) {zzXKA.set(Integer.valueOf(var0));}

在这一行,需要进行修改使这个ThreadLocal设置值为0(具体请自己调试,后续有>0判断)。

2、zzXvE,最后返回修改为true;

4、总结:

1、zznY.zzWUu修改,每次设置值为0:

static void zzWUu(int var0) {byte var1 = 0;//需自己修改字节码文件zzXKA.set(Integer.valueOf(var1));}

2、zzWf9.zzXvE修改,返回校验结果true:

private boolean zzXvE(InputStream var1) throws Exception {DocumentBuilderFactory var2 = zzXcV.zzWQB();DocumentBuilder var3 = var2.newDocumentBuilder();Document var4 = var3.parse(var1);Element var5 = var4.getDocumentElement();Element var6 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{97, 68, 97, 116}));Element var7 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{105, 83, 110, 103, 116, 97, 114, 117, 101}));boolean var8 = zzWxF((Node)var6, (Node)var7);Element var9 = zzWKk(var6, zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 115, 116}));NodeList var10 = var9.getElementsByTagName(zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 116}));this.zzWJJ = new String[var10.getLength()];for(int var11 = 0; var11 < this.zzWJJ.length; ++var11) {this.zzWJJ[var11] = var10.item(var11).getFirstChild().getNodeValue();}this.zzWO4 = zzZqK(var6, zzZ3T.zzZAj().zzMk(new byte[]{101, 83, 105, 114, 108, 97, 117, 78, 98, 109, 114, 101}));this.zzWec = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{117, 83, 115, 98, 114, 99, 112, 105, 105, 116, 110, 111, 120, 69, 105, 112, 121, 114}));this.zzYS9 = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{105, 76, 101, 99, 115, 110, 69, 101, 112, 120, 114, 105, 121}));return true;//自己修改字节码文件}

修改上述两处,即可完成对Aspose.Words jar破解,需配合License文件。

示例代码:

//读取word文件,保存为PDF文件。
License license = new License();
license.setLicense("E:\\Aspose.License.xml");Document doc = new Document("E:\\Aspose.Crack.docx");SaveOutputParameters parameters = doc.save("E:\\cracked.pdf", SaveOptions.createSaveOptions(SaveFormat.PDF));System.out.println(parameters.getContentType());

 转化后文件,无水印无页码限制:

 

搞定收工。

这篇关于Aspose Word Java字节码分析与破解研究的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/764568

相关文章

springboot集成easypoi导出word换行处理过程

《springboot集成easypoi导出word换行处理过程》SpringBoot集成Easypoi导出Word时,换行符n失效显示为空格,解决方法包括生成段落或替换模板中n为回车,同时需确... 目录项目场景问题描述解决方案第一种:生成段落的方式第二种:替换模板的情况,换行符替换成回车总结项目场景s

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏