谷粒学院(十五)JWT | 阿里云短信服务 | 登录与注册前后端实现(二)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
国际/港澳台短信套餐包,全球plus 100条 6个月
简介: 谷粒学院(十五)JWT | 阿里云短信服务 | 登录与注册前后端实现(二)

7、编写controller,根据手机号发送短信


@RestController
@RequestMapping("/smsservice/sms")
@CrossOrigin
public class SmsApiController {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private SmsService smsService;
    //阿里云发送短信
    @GetMapping("sendMsgByAly/{phone}")
    public R sendMsgByAly(@PathVariable String phone){
        //生成随机值,传递到阿里云进行发送
        String code = RandomUtil.getSixBitRandom();
        Map <String, Object> param = new HashMap <>();
        param.put("code", code);
        //调用service进行短信发送
        boolean isSend = smsService.sendMsgAly(param, phone);
        if(isSend){
            return R.ok();
        }else{
            return R.error().message("短信发送失败");
        }
    }
    //使用国阳云发送短信
    @GetMapping("sendMsgByGyy/{phone}")
    public R sendMsgByGyy(@PathVariable String phone){
        //从redis中获取验证码,如果获取则直接返回..==> 根据现实情况,用户可以多次发送.
        // String code = redisTemplate.opsForValue().get(phone);
        // if(!StringUtils.isEmpty(code)){
        //     return R.ok();
        // }
        //2.如果redis获取不到,进行阿里云发送
        //生成随机值,传递到阿里云进行发送
        String code = RandomUtil.getSixBitRandom();
        //调用service进行短信发送
        boolean isSend = smsService.sendMsgGyy(code, phone);
        if(isSend){
            //发送成功,把发送成功验证码放入到redis中
            //设置有效时间
            redisTemplate.opsForValue().set(phone, code, 30, TimeUnit.MINUTES);
            return R.ok();
        }else{
            return R.error().message("短信发送失败");
        }
    }
}

8、编写service

@Service
public class SmsServiceImpl implements SmsService {
    //通过阿里云发送短信(由于未申请下来,所以就先写个方法.项目中用国阳云/邮箱进行代替)
    @Override
    public boolean sendMsgAly(Map <String, Object> param, String phone) {
        if(StringUtils.isEmpty(phone)) return false;
        DefaultProfile profile =
                DefaultProfile.getProfile("default", "LTAIq6nIPY09VROj", "FQ7UcixT9wEqMv9F35nORPqKr8XkTF");
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        //request.setProtocol(ProtocolType.HTTPS);
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("SendSms");
        request.putQueryParameter("PhoneNumbers", phone);
        request.putQueryParameter("SignName", "我的谷粒在线教育网站");
        request.putQueryParameter("TemplateCode", "templateCode");
        request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));
        try {
            CommonResponse response = client.getCommonResponse(request);
            System.out.println(response.getData());
            return response.getHttpResponse().isSuccess();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    //使用国阳云发送短信
    @Override
    public boolean sendMsgGyy(String code, String phone) {
        try {
            //真正的短信发送 用于上线
            // SmsUtil.sendMessage(ConstantPropertiesUtil.APP_CODE,phone,code,"30",ConstantPropertiesUtil.SMS_SIGNID,ConstantPropertiesUtil.TEMPLATE_ID);
            //太贵了,先用邮箱代替....
            MailUtils.sendMail("2422737092@qq.com", "[谷粒学院] 验证码: \n"+code+"您正在进行注册,若非本人操作,请勿泄露.30分钟内有效.", "谷粒学院验证码");
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
}

9、使用swagger进行测试


http://localhost:8005/swagger-ui.html

国阳云短信如何使用,可参考 如何利用阿里云市场购买并使用短信服务


六、用户登录注册接口【后端】


1、在service模块下创建子模块service_ucenter

2、使用代码生成器生成代码


(1)创建ucenter_member表

(2)利用代码生成器生成代码



8a42e94b77865880e408daf99364510b.png


3、配置application.properties

# 服务端口
server.port=8006
# 服务名
spring.application.name=service-ucenter
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli_edu
spring.datasource.username=root
spring.datasource.password=186259
spring.redis.host=192.168.174.128
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
#最小空闲
#nacos服务地址
spring.cloud.nacos.discovery.server-addr=192.168.174.128:8848
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/rg/ucenterservice/mapper/xml/*.xml
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl


4、创建启动类


创建UcenterMemberApplication.java

@SpringBootApplication
@ComponentScan("com.rg")
@MapperScan("com.rg.ucenterservice.mapper")
public class UcenterMemberApplication {
    public static void main(String[] args) {
        SpringApplication.run(UcenterMemberApplication.class, args);
    }
}

5、创建Controller编写登录和注册方法

@RestController
@RequestMapping("/ucenterservice/member")
@CrossOrigin
public class UcenterMemberController {
    @Autowired
    private UcenterMemberService memberService;
    //用户登录
    @PostMapping("login")
    public R login(@RequestBody LoginVo loginVo){
        //返回token,使用jwt生成
        String token = memberService.login(loginVo);
        return R.ok().data("token",token);
    }
    //用户注册
    @PostMapping("register")
    public R register(@RequestBody RegisterVo registerVo){
        R result = memberService.register(registerVo);
        return result;
    }
    //根据token获取用户信息
    @GetMapping("getMemberInfo")
    public R getMemberInfo(HttpServletRequest request){
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        UcenterMember member = memberService.getById(memberId);
        return R.ok().data("userInfo", member);
    }
}

6、创建service接口和实现类

@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    //用户登录
    @Override
    public String login(LoginVo loginVo) {
        String mobile = loginVo.getMobile();
        String password = loginVo.getPassword();
        //判断用户名密码,是否为空
        if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)){
            throw new GuLiException(20001, "用户名或密码不能为空!");
        }
        //去数据库查询
        QueryWrapper <UcenterMember> wrapper = new QueryWrapper <>();
        wrapper.eq("mobile",mobile);
        UcenterMember ucenterMember = this.baseMapper.selectOne(wrapper);
        //判断UCenterMember是否为空
        if(ucenterMember==null){//没有这个手机号
            throw new GuLiException(20001,"用户名或密码错误!");
        }
        //判断密码是否相等
        if(!MD5Utils.md5(password).equals(ucenterMember.getPassword())){
            throw new GuLiException(20001,"用户名或密码错误!");
        }
        //判断是否被禁用
        if(ucenterMember.getIsDisabled()){
            throw new GuLiException(20001, "您的账号暂时被冻结,请联系管理员!");
        }
        //登录成功
        //生成token字符串,使用jwt工具类
        String jwtToken = JwtUtils.getJwtToken(ucenterMember.getId(), ucenterMember.getMobile());
        return jwtToken;
    }
    //用户注册
    @Override
    public R register(RegisterVo registerVo) {
        String code = registerVo.getCode();
        String mobile = registerVo.getMobile();
        String nickname = registerVo.getNickname();
        String password = registerVo.getPassword();
        //判断是否为空
        if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password) || StringUtils.isEmpty(code) || StringUtils.isEmpty(nickname)){
            return R.error().message("数据格式不正确!!");
        }
        //判断验证码是否正确
        String redisCode = redisTemplate.opsForValue().get(mobile);
        if(!code.equals(redisCode)){
            return R.error().message("验证码无效或已过期,请重新发送验证码!");
        }
        //判断手机号是否已经注册
        QueryWrapper <UcenterMember> wrapper = new QueryWrapper <>();
        wrapper.eq("mobile",mobile);
        Integer count = this.baseMapper.selectCount(wrapper);
        if(count > 0){
            return R.error().message("手机号已被注册!");
        }
        //判断昵称是否重复
        wrapper = new QueryWrapper <UcenterMember>();
        wrapper.eq("nickname",nickname);
        count = this.baseMapper.selectCount(wrapper);
        if(count > 0){
            return R.error().message("昵称 昵称已被使用,换一个吧");
        }
        //进行数据插入
        UcenterMember member = new UcenterMember();
        member.setMobile(mobile);
        member.setNickname(nickname);
        member.setPassword(MD5Utils.md5(password));
        member.setIsDeleted(false);//用户不禁用
        member.setAvatar("https://guli-photos.oss-cn-hangzhou.aliyuncs.com/FatDog.jpg");
        this.baseMapper.insert(member);
        return R.ok();
    }
}

7、MD5工具类

public class MD5Utils {
  /**
   * 使用md5的算法进行加密
   */
  public static String md5(String plainText) {
    byte[] secretBytes = null;
    try {
      secretBytes = MessageDigest.getInstance("md5").digest(
          plainText.getBytes());
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException("没有md5这个算法!");
    }
    String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
    // 如果生成数字未满32位,需要前面补0
    for (int i = 0; i < 32 - md5code.length(); i++) {
      md5code = "0" + md5code;
    }
    return md5code;
  }
  public static void main(String[] args) {
    System.out.println(md5("123456"));
  }
}


8、使用swagger测试


http://localhost:8006/swagger-ui.html


七、用户登录注册【前端】


1、在Nuxt环境中安装插件

(1)安装element-ui 和 vue-qriously插件


npm install element-ui
npm install vue-qriously


(2)修改配置文件 nuxt-swiper-plugin.js,使用插件

import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'
import VueQriously from 'vue-qriously'
import ElementUI from 'element-ui' //element-ui的全部组件
import 'element-ui/lib/theme-chalk/index.css'//element-ui的css
Vue.use(ElementUI) //使用elementUI
Vue.use(VueQriously)
Vue.use(VueAwesomeSwiper)


2、用户注册功能前端整合


1、在api文件夹中定义接口


register.js

import request from '@/utils/request'
export default {
    //发送短信
    sendCode(phone){
        return request({
            url:`/smsservice/sms/sendMsgByGyy/${phone}`,
            method:`get`
        })
    },
    //注册的方法
    registerMember(formItem){
        return request({
            url:`/ucenterservice/member/register`,
            method:`post`,
            data:formItem
        })
    }
}


2、在pages文件夹中创建注册页面,调用方法


(1)在layouts创建布局页面


sign.vue

<template>
  <div class="sign">
    <!--标题-->
    <div class="logo">
      <img src="~/assets/img/logo.png" alt="logo">
    </div>
    <!--表单-->
    <nuxt/>
  </div>
</template>

(2)创建注册页面

修改layouts文件夹里面default.vue页面,修改登录和注册超链接地址


e5b903185c32f5a530964d04f9b06449.png

在pages文件夹下,创建注册和登录页面


image.png


register.vue

<template>
  <div class="main">
    <div class="title">
      <a href="/login">登录</a>
      <span>·</span>
      <a class="active" href="/register">注册</a>
    </div>
    <div class="sign-up-container">
      <el-form ref="userForm" :model="params">
        <el-form-item class="input-prepend restyle" prop="nickname" :rules="[{ required: true, message: '请输入你的昵称', trigger: 'blur' }]">
          <div>
            <el-input type="text" placeholder="你的昵称" v-model="params.nickname"/>
            <i class="iconfont icon-user"/>
          </div>
        </el-form-item>
        <el-form-item class="input-prepend restyle no-radius" prop="mobile" :rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone, trigger: 'blur'}]">
          <div>
            <el-input type="text" placeholder="手机号" v-model="params.mobile"/>
            <i class="iconfont icon-phone"/>
          </div>
        </el-form-item>
        <el-form-item class="input-prepend restyle no-radius" prop="code" :rules="[{ required: true, message: '请输入验证码', trigger: 'blur' }]">
          <div style="width: 100%;display: block;float: left;position: relative">
            <el-input type="text" placeholder="验证码" v-model="params.code"/>
            <i class="iconfont icon-phone"/>
          </div>
          <div class="btn" style="position:absolute;right: 0;top: 6px;width: 45%;">
            <a href="javascript:" type="button" @click="getCodeFun()" :value="codeTest" style="border: none;background-color: none">
                <el-button type="success" round size="medium" :disabled="!sending">{{codeTest}}</el-button>
                </a>
          </div>
        </el-form-item>
        <el-form-item class="input-prepend" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
          <div>
            <el-input type="password" placeholder="设置密码" v-model="params.password"/>
            <i class="iconfont icon-password"/>
          </div>
        </el-form-item>
        <div class="btn">
          <input type="button" class="sign-up-button" value="注册" @click="submitRegister('userForm')">
        </div>
        <p class="sign-up-msg">
          点击 “注册” 即表示您同意并愿意遵守简书
          <br>
          <a target="_blank" href="http://www.jianshu.com/p/c44d171298ce">用户协议</a>
          <a target="_blank" href="http://www.jianshu.com/p/2ov8x3">隐私政策</a> 。
        </p>
      </el-form>
      <!-- 更多注册方式 -->
      <div class="more-sign">
        <h6>社交帐号直接注册</h6>
        <ul>
          <li><a id="weixin" class="weixin" target="_blank" href="http://huaan.free.idcfengye.com/api/ucenter/wx/login"><i
            class="iconfont icon-weixin"/></a></li>
          <li><a id="qq" class="qq" target="_blank" href="#"><i class="iconfont icon-qq"/></a></li>
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
  import '~/assets/css/sign.css'
  import '~/assets/css/iconfont.css'
  import registerApi from '@/api/register'
  export default {
    layout: 'sign',
    data() {
      return {
        params: {
          mobile: '',
          code: '',
          nickname: '',
          password: ''
        },
        sending: true,      //是否发送验证码
        second: 60,        //倒计时间
        codeTest: '获取验证码'
      }
    },
    methods: {
      //注册提交的方法
      submitRegister(formName){
          this.$refs[formName].validate(valid=>{
            if(!valid){
              this.$message({
                type:'error',
                message:'请填写正确的注册信息,再进行注册!'
              })
              return false;
            }
            registerApi.registerMember(this.params).then(response=>{
              console.log(response);
              //提示登录成功
              this.$message({
                  type:'success',
                  message:'注册成功'
              })
              //跳转到登录页面
              this.$router.push({path:'/login'})
          }) //这里不用写catch了,因为外部进行了处理
          })
      },
      getCodeFun(){
          if(this.sending==false || this.params.mobile==""){
              return;
          }
          registerApi.sendCode(this.params.mobile).then(response=>{
              //禁止发送
              this.sending = false
              //倒计时
              this.timeDown()
              //提示验证码发送成功
              this.$message({
                  type:'success',
                  message:'验证码已发送'
              })
          })
      },
      timeDown() {
        let result = setInterval(() => {
          --this.second;
          this.codeTest = "重新发送"+this.second+"(s)"
          if (this.second < 1) {
            clearInterval(result);
            this.sending = true;
            //this.disabled = false;
            this.second = 60;
            this.codeTest = "获取验证码"
          }
        }, 1000);
      },
      checkPhone (rule, value, callback) {
        //debugger
        if (!(/^1[34578]\d{9}$/.test(value))) {
          return callback(new Error('手机号码格式不正确'))
        }
        return callback()
      }
    }
  }
</script>

3、修改nginx配置文件的端口



image.png

4、启动服务进行测试


3、用户登录功能前端整合


1、在api文件夹中创建登录的js文件,定义接口

import request from '@/utils/request'
export default {
    //登录的方法
    submitLoginUser(userInfo){
        return request({
            url:`/ucenterservice/member/login`,
            method:'post',
            data:userInfo
        })
    },
    //根据token获取用户信息
    getLoginUserInfo(){
        return request({
            url:`/ucenterservice/member/getMemberInfo`,
            method:'get'
        })
    }
}

2、在pages文件夹中创建登录页面,调用方法


(1)安装js-cookie插件


npm install js-cookie


(2)login.vue


<template>
  <div class="main">
    <div class="title">
      <a class="active" href="/login">登录</a>
      <span>·</span>
      <a href="/register">注册</a>
    </div>
    <div class="sign-up-container">
      <el-form ref="userForm" :model="user">
        <el-form-item class="input-prepend restyle" prop="mobile" :rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone, trigger: 'blur'}]">
          <div >
            <el-input type="text" placeholder="手机号" v-model="user.mobile"/>
            <i class="iconfont icon-phone" />
          </div>
        </el-form-item>
        <el-form-item class="input-prepend" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
          <div>
            <el-input type="password" placeholder="密码" v-model="user.password"/>
            <i class="iconfont icon-password"/>
          </div>
        </el-form-item>
        <div class="btn">
          <input type="button" class="sign-in-button" value="登录" @click="submitLogin()">
        </div>
      </el-form>
      <!-- 更多登录方式 -->
      <div class="more-sign">
        <h6>社交帐号登录</h6>
        <ul>
          <li><a id="weixin" class="weixin" target="_blank" href="http://localhost:8160/api/ucenter/wx/login"><i class="iconfont icon-weixin"/></a></li>
          <li><a id="qq" class="qq" target="_blank" href="#"><i class="iconfont icon-qq"/></a></li>
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
  import '~/assets/css/sign.css'
  import '~/assets/css/iconfont.css'
  import cookie from 'js-cookie'
  import loginApi from '@/api/login'
  export default {
    layout: 'sign',
    data () {
      return {
        user:{
          mobile:'',
          password:''
        },
        loginInfo:{}
      }
    },
    methods: {
      submitLogin(){
        //第一步调用接口进行登录,返回token字符串
        loginApi.submitLoginUser(this.user).then(response=>{
          //第二步,获取token字符串存放到token中
          cookie.set('guli_token',response.data.data.token,{domain:'localhost'})
          //第四步 调用接口 根据token获取用户信息,为了首页面显示
          loginApi.getLoginUserInfo().then(response=>{
            this.loginInfo = response.data.data.userInfo
            //获取用户信息,存放到cookie里面
            cookie.set("guli_ucenter",JSON.stringify(this.loginInfo),{domain:'localhost'})
            //跳转页面
            window.location.href = "/"
          })
        })
      },
      checkPhone (rule, value, callback) {
        //debugger
        if (!(/^1[34578]\d{9}$/.test(value))) {
          return callback(new Error('手机号码格式不正确'))
        }
        return callback()
      }
    }
  }
</script>
<style>
   .el-form-item__error{
    z-index: 9999999;
  }
</style>


登录和登录成功后首页显示具体实现过程分析:


(1) 调用接口登录返回token字符串

(2) 把第一步返回token字符串放到cookie里面

(3) 创建拦截器:判断cookie里面是否有token字符串,如果有,把token字符串放到header(请求头中)

(4) 根据token值,调用接口,根据token获取用户信息,为了首页面显示。调用接口返回用户信息放到cookie里面

(5) 从首页面显示用户信息,从第四步cookie获取用户信息


3、在request.js添加拦截器,用于传递token信息

import axios from 'axios'
import cookie from 'js-cookie'
import { MessageBox, Message } from 'element-ui'
// 创建axios实例
const service = axios.create({
  baseURL: 'http://localhost:9001', // api的base_url
  timeout: 15000 // 请求超时时间
})
//第三步 创建拦截器  http request 拦截器
service.interceptors.request.use(
  config => {
  //debugger
  if (cookie.get('guli_token')) {
    config.headers['token'] = cookie.get('guli_token');
  }
    return config
  },
  err => {
  return Promise.reject(err);
})
// http response 拦截器
service.interceptors.response.use(
  response => {
    //debugger
    if (response.data.code == 28004) {
        console.log("response.data.resultCode是28004")
        // 返回 错误代码-1 清除ticket信息并跳转到登录页面
        //debugger
        window.location.href="/login"
        return
    }else{
      if (response.data.code !== 20000) {
        //25000:订单支付中,不做任何提示
        if(response.data.code != 25000) {
          Message({
            message: response.data.message || 'error',
            type: 'error',
            duration: 5 * 1000
          })
          return Promise.reject('error')
        }
      } else {
        return response;
      }
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error.response)   // 返回接口返回的错误信息
});
export default service


4、修改layouts中的default.vue页面


(1)显示登录之后的用户信息


<script>
import '~/assets/css/reset.css'
import '~/assets/css/theme.css'
import '~/assets/css/global.css'
import '~/assets/css/web.css'
import cookie from 'js-cookie'
import loginApi from '@/api/login'
export default {
  data() {
    return {
      token: '',
      loginInfo: {
        id: '',
        age: '',
        avatar: '',
        mobile: '',
        nickname: '',
        sex: ''
      }
    }
  },
  created() {
    //获取路径里面的token值
    this.token = this.$route.query.token
    if(this.token){//判断路径中是否有token值
      this.wxLogin()
      return;
    }
    this.showInfo()
  },
  methods: {
    //从cookie中获取用户信息
    showInfo(){
      //从cookie获取用户信息  cookie中存放的都是str
      let userStr = cookie.get('guli_ucenter')
      console.log(userStr);
      if(userStr){
        this.loginInfo = JSON.parse(userStr)
      }
    },
    //退出
    logout(){
      //清空cookie值
      cookie.set('guli_token','',{domain:'localhost'})
      cookie.set('guli_ucenter','',{domain:'localhost'})
      //回到首页面
      window.location.href = "/"
    }
  }
}
</script>

(2)default.vue页面显示登录之后的用户信息


<ul class="h-r-login">
    <li v-if="!loginInfo.id" id="no-login">
        <a href="/login" title="登录">
            <em class="icon18 login-icon">&nbsp;</em>
            <span class="vam ml5">登录</span>
        </a>
        |
        <a href="/register" title="注册">
            <span class="vam ml5">注册</span>
        </a>
    </li>
    <li v-if="loginInfo.id" id="is-login-one" class="mr10">
        <a id="headerMsgCountId" href="#" title="消息">
            <em class="icon18 news-icon">&nbsp;</em>
        </a>
        <q class="red-point" style="display: none">&nbsp;</q>
    </li>
    <li v-if="loginInfo.id" id="is-login-two" class="h-r-user">
        <a href="/ucenter" title>
            <img
                 :src="loginInfo.avatar"
                 width="30"
                 height="30"
                 class="vam picImg"
                 alt
                 >
            <span id="userName" class="vam disIb">{{ loginInfo.nickname }}</span>
        </a>
        <a href="javascript:void(0);" title="退出" @click="logout()" class="ml5">退出</a>
    </li>
    <!-- /未登录显示第1 li;登录后显示第2,3 li -->
</ul>


4、登录注册效果展示


  • 用户填写注册信息,填写收到的短信验证码(目前先用邮箱代替),进行注册


0778ff6eb34ebd997ad063fb374aa9d2.png

  • 注册表单校验功能


f03617f6aca8e35bb2e22ebd20c7e6a1.png

  • 注册成功


6b86474b7e0f25e03bdcef94217a2a61.png

  • 用户登录


280e25f4c7cba6f47f5789b4fd13641b.png

相关文章
|
2月前
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
131 0
|
22天前
|
数据采集 监控 安全
阿里云短信服务+图形认证,有效降低验证码盗刷概率
阿里云短信服务+图形认证服务,有效降低验证码盗刷概率。
阿里云短信服务+图形认证,有效降低验证码盗刷概率
|
4月前
|
存储 小程序 前端开发
【微信小程序 - 工作实战分享】1.微信小程序发送手机短信验证码(阿里云)
【微信小程序 - 工作实战分享】1.微信小程序发送手机短信验证码(阿里云)
319 0
|
22天前
|
存储 NoSQL Java
|
2月前
|
数据采集 存储 监控
99%成功率背后:阿里云短信服务有何优势?
为什么短信会发送失败,如何提高短信发送成功率,本文将为您介绍短信发送成功率和阿里云短信服务如何保障企业短信稳定送达等相关知识。
102 1
99%成功率背后:阿里云短信服务有何优势?
|
2月前
|
存储 安全 网络安全
|
2月前
【Azure APIM】在APIM中实现JWT验证不通过时跳转到Azure登录页面
【Azure APIM】在APIM中实现JWT验证不通过时跳转到Azure登录页面
|
2月前
|
小程序
阿里云短信签名申请流程,有图,短信接入新手教程
阿里云短信签名是短信中的标识信息,如【阿里云】,用于表明发送方身份。申请流程简便:登录阿里云短信服务控制台,选择签名管理并添加签名,填写相关信息。审核通常2小时内完成。个人用户每日限申请一个通用签名,企业用户数量不限。已通过审核的签名不可更改名称,仅能调整其他信息并重新提交审核。更多详情及FAQ
|
3月前
|
开发框架 前端开发 JavaScript
ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成
ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成
ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成
|
2月前
|
小程序
阿里云短信签名申请流程,手动整理(附短信签名问题解答)
阿里云短信签名是短信中的标识信息,如【阿里云】,用于表明发送方身份。申请流程简便:登录阿里云短信服务控制台,选择国内消息下的签名管理并添加签名,按指引填写表单。审核通常2小时内完成。个人用户每日限申请一个通用签名,企业用户数量不限。签名审核需确保业务主体一致,已通过审核的签名不可改名,仅能调整其它信息并重新审核。特定情况下需上传授权委托书,且签名需关联已备案网站。
110 1
下一篇
无影云桌面