如何使用阿里云短信服务实现登录页面,手机验证码登录?2

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 如何使用阿里云短信服务实现登录页面,手机验证码登录?

LoginController登录接口:(通过@Validated  对手机号,邮箱等参数进行校验)

package com.xialj.demoend.controller;
import cn.hutool.core.util.StrUtil;
import com.xialj.demoend.common.Result;
import com.xialj.demoend.entity.User;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.Optional;
import java.util.function.Supplier;
/**
 * @Author 
 * @Date Created in  2023/3/18 14:30
 * @DESCRIPTION:
 * @Version V1.0
 */
@RestController
@RequestMapping("/user/login")
@Slf4j
@CrossOrigin
public class LoginController {
   @Autowired
   private RedisTemplate<String,String> redisTemplate;
   @ApiOperation("手机验证码登录接口")
   @PostMapping("/phoneCode")
   @SuppressWarnings("all")
   public Result phoneCodeLogin(@RequestBody @Validated User user) throws Throwable {
      Optional.ofNullable(user.getPhone()).orElseThrow((Supplier<Throwable>) () -> new RuntimeException("手机号码为null"));
      String phone = user.getPhone();
      log.info("当前获取的手机号为:{}",phone);
      //从redis中获取手机验证码
      String userPhoneKey = redisTemplate.opsForValue().get(phone);
      if (StringUtils.isEmpty(userPhoneKey)) {
         return Result.fail("手机验证码有误");
      }
      if (!userPhoneKey.equals(String.valueOf(user.getCode()))) {
         return Result.fail("手机验证码有误");
      }
      return Result.ok("登录成功");
   }
}

前端vue代码:

<template>
  <div class="cont1" id="mainContainer" v-title data-title="若梦管理系统">
    <p class="tip">Click on button in image container</p>
    <div class="cont" style="border-radius: 15px">
      <div class="form sign-in" style="line-height: 50px;">
        <h2 style="margin-top: 1px">后台管理系统</h2>
        <el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
          <el-form-item label="手机号" prop="phone" style="width: 50%;margin-left: 110px;margin-top: 80px">
            <el-input v-model.number="formData.phone" placeholder="请输入手机号" :maxlength="11" show-word-limit
                      clearable
                      prefix-icon='el-icon-mobile-phone' :style="{width: '200px'}"></el-input>
          </el-form-item>
          <el-form-item label="验证码" prop="yzm" style="width: 50%;margin-left: 110px;margin-top: 50px">
            <el-input v-model.number="formData.yzm" :maxlength="6" :minlength="6"
                      clearable show-word-limit
                      prefix-icon='el-icon-chat-round'
                      oninput="value=value.replace(/[^0-9.]/g,'')"
                      :style="{width: '200px'}">
              <template #append >
                <el-button class="test" style="width: 5px;
                margin-right: 10px;
                text-align: center;
                background-color: inherit;"
                            type="danger"
                            :disabled="remainingTime > 0"
                           @click="startTimer">
                  {{ remainingTime > 0 ? '('+remainingTime+'s)' : '获取' }}
                </el-button>
              </template>
            </el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" :loading="loading" @click="submitForm('/manage/home')"
                       class="submit">登录
            </el-button>
          </el-form-item>
        </el-form>
      </div>
      <div class="sub-cont">
        <div class="img">
          <div class="img__text m--up">
            <h2>第一次来?</h2>
            <p>注册并发现大量的新机会!</p>
          </div>
          <div class="img__text m--in">
            <h2>加入我们 ?</h2>
            <p>如果你已经有一个账户,只需登录即可 </p>
            <span>We've missed you!</span>
          </div>
          <div class="img__btn">
            <span class="m--up">邮箱登录</span>
            <span class="m--in">手机登录</span>
          </div>
        </div>
        <div class="form sign-up" style="line-height: 50px">
          <h2>Time to feel like home</h2>
          <el-form ref="elForm2" :model="formData" :rules="rules2" size="medium" label-width="100px">
            <el-form-item label="电子邮箱" prop="email" style="width: 50%;margin-left: 110px;margin-top: 80px">
              <el-input v-model="formData.email" placeholder="请输入邮箱" show-word-limit clearable
                        prefix-icon='el-icon-message' :style="{width: '200px'}"></el-input>
            </el-form-item>
            <el-form-item label="验证码" prop="yzm2" style="width: 50%;margin-left: 110px;margin-top: 50px">
              <el-input v-model.number="formData.yzm2" :maxlength="6" :minlength="6"
                        clearable show-word-limit
                        prefix-icon='el-icon-chat-round'
                        oninput="value=value.replace(/[^0-9.]/g,'')"
                        :style="{width: '200px'}">
                <template #append >
                  <el-button class="test" style="width: 5px;
                margin-right: 10px;
                text-align: center;
                background-color: inherit;"
                             type="danger"
                             :disabled="remainingTime > 0"
                             @click="startTimer2">
                    {{ remainingTime > 0 ? '('+remainingTime+'s)' : '获取' }}
                  </el-button>
                </template>
              </el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary"  :loading="loading2" @click="submitForm2('/manage/home')"
                         class="submit">登录
              </el-button>
            </el-form-item>
          </el-form>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import {sendPhoneCode, sendEmailCode,phoneLogin,emailLogin} from "@/api/login.js";
export default {
  name: 'login',
  props: {
    msg: String
  },
  data() {
    return {
      remainingTime: 0,
      loading: false,
      loading2:false,
      timer: null,
      formData: {
        pwd: '',
        phone: '',
        email: '',
        yzm: '',
        yzm2: ''
      },
      rules: {
        phone: [{
          required: true,
          message: '请输入手机号',
          trigger: 'change'
        }, {
          pattern: /^1(3|4|5|7|8|9)\d{9}$/,
          message: '手机号格式错误',
          trigger: 'blur'
        }],
        yzm: [
          {required: true, message: '验证码不能为空', trigger: 'change'},
          // {type: 'number', message: '验证码必须为数字', trigger: ['blur', 'change']}
        ],
      },
      rules2: {
        email: [
          {required: true, message: '请输入邮箱地址', trigger: 'change'},
          {type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']}
        ],
        yzm2: [
          {required: true, message: '验证码不能为空', trigger: 'change'},
          // {type: 'number', message: '验证码必须为数字', trigger: ['blur', 'change']}
        ]
      },
    }
  },
  methods: {
    //手机号登录
    submitForm(path) {
      this.$refs['elForm'].validate(valid => {
        if (!valid) return
        // 显示loading 加载效果
        this.loading = true;
        const user = {
          phone:this.formData.phone,
          code: this.formData.yzm
        }
        this.timer = setTimeout(() => {
          phoneLogin(user).then( res=>{
            if (res.code == 200){
              this.$message({
                type: 'success',
                message: '登录成功!'
              })
              // 处理业务逻辑 以及发送请求
              this.$router.push(path)
            }else {
              this.$message({
                type: 'error',
                message: '登录失败!'
              });
            }
          }).finally((
              this.loading = false
          ));
        }, 1000);
      })
    },
    //邮箱登录
    submitForm2(path) {
      this.$refs['elForm2'].validate(valid => {
        if (!valid) return
        // TODO 提交表单
        // 显示loading 加载效果
        this.loading2 = true;
        const user = {
          email:this.formData.email,
          code: this.formData.yzm2
        }
        this.timer = setTimeout(() => {
          // 处理业务逻辑 以及发送请求
          emailLogin(user).then(res=>{
            console.log(res)
            if (res.code == 200){
              this.$message({
                type: 'success',
                message: '登录成功!'
              })
              // 处理业务逻辑 以及发送请求
              this.$router.push(path)
            }else {
              this.$message({
                type:'error',
                message: '登录失败!'
              })
            }
          }).finally((
              this.loading2 = false
          ));
        }, 1000);
      })
    },
    //发送手机验证码
    sendCode() {
      // TODO 提交表单
      let user ={phone:this.formData.phone}
      console.log(user)
      sendPhoneCode(user).then(res => {
        if (res.code == 200) {
          this.$message({
            type: 'success',
            message: '验证码发送成功!'
          })
        } else {
            this.$message({
              type: 'warning',
              message: '验证码发送失败!'
            });
        }
      })
    },
    startTimer() {
      this.$refs['elForm'].validateField('phone', valid => {
        if (valid) return
        // TODO 提交表单
        this.remainingTime = 60;
        this.sendCode()
        const timer = setInterval(() => {
          this.remainingTime--;
          if (this.remainingTime === 0) {
            clearInterval(timer);
          }
        }, 1000);
      })
    },
    startTimer2() {
      this.$refs['elForm2'].validateField('email', valid => {
        if (valid) return
        // TODO 提交表单
        this.remainingTime = 60;
        this.sendCodeTwo()
        const timer = setInterval(() => {
          this.remainingTime--;
          if (this.remainingTime === 0) {
            clearInterval(timer);
          }
        }, 1000);
      })
    },
    //发送邮箱验证码
    sendCodeTwo() {
      // TODO 提交表单
      let user ={email:this.formData.email}
      console.log(user)
      sendEmailCode(user).then(res => {
        if (res.code == 200) {
          this.$message({
            type: 'success',
            message: '验证码发送成功!'
          })
        } else {
          this.$message({
            type: 'warning',
            message: '验证码发送失败!'
          });
        }
      })
    },
  },
    mounted() {
      document.querySelector('.img__btn').addEventListener('click', function () {
        const contEl = document.querySelector('.cont');
        contEl.classList.toggle('s--signup');
        contEl.addEventListener('transitionend', function () {
          if (contEl.classList.contains('s--signup')) {
            contEl.querySelectorAll('input').forEach(inputEl => inputEl.value = '');
          }
        }, {once: true});
      });
    },
    beforeDestroy() {
      // eslint-disable-next-line no-irregular-whitespace
      clearInterval(this.timer); // 清除定时器
      this.timer = null;
    },
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
*, *:before, *:after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
input, button {
  border: none;
  outline: none;
  background: none;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
}
.tip {
  font-size: 20px;
  margin: 40px auto 50px;
  text-align: center;
}
.cont1::before {
  content: '';
  position: fixed;
  background-color: #ededed;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  background-image: linear-gradient(to bottom right ,#d3dae9,#14c2c2)
}
.cont {
  overflow: hidden;
  position: relative;
  width: 900px;
  height: 550px;
  margin: 0 auto 100px;
  background: #fff;
}
.form {
  position: relative;
  width: 640px;
  height: 100%;
  transition: transform 1.2s ease-in-out;
  padding: 50px 30px 0;
}
.sub-cont {
  overflow: hidden;
  position: absolute;
  left: 640px;
  top: 0;
  width: 900px;
  height: 100%;
  padding-left: 260px;
  background: #fff;
  transition: transform 1.2s ease-in-out;
}
.cont.s--signup .sub-cont {
  transform: translate3d(-640px, 0, 0);
}
button {
  display: block;
  margin: 0 auto;
  width: 260px;
  height: 36px;
  border-radius: 30px;
  color: #fff;
  font-size: 15px;
  cursor: pointer;
}
.img {
  overflow: hidden;
  z-index: 2;
  position: absolute;
  left: 0;
  top: 0;
  width: 260px;
  height: 100%;
  padding-top: 360px;
}
.img:before {
  content: "";
  position: absolute;
  right: 0;
  top: 0;
  width: 900px;
  height: 100%;
  background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/sections-3.jpg");
  background-size: cover;
  transition: transform 1.2s ease-in-out;
}
.img:after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.6);
}
.cont.s--signup .img:before {
  transform: translate3d(640px, 0, 0);
}
.img__text {
  z-index: 2;
  position: absolute;
  left: 0;
  top: 50px;
  width: 100%;
  padding: 0 20px;
  text-align: center;
  color: #fff;
  transition: transform 1.2s ease-in-out;
}
.img__text h2 {
  margin-bottom: 10px;
  font-weight: normal;
}
.img__text p {
  font-size: 14px;
  line-height: 1.5;
}
.cont.s--signup .img__text.m--up {
  transform: translateX(520px);
}
.img__text.m--in {
  transform: translateX(-520px);
}
.cont.s--signup .img__text.m--in {
  transform: translateX(0);
}
.img__btn {
  overflow: hidden;
  z-index: 2;
  position: relative;
  width: 100px;
  height: 36px;
  margin: 0 auto;
  background: transparent;
  color: #fff;
  text-transform: uppercase;
  font-size: 15px;
  cursor: pointer;
}
.img__btn:after {
  content: "";
  z-index: 2;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  border: 2px solid #fff;
  border-radius: 30px;
}
.img__btn span {
  position: absolute;
  left: 0;
  top: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  transition: transform 1.2s;
}
.img__btn span.m--in {
  transform: translateY(-72px);
}
.cont.s--signup .img__btn span.m--in {
  transform: translateY(0);
}
.cont.s--signup .img__btn span.m--up {
  transform: translateY(72px);
}
h2 {
  width: 100%;
  font-size: 26px;
  text-align: center;
}
label {
  display: block;
  width: 260px;
  margin: 25px auto 0;
  text-align: center;
}
label span {
  font-size: 12px;
  color: #cfcfcf;
  text-transform: uppercase;
}
input {
  display: block;
  width: 100%;
  margin-top: 5px;
  padding-bottom: 5px;
  font-size: 16px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.4);
  text-align: center;
}
.forgot-pass {
  margin-top: 15px;
  text-align: center;
  font-size: 12px;
  color: #cfcfcf;
}
.submit {
  margin-top: 30px;
  margin-left: 50px;
  margin-bottom: 10px;
  background: #d4af7a;
  /*text-transform: uppercase;*/
}
.style2 {
  margin-top: 10px;
  margin-right: 100px;
  margin-bottom: 10px;
  background: darkgrey;
  text-transform: uppercase;
}
.fb-btn {
  border: 2px solid #d3dae9;
  color: #8fa1c7;
}
.fb-btn span {
  font-weight: bold;
  color: #455a81;
}
.sign-in {
  transition-timing-function: ease-out;
}
.cont.s--signup .sign-in {
  transition-timing-function: ease-in-out;
  transition-duration: 1.2s;
  transform: translate3d(640px, 0, 0);
}
.sign-up {
  transform: translate3d(-900px, 0, 0);
}
.cont.s--signup .sign-up {
  transform: translate3d(0, 0, 0);
}
.icon-link {
  position: absolute;
  left: 5px;
  bottom: 5px;
  width: 32px;
}
.icon-link img {
  width: 100%;
  vertical-align: top;
}
.icon-link--twitter {
  left: auto;
  right: 5px;
}
.test[disabled]:not(.is-loading) {
  opacity: 0.5;
  cursor: not-allowed;
  width: 5px;
  margin-right: 10px;
  text-align: center;
}
</style>

页面效果图:

手机登录

2bb91a3e853a4abcb54e9dbfe0f1f33e.png

邮箱登录

5e6270af62154c17b56387ce42a618e2.png

短信:

a49649d14e8e4ac7b62bbcd91453f615.jpg


目录
相关文章
|
30天前
|
缓存 Kubernetes 网络协议
阿里云DNS常见问题之在手机上使用阿里的私人dns失败如何解决
阿里云DNS(Domain Name System)服务是一个高可用和可扩展的云端DNS服务,用于将域名转换为IP地址,从而让用户能够通过域名访问云端资源。以下是一些关于阿里云DNS服务的常见问题合集:
|
2月前
|
云安全 安全 API
阿里云——OpenAPI使用——短信服务
阿里云——OpenAPI使用——短信服务
155 0
|
2月前
|
弹性计算 Windows
使用阿里云服务器登录雾锁王国后,游戏创建失败怎么办
使用阿里云服务器登录雾锁王国后,游戏创建失败时,请更新游戏并重启游戏进程。
284 3
|
2月前
|
存储 安全
阿里云网盘与相册问题之账户登录不上如何解决
阿里云网盘与相册是阿里云提供的云存储服务,用户可以安全便捷地存储和管理个人文件、照片等数据;本合集将介绍如何使用阿里云网盘和相册服务,包括文件上传、同步、分享,以及处理常见使用问题的技巧。
45 2
|
2月前
|
存储 缓存 安全
阿里云网盘与相册问题之如何解除限制境外IP登录
阿里云网盘与相册是阿里云提供的云存储服务,用户可以安全便捷地存储和管理个人文件、照片等数据;本合集将介绍如何使用阿里云网盘和相册服务,包括文件上传、同步、分享,以及处理常见使用问题的技巧。
42 0
|
2月前
|
存储 监控 安全
阿里云云通信短信服务安全之安全架构
阿里云云通信长期致力于通过多种渠道向客户透明服务相关情况。客户一般可通过阿里云官网提出对阿里云云通信相关资质、服务使用情况、产品说明等信息,我们将7*24小时不间断处理您的建议与咨询。对于客户合理的要求,阿里云云通信服务团队均会及时响应客户的需求。同时,阿里云云通信也在探索更多增加透明度的方式,如对公邮箱、线上查询接口、钉钉服务客户群等。
|
2月前
|
运维 监控 安全
阿里云云通信短信服务安全之安全责任共担与安全合规
阿里云云通信长久以来致力于为客户提供稳定可靠、安全可信、合法合规的通信服务,利用先进技术、建立产品体系、严格管理人员等不同手段以保护客户及合作伙伴数据的机密性、完整性、可用性,以数据安全、用户隐私保护作为阿里云云通信的最高准则。
|
3月前
|
JSON 开发工具 数据格式
App Inventor 2 接入阿里云短信服务,实现短信验证码功能
App Inventor 2 接入阿里云短信服务,实现短信验证码功能:发送短信验证码功能一般都是基于短信平台提供的sdk进行调用,这里是基于阿里云短信平台进行的开发。
115 1
|
4月前
|
Java Maven
(短信服务)java SpringBoot 阿里云短信功能实现发送手机验证码
(短信服务)java SpringBoot 阿里云短信功能实现发送手机验证码
669 0
|
1月前
|
Ubuntu JavaScript 关系型数据库
在阿里云Ubuntu 20.04服务器中搭建一个 Ghost 博客
在阿里云Ubuntu 20.04服务器上部署Ghost博客的步骤包括创建新用户、安装Nginx、MySQL和Node.js 18.x。首先,通过`adduser`命令创建非root用户,然后安装Nginx和MySQL。接着,设置Node.js环境,下载Nodesource GPG密钥并安装Node.js 18.x。之后,使用`npm`安装Ghost-CLI,创建Ghost安装目录并进行安装。配置过程中需提供博客URL、数据库连接信息等。最后,测试访问前台首页和后台管理页面。确保DNS设置正确,并根据提示完成Ghost博客的配置。
在阿里云Ubuntu 20.04服务器中搭建一个 Ghost 博客

热门文章

最新文章