懒得勤快 4 years ago
parent
commit
9b4c4c6f9e

+ 74 - 0
src/Masuit.MyBlogs.Core/Controllers/FirewallController.cs

@@ -0,0 +1,74 @@
+using Masuit.Tools.AspNetCore.Mime;
+using Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;
+using Masuit.Tools.Core.Net;
+using Masuit.Tools.Security;
+using Masuit.Tools.Strings;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Net.Http.Headers;
+using System;
+
+namespace Masuit.MyBlogs.Core.Controllers
+{
+    public class FirewallController : Controller
+    {
+        /// <summary>
+        /// JS挑战,5秒盾
+        /// </summary>
+        /// <param name="token"></param>
+        /// <returns></returns>
+        [HttpPost("/challenge"), AutoValidateAntiforgeryToken]
+        public ActionResult JsChallenge(string token)
+        {
+            try
+            {
+                var privateKey = HttpContext.Session.Get<string>("challenge-private-key");
+                var crypto = HttpContext.Session.Get<string>("challenge-value");
+                if (token.RSADecrypt(privateKey) == crypto)
+                {
+                    HttpContext.Session.Set("js-challenge", 1);
+                    HttpContext.Session.Remove("challenge-private-key");
+                    HttpContext.Session.Remove("challenge-value");
+                    Response.Cookies.Delete("challenge-key");
+                }
+
+                return Ok();
+            }
+            catch
+            {
+                return BadRequest();
+            }
+        }
+
+        /// <summary>
+        /// 验证码挑战
+        /// </summary>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        [HttpPost("/captcha"), AutoValidateAntiforgeryToken]
+        public ActionResult CaptchaChallenge(string code)
+        {
+            if (code.Equals(HttpContext.Session.Get<string>("challenge-captcha"), StringComparison.CurrentCultureIgnoreCase))
+            {
+                HttpContext.Session.Set("js-challenge", 1);
+                HttpContext.Session.Remove("challenge-captcha");
+            }
+
+            return Redirect(Request.Headers[HeaderNames.Referer]);
+        }
+
+        /// <summary>
+        /// 验证码
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("/challenge-captcha.jpg")]
+        [ResponseCache(NoStore = true, Duration = 0)]
+        public ActionResult CaptchaChallenge()
+        {
+            string code = ValidateCode.CreateValidateCode(6);
+            HttpContext.Session.Set("challenge-captcha", code);
+            var buffer = HttpContext.CreateValidateGraphic(code);
+            return this.ResumeFile(buffer, ContentType.Jpeg, "验证码.jpg");
+        }
+
+    }
+}

+ 20 - 0
src/Masuit.MyBlogs.Core/Extensions/Firewall/FirewallAttribute.cs

@@ -68,6 +68,26 @@ namespace Masuit.MyBlogs.Core.Extensions.Firewall
                 return;
             }
 
+            if (!context.HttpContext.Session.TryGetValue("js-challenge", out _))
+            {
+                var mode = CommonHelper.SystemSettings.GetOrAdd("ChallengeMode", "");
+                if (mode == "JSChallenge")
+                {
+                    context.Result = new ViewResult()
+                    {
+                        ViewName = "/Views/Shared/JSChallenge.cshtml"
+                    };
+                }
+
+                if (mode == "CaptchaChallenge")
+                {
+                    context.Result = new ViewResult()
+                    {
+                        ViewName = "/Views/Shared/CaptchaChallenge.cshtml"
+                    };
+                }
+            }
+
             var times = CacheManager.AddOrUpdate("Frequency:" + ip, 1, i => i + 1, 5);
             CacheManager.Expire("Frequency:" + ip, ExpirationMode.Absolute, TimeSpan.FromSeconds(CommonHelper.SystemSettings.GetOrAdd("LimitIPFrequency", "60").ToInt32()));
             var limit = CommonHelper.SystemSettings.GetOrAdd("LimitIPRequestTimes", "90").ToInt32();

+ 28 - 0
src/Masuit.MyBlogs.Core/Views/Shared/CaptchaChallenge.cshtml

@@ -0,0 +1,28 @@
+@using Masuit.MyBlogs.Core.Common
+
+@{
+    Layout = null;
+}
+
+<!DOCTYPE html>
+
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>继续访问请输入验证码</title>
+    <script src="https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js"></script>
+</head>
+<body>
+    <h3>继续访问请输入验证码:</h3>
+    <img src="/challenge-captcha.jpg" onclick="this.src='/challenge-captcha.jpg?'+new Date" />
+    <form asp-action="CaptchaChallenge" asp-controller="Firewall" method="post">
+        @Html.AntiForgeryToken()
+        <input type="text" name="code" placeholder="验证码" required="" />
+        <button type="submit">提交</button>
+    </form>
+    @Html.Raw(CommonHelper.SystemSettings.GetOrAdd("Scripts", ""))
+</body>
+</html>

+ 54 - 0
src/Masuit.MyBlogs.Core/Views/Shared/JSChallenge.cshtml

@@ -0,0 +1,54 @@
+@using Masuit.Tools.Core.Net
+@using Masuit.MyBlogs.Core.Common
+@using Masuit.Tools.Security
+
+@{
+    Layout = null;
+    var value = Guid.NewGuid().ToString();
+    var keys = RsaCrypt.GenerateRsaKeys(RsaKeyType.PKCS8);
+    Context.Session.Set("challenge-private-key", keys.PrivateKey);
+    Context.Session.Set("challenge-value", value);
+    Context.Response.Cookies.Append("challenge-key", keys.PublicKey);
+}
+
+<!DOCTYPE html>
+
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>正在检测您的浏览器环境,请稍候......</title>
+    <script src="https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js"></script>
+    <script src="https://cdn.staticfile.org/jsencrypt/3.0.0-rc.1/jsencrypt.min.js"></script>
+</head>
+<body>
+    <form>
+        @Html.AntiForgeryToken()
+    </form>
+    正在检测您的浏览器环境,请稍候......
+    @Html.Raw(CommonHelper.SystemSettings.GetOrAdd("Scripts", ""))
+</body>
+</html>
+<script>
+    $(function () {
+        var encrypt = new JSEncrypt();
+        encrypt.setPublicKey($.cookie("challenge-key"));
+        setTimeout(function () {
+            var formData = new FormData();
+            formData.append("__RequestVerificationToken", $("[name='__RequestVerificationToken']").val());
+            formData.append("token", encrypt.encrypt("@value"));
+            window.fetch("/challenge", {
+                credentials: 'include',
+                method: 'POST',
+                mode: 'cors',
+                body: formData
+            }).then(function (response) {
+                if (response.ok) {
+                    location.reload();
+                }
+            });
+        }, 3000);
+    });
+</script>

+ 10 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/system/firewall.html

@@ -53,6 +53,16 @@
                         <span class="input-group-addon">次,上报防火墙永久冻结该IP。</span>
                     </div>
                 </div>
+                <div class="col-md-12 form-inline">
+                    <div class="input-group">
+                        <span class="input-group-addon">挑战模式:</span>
+                        <select ng-model="Settings.ChallengeMode" ng-init="Settings.ChallengeMode = ' '" class="form-control">
+                            <option value=" ">无</option>
+                            <option value="JSChallenge">JS挑战</option>
+                            <option value="CaptchaChallenge">验证码挑战</option>
+                        </select>
+                    </div>
+                </div>
                 <div class="col-md-12">
                     <div class="input-group">
                         <span class="input-group-addon">受限制的地区或运营商:</span>