diff --git a/src/Controller/School/VoteController.php b/src/Controller/School/VoteController.php index 790a923..1e9dca7 100644 --- a/src/Controller/School/VoteController.php +++ b/src/Controller/School/VoteController.php @@ -21,6 +21,10 @@ use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Translation\TranslatorInterface; +/** + * Class VoteController + * @deprecated No longer used. + */ class VoteController extends AbstractController { /** diff --git a/src/Controller/User/UserController.php b/src/Controller/User/UserController.php index fd8e32c..dfa0a8c 100644 --- a/src/Controller/User/UserController.php +++ b/src/Controller/User/UserController.php @@ -480,13 +480,13 @@ public function openid() /** * @Route("/user/code", methods="POST") */ - public function code(Request $request, NotificationService $service, TranslatorInterface $translator) + public function code(Request $request, NotificationService $service, TranslatorInterface $translator, CacheService $cacheService) { - if (!$this->verifyCaptcha($request->request->get("captcha"))) - return $this->response()->response($translator->trans("incorrect-captcha"), Response::HTTP_UNAUTHORIZED); $type = $request->request->getInt("type"); if($type == AliyunTemplateType::BIND) $this->denyAccessUnlessGranted(Permission::IS_LOGIN); + if(!$cacheService->canSend($request->getClientIp(), $request->request->get("email") ?? $request->request->get("phone"))) + return $this->response()->response($translator->trans("rate-limited"), Response::HTTP_FORBIDDEN); $error = $service->code($request->request->get("email"), $request->request->get("phone"), $type); if($error) return $this->response()->response(null); diff --git a/src/Service/CacheService.php b/src/Service/CacheService.php index 16dc43b..60c727d 100644 --- a/src/Service/CacheService.php +++ b/src/Service/CacheService.php @@ -45,6 +45,24 @@ public function antiSpiderUse(User $user, int $target) { } } + public function canSend(string $ip, string $target) { + $current = (int)$this->client->get($this->getIdentifierForCode($ip)); + if($current > 5) + return false; + $current ++; + $this->client->set($this->getIdentifierForCode($ip), $current); + $this->client->expire($this->getIdentifierForCode($ip), 1800); + + $current = (int)$this->client->get($this->getIdentifierForCode($target)); + if($current > 3) + return false; + $current ++; + $this->client->set($this->getIdentifierForCode($target), $current); + $this->client->expire($this->getIdentifierForCode($target), 1800); + + return true; + } + public function rateWrite(User $user) { $current = (int)$this->client->get($this->getIdentifierForRate($user)); $current ++; @@ -84,6 +102,10 @@ private function getIdentifierForAntiSpider(User $user){ return "antispider.".(string)$user->getId(); } + private function getIdentifierForCode(string $target){ + return "code.".$target; + } + private function getIdentifierForRate(User $user) { return "rate.".(string)$user->getId(); } diff --git a/src/Service/NotificationService.php b/src/Service/NotificationService.php index e608c66..f5d2dea 100644 --- a/src/Service/NotificationService.php +++ b/src/Service/NotificationService.php @@ -81,7 +81,6 @@ public function code($email = null, $phone = null, int $action) { $phone, AliyunTemplateType::REGISTER, ["code" => $code]); - } return false; case CodeActionType::RESET: diff --git a/web/components/User/Register.vue b/web/components/User/Register.vue index f08a4fe..7dc4db4 100644 --- a/web/components/User/Register.vue +++ b/web/components/User/Register.vue @@ -41,7 +41,7 @@
- {{$t('send')}} + {{$t('send')}}
@@ -173,13 +173,12 @@ } } }, - ct() { + send() { this.sending = true this.axios.post("/user/code", { "type": 1, "email": this.form.email, - "phone": this.form.phone, - "captcha": grecaptcha.getResponse() + "phone": this.form.phone }).then((response) => { this.sending = false if (response.data["code"] === 200) { @@ -191,7 +190,6 @@ this.sending = false this.$emit("generalError",error) }) - grecaptcha.reset() }, register() { this.validateItems = { @@ -249,7 +247,7 @@ this.$v.$touch() if (!this.$v.$invalid) { this.form.email = null - grecaptcha.execute() + this.send() } }, sendEmail() { @@ -262,7 +260,7 @@ this.$v.$touch() if (!this.$v.$invalid) { this.form.phone = null - grecaptcha.execute() + this.send() } }, showMsg(msg) { @@ -271,14 +269,6 @@ }, mounted: function () { this.$emit("changeTitle", this.$t("register-title")) - this.$emit("prepareRecaptcha") - }, - watch: { - gResponse: { - handler: function (val, newVal) { - this.ct(); - } - } } } diff --git a/web/components/User/Reset.vue b/web/components/User/Reset.vue index 045ccc1..8bdcfe5 100644 --- a/web/components/User/Reset.vue +++ b/web/components/User/Reset.vue @@ -150,13 +150,12 @@ } } }, - ct() { + send() { this.sending = true this.axios.post("/user/code", { "type": 2, "phone": this.form.phone, - "email": this.form.email, - "captcha": grecaptcha.getResponse() + "email": this.form.email }).then((response) => { this.sending = false if (response.data["code"] === 200) { @@ -168,7 +167,6 @@ this.sending = false this.$emit("generalError",error) }) - grecaptcha.reset() }, reset() { this.validateItems = { @@ -218,7 +216,7 @@ this.$v.$touch() if (!this.$v.$invalid) { this.form.email = null - grecaptcha.execute() + this.send() } }, sendEmail() { @@ -231,7 +229,7 @@ this.$v.$touch() if (!this.$v.$invalid) { this.form.phone = null - grecaptcha.execute() + this.send() } }, showMsg(msg) { @@ -240,14 +238,6 @@ }, mounted: function () { this.$emit("changeTitle", this.$t("reset-title")) - this.$emit("prepareRecaptcha") - }, - watch: { - gResponse: { - handler: function (val, newVal) { - this.ct(); - } - } } } diff --git a/web/components/User/Security.vue b/web/components/User/Security.vue index 8e53e4b..3fe4e5e 100644 --- a/web/components/User/Security.vue +++ b/web/components/User/Security.vue @@ -112,7 +112,6 @@ }, mounted: function () { this.$emit("changeTitle", this.$t("security-title")) - this.$emit("prepareRecaptcha") this.incomplete = (this.$route.query.reason === 'incomplete') }, methods: { @@ -157,19 +156,18 @@ }, sendEmail() { this.task = "email" - grecaptcha.execute() + this.send() }, sendSMS() { this.task = "sms" - grecaptcha.execute() + this.send() }, - ct() { + send() { switch (this.task) { case "email": this.axios.post("/user/code", { "type": 3, - "email": this.form.newEmail, - "captcha": grecaptcha.getResponse() + "email": this.form.newEmail }).then((response) => { if (response.data["code"] === 200) { this.showMsg(this.$t("send-succeeded")) @@ -197,18 +195,10 @@ } this.task = "" - grecaptcha.reset() }, showMsg(msg) { this.$emit("showMsg", msg) } - }, - watch: { - gResponse: { - handler: function (val, newVal) { - this.ct(); - } - } } } diff --git a/web/main.js b/web/main.js index 9605d05..af04901 100644 --- a/web/main.js +++ b/web/main.js @@ -10,7 +10,6 @@ import VueAxios from 'vue-axios' import VuePreview from 'vue-preview' import 'vue-material/dist/vue-material.css' import 'vue-material/dist/theme/default.css' -import VueMarkdown from 'vue-markdown' import VueAnalytics from 'vue-analytics' import Raven from 'raven-js' import RavenVue from 'raven-js/plugins/vue'