python爬蟲破解CryptoJS的aes加密數據

来源:https://www.cnblogs.com/Eeyhan/archive/2020/01/18/12209074.html
-Advertisement-
Play Games

發現問題 在一次偶然中,在爬取某個網站時,老方法,打開調試工具查看請求方式,請求攔截,是否是非同步載入,不亦樂乎,當我以為這個網站非常簡單的時候,發現二級網頁的地址和源碼不對應 Ajax非同步載入?源碼也是這樣的 而且這些鏈接直... ...


 

發現問題 

 

在一次偶然中,在爬取某個網站時,老方法,打開調試工具查看請求方式,請求攔截,是否是非同步載入,不亦樂乎,當我以為這個網站非常簡單的時候,發現二級網頁的地址和源碼不對應

 

 

 

 

 

Ajax非同步載入?源碼也是這樣的

 

 

 

 

 

而且這些鏈接直接訪問根本無法訪問

 

 

 

 

 

 

用火狐瀏覽器的event顯示:

 

 

 

 

找到加密方式 

 

源碼:

 

 

 

 

 

function() {

  var hh = $(this).attr("href");

  if (typeof(hh) == 'undefined' || hh == '#') {

    return

  }

  var aa = hh.split("/");

  var aaa = aa.length;

  var bbb = aa[aaa - 1].split('.');

  var ccc = bbb[0];

  var cccc = bbb[1];

  var r = /^\+?[1-9][0-9]*$/;

  if (r.test(ccc) && cccc.indexOf('jhtml') != -1) {

    var srcs = CryptoJS.enc.Utf8.parse(ccc);

    var k = CryptoJS.enc.Utf8.parse(s);

    var en = CryptoJS.AES.encrypt(srcs, k, {

      mode: CryptoJS.mode.ECB,

      padding: CryptoJS.pad.Pkcs7

    });

    var ddd = en.toString();

    ddd = ddd.replace(/\//g, "^");

    ddd = ddd.substring(0, ddd.length - 2);

    var bbbb = ddd + '.' + bbb[1];

    aa[aaa - 1] = bbbb;

    var uuu = '';

    for (i = 0; i < aaa; i++) {

      uuu += aa[i] + '/'

    }

    uuu = uuu.substring(0, uuu.length - 1);

    window.open(uuu)

  } else {

    var ee = $(this).attr('target');

    if (ee.typeof('undefined')) {

      window.location = hh

    } else {

      window.open(hh)

    }

  }

  return false

}

 

 

 

根據我的發現,實現的效果就是讓源碼的鏈接:

http://xxxx.xxxx.xx:80/jssfdg/3254639.jhtml

變為如下鏈接:

http://xxxx.xxxx.xx:80/jssfdg/JzRnGhk7J9D1ZNMlh47fMw.jhtml

 

 

以上代碼縮減了下:

就這幾行代碼:

var srcs = CryptoJS.enc.Utf8.parse(ccc);

var k = CryptoJS.enc.Utf8.parse(s);

var en = CryptoJS.AES.encrypt(srcs, k, {

         mode: CryptoJS.mode.ECB,

         padding: CryptoJS.pad.Pkcs7

});

var ddd = en.toString();

ddd = ddd.replace(/\//g, "^");

ddd = ddd.substring(0, ddd.length - 2);

return ddd

 

 

 

將以上代碼封裝為一個函數併在瀏覽器的控制台測試:

 

大概的意思就是先將那串數字用utf8加密成數組:

 

 

 

 

再對密鑰操作:

 

 

 

 

再將上面的兩個數組用aes加密

 

 

 

 

 

 

將用aes加密過的en轉為字元串:

 

 

 

我渣一看這種字元串像是base64加密 

 

再將帶有/符號的轉為^,因為在url編碼中,/符號有特殊意義

 

 

 

 

再將數據後面的[==]分割掉

 

 

 

 

最後的字元串就是需要的數據了

 

把這段操作封裝成一個函數,然後測試:

 

 

 

 

 

經過測試,沒毛病,返回的這個字元與真實的一致。

 

卧槽,瞬間激動啦,這還沒完,我是直接在當前網頁的控制台操作的,換到其他網頁的控制台:

 

 

 

 

 

需要兩個重要的東西:CryptoJS對象 和 s變數

 

CryptoJS對象都還好,那特麽一定是引入的,s變數就難了,從源碼找,包含s的太多了,而且還是小寫,關鍵詞太多了

 

 

而且S變數是已經定義好的,因為我打開控制台直接訪問s就能有結果:

 

 

 

 

 

 

所以,那絕對是已定義的,但是我無法確定是寫死的還是隨機生成的,如果按寫死的來,那就難了,所以我必須找到它,它也至關重要,因為就是aes加密需要的密鑰,不然無法加密出來希望的格式

我找了一上午,還請教了我的前端大佬同事,也是一時不知道怎麼找

 

 

 

最後,我把當天的任務忙完之後,專門騰出兩小時時間來再分析,我一個一個js文件慢慢看:

 

找到key值

 

結果,他麽的,放在jquery源碼裡面:

 

 

 

 

 

 

而且,還真的是寫死的

 

卧槽!!!!!!!!!!!!!!!!!!!!,太nm騷了,放源碼了,按正常人的慣用思維,像什麼jquery,vue等的源碼,一定是不敢去亂改的,要寫js的話,也不敢去亂搞,都是新建一個js文件寫入,它這個把密鑰寫在jquery源碼里,簡直反其道而行,我不知道說這個網站的前端開發者是高明還是奇葩了

 

好的,兩樣東西都齊了,準備用代碼實現了,在這之前,先瞭解下什麼是CryptoJS

 

 

 

源碼:

 


!function(t, n) {

    "object" == typeof exports ? module.exports = exports = n() : "function" == typeof define && define.amd ? define([], n) : t.CryptoJS = n()

} (this,

function() {

    var t = t ||

    function(t, n) {

        var i = Object.create ||

        function() {

            function t() {}

            return function(n) {

                var i;

                return t.prototype = n,

                i = new t,

                t.prototype = null,

                i

            }

        } (),

        e = {},

        r = e.lib = {},

        o = r.Base = function() {

            return {

                extend: function(t) {

                    var n = i(this);

                    return t && n.mixIn(t),

                    n.hasOwnProperty("init") && this.init !== n.init || (n.init = function() {

                        n.$super.init.apply(this, arguments)

                    }),

                    n.init.prototype = n,

                    n.$super = this,

                    n

                },

                create: function() {

                    var t = this.extend();

                    return t.init.apply(t, arguments),

                    t

                },

                init: function() {},

                mixIn: function(t) {

                    for (var n in t) t.hasOwnProperty(n) && (this[n] = t[n]);

                    t.hasOwnProperty("toString") && (this.toString = t.toString)

                },

                clone: function() {

                    return this.init.prototype.extend(this)

                }

            }

        } (),

        s = r.WordArray = o.extend({

            init: function(t, i) {

                t = this.words = t || [],

                i != n ? this.sigBytes = i: this.sigBytes = 4 * t.length

            },

            toString: function(t) {

                return (t || c).stringify(this)

            },

            concat: function(t) {

                var n = this.words,

                i = t.words,

                e = this.sigBytes,

                r = t.sigBytes;

                if (this.clamp(), e % 4) for (var o = 0; o < r; o++) {

                    var s = i[o >>> 2] >>> 24 - o % 4 * 8 & 255;

                    n[e + o >>> 2] |= s << 24 - (e + o) % 4 * 8

                } else for (var o = 0; o < r; o += 4) n[e + o >>> 2] = i[o >>> 2];

                return this.sigBytes += r,

                this

            },

            clamp: function() {

                var n = this.words,

                i = this.sigBytes;

                n[i >>> 2] &= 4294967295 << 32 - i % 4 * 8,

                n.length = t.ceil(i / 4)

            },

            clone: function() {

                var t = o.clone.call(this);

                return t.words = this.words.slice(0),

                t

            },

            random: function(n) {

                for (var i, e = [], r = function(n) {

                    var n = n,

                    i = 987654321,

                    e = 4294967295;

                    return function() {

                        i = 36969 * (65535 & i) + (i >> 16) & e,

                        n = 18e3 * (65535 & n) + (n >> 16) & e;

                        var r = (i << 16) + n & e;

                        return r /= 4294967296,

                        r += .5,

                        r * (t.random() > .5 ? 1 : -1)

                    }

                },

                o = 0; o < n; o += 4) {

                    var a = r(4294967296 * (i || t.random()));

                    i = 987654071 * a(),

                    e.push(4294967296 * a() | 0)

                }

                return new s.init(e, n)

            }

        }),

        a = e.enc = {},

        c = a.Hex = {

            stringify: function(t) {

                for (var n = t.words,

                i = t.sigBytes,

                e = [], r = 0; r < i; r++) {

                    var o = n[r >>> 2] >>> 24 - r % 4 * 8 & 255;

                    e.push((o >>> 4).toString(16)),

                    e.push((15 & o).toString(16))

                }

                return e.join("")

            },

            parse: function(t) {

                for (var n = t.length,

                i = [], e = 0; e < n; e += 2) i[e >>> 3] |= parseInt(t.substr(e, 2), 16) << 24 - e % 8 * 4;

                return new s.init(i, n / 2)

            }

        },

        u = a.Latin1 = {

            stringify: function(t) {

                for (var n = t.words,

                i = t.sigBytes,

                e = [], r = 0; r < i; r++) {

                    var o = n[r >>> 2] >>> 24 - r % 4 * 8 & 255;

                    e.push(String.fromCharCode(o))

                }

                return e.join("")

            },

            parse: function(t) {

                for (var n = t.length,

                i = [], e = 0; e < n; e++) i[e >>> 2] |= (255 & t.charCodeAt(e)) << 24 - e % 4 * 8;

                return new s.init(i, n)

            }

        },

        f = a.Utf8 = {

            stringify: function(t) {

                try {

                    return decodeURIComponent(escape(u.stringify(t)))

                } catch(t) {

                    throw new Error("Malformed UTF-8 data")

                }

            },

            parse: function(t) {

                return u.parse(unescape(encodeURIComponent(t)))

            }

        },

        h = r.BufferedBlockAlgorithm = o.extend({

            reset: function() {

                this._data = new s.init,

                this._nDataBytes = 0

            },

            _append: function(t) {

                "string" == typeof t && (t = f.parse(t)),

                this._data.concat(t),

                this._nDataBytes += t.sigBytes

            },

            _process: function(n) {

                var i = this._data,

                e = i.words,

                r = i.sigBytes,

                o = this.blockSize,

                a = 4 * o,

                c = r / a;

                c = n ? t.ceil(c) : t.max((0 | c) - this._minBufferSize, 0);

                var u = c * o,

                f = t.min(4 * u, r);

                if (u) {

                    for (var h = 0; h < u; h += o) this._doProcessBlock(e, h);

                    var p = e.splice(0, u);

                    i.sigBytes -= f

                }

                return new s.init(p, f)

            },

            clone: function() {

                var t = o.clone.call(this);

                return t._data = this._data.clone(),

                t

            },

            _minBufferSize: 0

        }),

        p = (r.Hasher = h.extend({

            cfg: o.extend(),

            init: function(t) {

                this.cfg = this.cfg.extend(t),

                this.reset()

            },

            reset: function() {

                h.reset.call(this),

                this._doReset()

            },

            update: function(t) {

                return this._append(t),

                this._process(),

                this

            },

            finalize: function(t) {

                t && this._append(t);

                var n = this._doFinalize();

                return n

            },

            blockSize: 16,

            _createHelper: function(t) {

                return function(n, i) {

                    return new t.init(i).finalize(n)

                }

            },

            _createHmacHelper: function(t) {

                return function(n, i) {

                    return new p.HMAC.init(t, i).finalize(n)

                }

            }

        }), e.algo = {});

        return e

    } (Math);

    return t

});

//# sourceMappingURL=core.min.js.map

!

function(e, t, i) {

    "object" == typeof exports ? module.exports = exports = t(require("./core.min"), require("./sha1.min"), require("./hmac.min")) : "function" == typeof define && define.amd ? define(["./core.min", "./sha1.min", "./hmac.min"], t) : t(e.CryptoJS)

} (this,

function(e) {

    return function() {

        var t = e,

        i = t.lib,

        r = i.Base,

        n = i.WordArray,

        o = t.algo,

        a = o.MD5,

        c = o.EvpKDF = r.extend({

            cfg: r.extend({

                keySize: 4,

                hasher: a,

                iterations: 1

            }),

            init: function(e) {

                this.cfg = this.cfg.extend(e)

            },

            compute: function(e, t) {

                for (var i = this.cfg,

                r = i.hasher.create(), o = n.create(), a = o.words, c = i.keySize, f = i.iterations; a.length < c;) {

                    s && r.update(s);

                    var s = r.update(e).finalize(t);

                    r.reset();

                    for (var u = 1; u < f; u++) s = r.finalize(s),

                    r.reset();

                    o.concat(s)

                }

                return o.sigBytes = 4 * c,

                o

            }

        });

        t.EvpKDF = function(e, t, i) {

            return c.create(i).compute(e, t)

        }

    } (),

    e.EvpKDF

});

//# sourceMappingURL=evpkdf.min.js.map

!

function(r, e) {

    "object" == typeof exports ? module.exports = exports = e(require("./core.min")) : "function" == typeof define && define.amd ? define(["./core.min"], e) : e(r.CryptoJS)

} (this,

function(r) {

    return function() {

        function e(r, e, t) {

            for (var n = [], i = 0, o = 0; o < e; o++) if (o % 4) {

                var f = t[r.charCodeAt(o - 1)] << o % 4 * 2,

                c = t[r.charCodeAt(o)] >>> 6 - o % 4 * 2;

                n[i >>> 2] |= (f | c) << 24 - i % 4 * 8,

                i++

            }

            return a.create(n, i)

        }

        var t = r,

        n = t.lib,

        a = n.WordArray,

        i = t.enc;

        i.Base64 = {

            stringify: function(r) {

                var e = r.words,

                t = r.sigBytes,

                n = this._map;

                r.clamp();

                for (var a = [], i = 0; i < t; i += 3) for (var o = e[i >>> 2] >>> 24 - i % 4 * 8 & 255, f = e[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 255, c = e[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 255, s = o << 16 | f << 8 | c, h = 0; h < 4 && i + .75 * h < t; h++) a.push(n.charAt(s >>> 6 * (3 - h) & 63));

                var p = n.charAt(64);

                if (p) for (; a.length % 4;) a.push(p);

                return a.join("")

            },

            parse: function(r) {

                var t = r.length,

                n = this._map,

                a = this._reverseMap;

                if (!a) {

                    a = this._reverseMap = [];

                    for (var i = 0; i < n.length; i++) a[n.charCodeAt(i)] = i

                }

                var o = n.charAt(64);

                if (o) {

                    var f = r.indexOf(o);

                    f !== -1 && (t = f)

                }

                return e(r, t, a)

            },

            _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

        }

    } (),

    r.enc.Base64

});

//# sourceMappingURL=enc-base64.min.js.map

!

function(e, t, r) {

    "object" == typeof exports ? module.exports = exports = t(require("./core.min"), require("./evpkdf.min")) : "function" == typeof define && define.amd ? define(["./core.min", "./evpkdf.min"], t) : t(e.CryptoJS)

} (this,

function(e) {

    e.lib.Cipher ||

    function(t) {

        var r = e,

        i = r.lib,

        n = i.Base,

        c = i.WordArray,

        o = i.BufferedBlockAlgorithm,

        s = r.enc,

        a = (s.Utf8, s.Base64),

        f = r.algo,

        p = f.EvpKDF,

        d = i.Cipher = o.extend({

            cfg: n.extend(),

            createEncryptor: function(e, t) {

                return this.create(this._ENC_XFORM_MODE, e, t)

            },

            createDecryptor: function(e, t) {

                return this.create(this._DEC_XFORM_MODE, e, t)

            },

            init: function(e, t, r) {

                this.cfg = this.cfg.extend(r),

                this._xformMode = e,

                this._key = t,

                this.reset()

            },

            reset: function() {

                o.reset.call(this),

                this._doReset()

            },

            process: function(e) {

                return this._append(e),

                this._process()

            },

            finalize: function(e) {

                e && this._append(e);

                var t = this._doFinalize();

                return t

            },

            keySize: 4,

            ivSize: 4,

            _ENC_XFORM_MODE: 1,

            _DEC_XFORM_MODE: 2,

            _createHelper: function() {

                function e(e) {

                    return "string" == typeof e ? B: x

                }

                return function(t) {

                    return {

                        encrypt: function(r, i, n) {

                            return e(i).encrypt(t, r, i, n)

                        },

                        decrypt: function(r, i, n) {

                            return e(i).decrypt(t, r, i, n)

                        }

                    }

                }

            } ()

        }),

        h = (i.StreamCipher = d.extend({

            _doFinalize: function() {

                var e = this._process(!0);

                return e

            },

            blockSize: 1

        }), r.mode = {}),

        u = i.BlockCipherMode = n.extend({

            createEncryptor: function(e, t) {

                return this.Encryptor.create(e, t)

            },

            createDecryptor: function(e, t) {

                return this.Decryptor.create(e, t)

            },

            init: function(e, t) {

                this._cipher = e,

                this._iv = t

            }

        }),

        l = h.CBC = function() {

            function e(e, r, i) {

                var n = this._iv;

                if (n) {

                    var c = n;

                    this._iv = t

                } else var c = this._prevBlock;

                for (var o = 0; o < i; o++) e[r + o] ^= c[o]

            }

            var r = u.extend();

            return r.Encryptor = r.extend({

                processBlock: function(t, r) {

                    var i = this._cipher,

                    n = i.blockSize;

                    e.call(this, t, r, n),

                    i.encryptBlock(t, r),

                    this._prevBlock = t.slice(r, r + n)

                }

            }),

            r.Decryptor = r.extend({

                processBlock: function(t, r) {

                    var i = this._cipher,

                    n = i.blockSize,

                    c = t.slice(r, r + n);

                    i.decryptBlock(t, r),

                    e.call(this, t, r, n),

                    this._prevBlock = c

                }

            }),

            r

        } (),

        _ = r.pad = {},

        v = _.Pkcs7 = {

            pad: function(e, t) {

                for (var r = 4 * t,

                i = r - e.sigBytes % r,

                n = i << 24 | i << 16 | i << 8 | i,

                o = [], s = 0; s < i; s += 4) o.push(n);

                var a = c.create(o, i);

                e.concat(a)

            },

            unpad: function(e) {

                var t = 255 & e.words[e.sigBytes - 1 >>> 2];

                e.sigBytes -= t

            }

        },

        y = (i.BlockCipher = d.extend({

            cfg: d.cfg.extend({

                mode: l,

                padding: v

            }),

            reset: function() {

                d.reset.call(this);

                var e = this.cfg,

                t = e.iv,

                r = e.mode;

                if (this._xformMode == this._ENC_XFORM_MODE) var i = r.createEncryptor;

                else {

                    var i = r.createDecryptor;

                    this._minBufferSize = 1

                }

                this._mode && this._mode.__creator == i ? this._mode.init(this, t && t.words) : (this._mode = i.call(r, this, t && t.words), this._mode.__creator = i)

            },

            _doProcessBlock: function(e, t) {

                this._mode.processBlock(e, t)

            },

            _doFinalize: function() {

                var e = this.cfg.padding;

                if (this._xformMode == this._ENC_XFORM_MODE) {

                    e.pad(this._data, this.blockSize);

                    var t = this._process(!0)

                } else {

                    var t = this._process(!0);

                    e.unpad(t)

                }

                return t

            },

            blockSize: 4

        }), i.CipherParams = n.extend({

            init: function(e) {

                this.mixIn(e)

            },

            toString: function(e) {

                return (e || this.formatter).stringify(this)

            }

        })),

        m = r.format = {},

        k = m.OpenSSL = {

            stringify: function(e) {

                var t = e.ciphertext,

                r = e.salt;

                if (r) var i = c.create([1398893684, 1701076831]).concat(r).concat(t);

                else var i = t;

                return i.toString(a)

            },

            parse: function(e) {

                var t = a.parse(e),

                r = t.words;

                if (1398893684 == r[0] && 1701076831 == r[1]) {

                    var i = c.create(r.slice(2, 4));

                    r.splice(0, 4),

                    t.sigBytes -= 16

                }

                return y.create({

                    ciphertext: t,

                    salt: i

                })

            }

        },

        x = i.SerializableCipher = n.extend({

            cfg: n.extend({

                format: k

            }),

            encrypt: function(e, t, r, i) {

                i = this.cfg.extend(i);

                var n = e.createEncryptor(r, i),

                c = n.finalize(t),

                o = n.cfg;

                return y.create({

                    ciphertext: c,

                    key: r,

                    iv: o.iv,

                    algorithm: e,

                    mode: o.mode,

                    padding: o.padding,

                    blockSize: e.blockSize,

                    formatter: i.format

                })

            },

            decrypt: function(e, t, r, i) {

                i = this.cfg.extend(i),

                t = this._parse(t, i.format);

                var n = e.createDecryptor(r, i).finalize(t.ciphertext);

                return n

            },

            _parse: function	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.基礎方式的增刪該查: 1.mybatis約定:輸入參數parameterType和輸出參數resulrType在形式上只能有一個。 2.如果輸入/輸出參數:是簡單類型(8個基本類型加String)則可以使用任何占位符,#{xxx}; 如果是對象類型,則必須是對象的屬性,#{屬性名}。 3.輸出參 ...
  • 一、概述 泛型( )是 中引入的一個新特性, 泛型提供了 編譯時類型安全檢測機制 ,該機制允許開發者在編譯時檢測到非法的類型。 1.1 什麼是泛型? 泛型,即 參數化類型 。 一提到參數,最熟悉的就是定義方法時有形參,然後調用此方法時傳遞實參。那麼參數化類型怎麼理解呢?顧名思義,就是將類型由原來的具 ...
  • ​ 【新智元導讀】又到了每年集五福的時間。你的五福集齊了嗎?每天在各種群里苦苦求掃福,或者忍受著別人天天求掃福,是不是有點厭倦了。作為技術人員,怎麼能忍受得了這種低效的全人工操作呢?今天就為大家推薦用Python生成風格不同又數量龐大的「福」字,讓大家不用滿世界找福字,動動手指即可。 ! 是什麼讓你 ...
  • 棧: 1、又名堆棧,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把 另一端稱為棧底。其特性是先進後出。 2、棧是線程私有的,生命周期跟線程相同,當創建一個線程時,同時會創建一個棧,棧的大小和深度都是固定的。 3、 方法參數列表中的變數,方法體中的基 ...
  • 這兩天看opencv-python的HSV色彩空間,在寫程式時發現用HSV來提取圖像區域是件令人噁心的麻煩事。拿閾值分割做個對比,閾值最多也就一兩個參數需要調整;但是HSV需要對三個通道調整上下限,也就是起碼有6個參數。於是乎,就一時興起決定做個小程式,把參數都做成滑動塊,這樣自然方便許多。一開始, ...
  • 一、什麼式方法區 方法區,也稱非堆(Non Heap),又是一個被線程共用的記憶體區域。其中主要存儲載入的類位元組碼、class/method/field等元數據對象、static final常量、static變數、jit編譯器編譯後的代碼等數據。另外,方法區包含了一個特殊的區域“運行時常量池”。 (1 ...
  • JVM體繫結構圖 Native Interface(本地介面) Java本地介面(Java Native Interface (JNI))允許運行在Java虛擬機(Java Virtual Machine (JVM))上的代碼調用本地程式和類庫,或者被它們調用,這些程式和類庫可以是其它語言編寫的,比 ...
  • 準備年後要跳槽,所以最近一直再看面試題,並且把收集到的面試題整理了以下發到博客上,希望對大家有所幫助。 首先是集合類的面試題 1. HashMap 排序題,上機題。 已知一個 HashMap<Integer,User>集合, User 有 name(String)和 age(int)屬性。請寫一個方 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...