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

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 谷粒学院(十五)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

相关文章
|
9天前
|
存储 安全
阿里云网盘与相册问题之账户登录不上如何解决
阿里云网盘与相册是阿里云提供的云存储服务,用户可以安全便捷地存储和管理个人文件、照片等数据;本合集将介绍如何使用阿里云网盘和相册服务,包括文件上传、同步、分享,以及处理常见使用问题的技巧。
21 2
|
9天前
|
存储 缓存 安全
阿里云网盘与相册问题之限制境外IP登录如何解决
阿里云网盘与相册是阿里云提供的云存储服务,用户可以安全便捷地存储和管理个人文件、照片等数据;本合集将介绍如何使用阿里云网盘和相册服务,包括文件上传、同步、分享,以及处理常见使用问题的技巧。
17 0
|
16天前
|
云安全 安全 API
阿里云——OpenAPI使用——短信服务
阿里云——OpenAPI使用——短信服务
44 0
|
17天前
|
存储 监控 安全
阿里云云通信短信服务安全之安全架构
阿里云云通信长期致力于通过多种渠道向客户透明服务相关情况。客户一般可通过阿里云官网提出对阿里云云通信相关资质、服务使用情况、产品说明等信息,我们将7*24小时不间断处理您的建议与咨询。对于客户合理的要求,阿里云云通信服务团队均会及时响应客户的需求。同时,阿里云云通信也在探索更多增加透明度的方式,如对公邮箱、线上查询接口、钉钉服务客户群等。
|
17天前
|
运维 监控 安全
阿里云云通信短信服务安全之安全责任共担与安全合规
阿里云云通信长久以来致力于为客户提供稳定可靠、安全可信、合法合规的通信服务,利用先进技术、建立产品体系、严格管理人员等不同手段以保护客户及合作伙伴数据的机密性、完整性、可用性,以数据安全、用户隐私保护作为阿里云云通信的最高准则。
|
17天前
|
弹性计算 Windows
使用阿里云服务器登录雾锁王国后,游戏创建失败怎么办
使用阿里云服务器登录雾锁王国后,游戏创建失败时,请更新游戏并重启游戏进程。
170 3
|
23天前
|
安全 Java 数据库
SpringSecurity+JWT前后端分离架构登录认证
在SpringSecurity实现前后端分离登录token认证详解_springsecurity前后端分离登录认证-CSDN博客基础上进行重构,实现前后端分离架构登录认证,基本思想相同,借鉴开源Gitee代码进行改造,具有更好的代码规范。
|
30天前
|
JSON 前端开发 安全
前后端分离项目知识汇总(微信扫码登录,手机验证码登录,JWT)-2
前后端分离项目知识汇总(微信扫码登录,手机验证码登录,JWT)
30 0
|
30天前
|
存储 JSON JavaScript
前后端分离项目知识汇总(微信扫码登录,手机验证码登录,JWT)-1
前后端分离项目知识汇总(微信扫码登录,手机验证码登录,JWT)
23 0
|
1月前
阿里云域名优惠:批量注册和续费优惠价格表
阿里云域名优惠:批量注册和续费优惠价格表,com和cn域名均有活动
99 5

相关产品

  • 云迁移中心