B070-项目实战-用户模块--手机注册

news2024/11/17 7:39:03

目录

      • 用户模块需求分析
      • 静态网站部署与调试
        • 两种前端项目的部署
        • 两种前端项目的调试(热部署)
        • 创建静态web项目
      • 注册分析与设计
        • 分析需求
        • 设计 界面设计(ui)设计表(后台) 流程设计(后台)
          • 三范式
          • 表设计
          • 流程设计
        • 相关技术准备
          • 1.随机字符串(用StrUtils工具类)
          • 2.springdata-redis
          • 3.Md5技术
          • 4.短信接口
      • 发送短信验证码
        • 前端页面准备
        • 发送短信后台
          • 搭建用户模块
          • 单独写公共可用的发短信接口
            • VerifycodeController
            • VerifycodeServiceImpl

用户模块需求分析

用户注册
  登录信息
  基本信息
  安全信息
用户登录
  账号登录
  扫码登录-自己app扫描页面
  三方登录-使用三方app(微信,qq,支付宝等)
  验证码登录-自己做  reids.set(logincode+phone,xxx)
用户管理
地址管理
收藏
   商品收藏
   店铺收藏
   服务收藏
   宠物收藏
   。。。。
足迹
   。。。。

静态网站部署与调试

两种前端项目的部署

vue-cli项目:pethome-admin(管理员的管理界面),用npm打包成静态项目后丢进Tomcat服务器跑
Css+div项目:pethome_web(用户访问门户网站),本身是静态项目可以直接丢进Tomcat服务器跑

两种前端项目的调试(热部署)

vue-cli项目可以npm run dev启动调试
传统静态项目需要安装 live-server服务器来调试,这个服务器很简单就是node的一个模块,
全局安装:npm install -g live-server,运行:live-server --port=80

创建静态web项目

拷贝amz_02_adp的two里项目资源到pethome-web目录下
复制home3.html到pethome-web根目录并改名为index.html作为首页
修改css和js引用路径

进入web子项目
全局安装:npm install -g live-server
启动调试:live-server --port=80

替换images图片

注册分析与设计

分析需求

注册分为:邮箱注册,和手机号注册,逻辑都是一样的,只是激活方式不一样。

  手机号注册:
    1)输入手机号
    2)获取验证码并且输入
    3)输入蜜马和确认蜜马
    4)完成注册

设计 界面设计(ui)设计表(后台) 流程设计(后台)

三范式

1NF:列的原子性,不可再拆分
2NF:行唯一,
3NF:如果一张表的数据能够通过其他表推导出来,不应该单独设计,应该通过外键的方式关联查询出来。

反3NF:有的时候我们为了增强查询效率,会设计一些冗余字段,变多表查询为单表查询。

表设计

在这里插入图片描述
在这里插入图片描述
注意:以后只要在eployee或user操作时涉及到登录信息都要同步操作logininfo。比如添加员工也要在logininfo添加一条记录,注册用户也要在logininfo中添加。同理删除或修改也要同步。

流程设计

见文档

相关技术准备

1.随机字符串(用StrUtils工具类)
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author yaohuaipeng
 * @date 2018/10/26-16:16
 */
public class StrUtils {
    /**
     * 把逗号分隔的字符串转换字符串数组
     *
     * @param str
     * @return
     */
    public static String[] splitStr2StrArr(String str,String split) {
        if (str != null && !str.equals("")) {
            return str.split(split);
        }
        return null;
    }


    /**
     * 把逗号分隔字符串转换List的Long
     *
     * @param str
     * @return
     */
    public static List<Long> splitStr2LongArr(String str) {
        String[] strings = splitStr2StrArr(str,",");
        if (strings == null) return null;

        List<Long> result = new ArrayList<>();
        for (String string : strings) {
            result.add(Long.parseLong(string));
        }

        return result;
    }
    /**
     * 把逗号分隔字符串转换List的Long
     *
     * @param str
     * @return
     */
    public static List<Long> splitStr2LongArr(String str,String split) {
        String[] strings = splitStr2StrArr(str,split);
        if (strings == null) return null;

        List<Long> result = new ArrayList<>();
        for (String string : strings) {
            result.add(Long.parseLong(string));
        }

        return result;
    }

    public static String getRandomString(int length) {
        String str = "0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(10);
            sb.append(str.charAt(number));
        }
        return sb.toString();

    }

    // 26字母+10数字
    public static String getComplexRandomString(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

    public static String convertPropertiesToHtml(String properties){
        //1:容量:6:32GB_4:样式:12:塑料壳
        StringBuilder sBuilder = new StringBuilder();
        String[] propArr = properties.split("_");
        for (String props : propArr) {
            String[] valueArr = props.split(":");
            sBuilder.append(valueArr[1]).append(":").append(valueArr[3]).append("<br>");
        }
        return sBuilder.toString();
    }

}
2.springdata-redis

A 导入jar

<!--spirngboot springdata对redis支持-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

B 配置属性

spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password: 123456
    jedis:
      pool:
        max-wait: 2000ms
        min-idle: 2
        max-idle: 8

启动redis服务:找到redis安装目录,cmd运行,redis-server.exe redis.windows.conf
C 编写测试类(基于springboot的测试类(继承BaseTest即可),用springdataredis提供的bean直接操作redis)

public class RedisTest extends BaseTest {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void test(){
        redisTemplate.opsForValue().set("ceshi", "搞一条数据");
        System.out.println(redisTemplate.opsForValue().get("ceshi"));

    }
}
3.Md5技术

不可逆加密技术,只能加密不能解密。 只能做比对,一般用来加密用户登录蜜马。
我们要做的是把传入的蜜马进行加密和然后数据库查询出来的密文进行比对判断蜜马是否正确。

盐值:同一种加密算法,由于不同的盐值,加密出来就不一样。
每个用户都有自己盐值,就算是相同的蜜马,两个用户加密出来的密文也不一样。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Utils {

    /**
     * 加密
     * @param context  明文
     */
    public static String encrypByMd5(String context) {
        try {  
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(context.getBytes());//update处理
            byte [] encryContext = md.digest();//调用该方法完成计算

            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < encryContext.length; offset++) {//做相应的转化(十六进制)
                i = encryContext[offset];
                if (i < 0) i += 256;
                if (i < 16) buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            return buf.toString();
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block  
            e.printStackTrace();
            return  null;
        }  
    }

    public static void main(String[] args) {

        //1.准备需要加密的蜜马
        String pwd = "1";
        //2 准备盐值
        String salt = StrUtils.getComplexRandomString(32);
        //2.加密  + 盐
        String pwdMd5 = MD5Utils.encrypByMd5(pwd + salt);
            // 将盐值和秘文存储到数据
        System.out.println(pwdMd5);

        //模拟登陆蜜马校验
        String pwdStr = pwd;
        String saltTem = salt;//相当于从数据库查询出来
        String pwdTmpMd5 = MD5Utils.encrypByMd5(pwdStr + saltTem);
        System.out.println("==="+pwdTmpMd5);
        if(pwdTmpMd5.equals(pwdMd5)){
            System.out.println("登陆成功");
        }else{
            System.out.println("蜜马错误");
        }

    }

}
4.短信接口

http://sms.webchinese.com.cn/Rates.shtml
用户信息修改,修改短信秘钥

导入依赖包

        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
        </dependency>

代码应用(工具类 常量类)

	public static void sendsms(String phones, String message) throws IOException {
        HttpClient client = new HttpClient();
        PostMethod post = new PostMethod("https://utf8api.smschinese.cn/");
        post.addRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8");//在头文件中设置转码
        NameValuePair[] data ={ new NameValuePair("Uid", "mingfeng"),new NameValuePair("Key", "d41d8cd98f00b204e980"),new NameValuePair("smsMob",phones),new NameValuePair("smsText",message)};
        post.setRequestBody(data);

        client.executeMethod(post);
        Header[] headers = post.getResponseHeaders();
        int statusCode = post.getStatusCode();
        System.out.println("statusCode:"+statusCode); //HTTP状态码
        for(Header h : headers){
            System.out.println(h.toString());
        }
        String result = new String(post.getResponseBodyAsString().getBytes("utf-8"));
        System.out.println(result);  //打印返回消息状态

        post.releaseConnection();
    }

发送短信验证码

前端页面准备

1.拷贝register.html到根目录,更新引用路径

2.index.html设置在新标签页打开登录界面: target=“_blank”

	<div class="menu-hd">
		<a href="#" target="_top" class="h">亲,请登录</a>
		<a href="register.html" target="_blank">免费注册</a>
	</div>

	<div class="member-logout">
		<a class="am-btn-warning btn" href="login.html">登录</a>
		<a class="am-btn-warning btn" target="_blank" href="register.html">注册</a>
	</div>

3.静态项目引入js插件vue和axios
拷贝ph-admin\node_modules里的axios和vue到ph-web/js/plugin目录里并在register.html页面引入

        <!--引入vue和axios-->
		<script src="js/plugin/vue/dist/vue.min.js"></script>
		<script src="js/plugin/axios/dist/axios.js"></script>
		<!--全局使用-->
		<script src="js/common.js"></script>
		<!-- 局部使用
		<script type="text/javascript">
            //配置axios的全局基本路径
            /*axios.defaults.baseURL='/api' 前端解决跨域*/
            axios.defaults.baseURL='http://localhost:8080/'
            //全局属性配置,在任意组件内可以使用this.$http获取axios对象
            Vue.prototype.$http = axios
		</script>-->

在js目录下新建common.js

//配置axios的全局基本路径
/*axios.defaults.baseURL='/api' 前端解决跨域*/
axios.defaults.baseURL='http://localhost:8080/'
//全局属性配置,在任意组件内可以使用this.$http获取axios对象
Vue.prototype.$http = axios

绑定div节点

<div class="am-tab-panel" id="myDiv">

写vue.js

<script type="text/javascript">
	new Vue({
		el:"#myDiv",
		mounted(){
			alert(this.$http)
		}
	})
</script>

获取的a标签换为按钮(方便置灰和显示秒数倒计时),绑定方法

<!--<a class="btn" href="javascript:void(0);" οnclick="sendMobileCode();" id="sendMobileCode">
	<span id="dyMobileButton">获取</span></a>-->
<button type="button" @click="sendMobileCode">获取</button>
		methods:{
			sendMobileCode(){
				alert(123)
			}
		},

点击获取拿到手机号

<input type="tel" v-model="phoneUserForm.phone" name="" id="phone" placeholder="请输入手机号">
<script type="text/javascript">
	new Vue({
		el:"#myDiv",
		data:{
			phoneUserForm:{
				phone:18696148335
			}
		},
		methods:{
			sendMobileCode(){
				alert(this.registerForm.phone)
			}
		},
		mounted(){
			// alert(this.$http)
		}
	})
</script>
<script type="text/javascript">
	new Vue({
		el:"#myDiv",
		data:{
			phoneUserForm:{
				phone:""
			}
		},
		methods:{
			sendMobileCode(){
				//1.判断手机号不为空
				if(!this.phoneUserForm.phone){
					alert("手机号不能为空");
					return;
				}
				//2.获取按钮 禁用按钮 发送时灰化不能使用 发送成功倒计时60才能使用 如果发送失败立即可以发送
				var sendBtn = $(event.target);
				sendBtn.attr("disabled",true);
				this.$http.post('/verifycode/smsCode',
						{"phone":this.phoneUserForm.phone}).then((res) => {
					console.log(res);
					var ajaxResult = res.data;
					if(ajaxResult.success){
						alert("手机验证码已经发送到您的手机,请在3分钟内使用");
						//3.1.发送成:倒计时
						var time = 60;
						var interval = window.setInterval( function () {
							//每一条倒计时减一
							time = time - 1 ;
							//把倒计时时间搞到按钮上
							sendBtn.html(time);
							//3.2.倒计时完成恢复按钮
							if(time <= 0){
								sendBtn.html("重发");
								sendBtn.attr("disabled",false);
								//清除定时器
								window.clearInterval(interval);
							}
						},1000);
					}else{
						//3.3.发送失败:提示,恢复按钮
						sendBtn.attr("disabled",false);
						alert("发送失败:"+ajaxResult.message);
					}
				});
			}
		},
		mounted(){
			// alert(this.$http)
		}
	})
</script>

发送短信后台

搭建用户模块

User,loginInfo
搭建完模块后记得yml配置domain和query的扫描路径(别名)

单独写公共可用的发短信接口
VerifycodeController
@RestController
@RequestMapping("/verifycode")
public class VerifycodeController {
    @Autowired
    private IVerifycodeService verifycodeService;

    /**
     * 发送短信验证码
     * @param phone  给谁发送  只是允许传入一个手机号
     * @return
     */
    @PostMapping("/smsCode")
    public AjaxResult sendSmsCode(@RequestBody String phone){
        try {
            verifycodeService.sendSmsCode(phone);
            return AjaxResult.me();
        } catch (BusinessException e) {
            return AjaxResult.me().setMessage("发送失败!"+e.getMessage());
        }catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("系统繁忙,稍后重试!!");
        }
    }
}
VerifycodeServiceImpl
		//1.校验
          //1.1手机号空校验
          //1.2不能被注册
        //2.判断是否存在验证码(从redis中获取电话对应的验证码)
        //2.1 存在
            //2.1.1 判断是否过了重发时间(1分钟)
                //2.1.1.1 如果没过重发时间。报错:请勿重复发送
                //2.1.1.2 如果过了,使用原来还没过期的验证码,刷新过期时间
        //2.2 不存在
           //2.2.1直接生成验证码
        //3 保存验证码到redis  设置过期时间
        //4 发送短信(使用工具类掉短信接口即可)
@Service
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class VerifycodeServiceImpl implements IVerifycodeService {
    @Autowired
    private IUserService userService;
    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public void sendSmsCode(String phone) {
        //1.校验
          //1.1手机号空校验
        if(!StringUtils.hasLength(phone)){
           throw new BusinessException("手机号不能为空!");
        }
          //1.2不能被注册
        User user = userService.loadUserByPhone(phone);
        if(user != null){
            throw new BusinessException("用户已经你存在!");
        }
        //2.判断是否存在验证码(从redis中获取电话对应的验证码)
        Object codeObj = redisTemplate.opsForValue().get(UserConstant.USER_VERFIY_CODE+":"+phone);
        String code = "";
        if(codeObj !=null){
            //2.1 存在
            String value =(String)codeObj;
            String timeStr = value.split(":")[1];//时间戳
            //2.1.1 判断是否过了重发时间(1分钟)
            if(System.currentTimeMillis()-Long.valueOf(timeStr) <= 1*60*1000){
                //2.1.1.1 如果没过重发时间。报错:请勿重复发送
                throw new BusinessException("请勿重复发送验证码!!!");
            }
            //2.1.1.2 如果过了,使用原来还没过期的验证码,刷新过期时间
            code = value.split(":")[0];//验证码
        }else{//2.2 不存在
            //2.2.1直接生成验证码
            code = StrUtils.getComplexRandomString(4);
        }
        //拼接存储验证码的格式
        String codeValue = code + ":" + System.currentTimeMillis();
        //3 保存验证码到redis  设置过期时间
        redisTemplate.opsForValue().set(UserConstant.USER_VERFIY_CODE+":"+phone, codeValue, 3, TimeUnit.MINUTES);
        //4 发送短信(使用工具类掉短信接口即可)
        /*SmsUtils.sendSms(phone, "您的验证码是:"+code+"; 请在3分钟内使用!");*/
        System.out.println("您的验证码是:"+code+"; 请在3分钟内使用!");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/757089.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

英语动词-分类及应用

文章目录 1.实义动词系动词情态动词 1.实义动词 实义动词是表示具体动词的词。 常见的分类&#xff1a;及物动词和不及物动词。 1.及物动词&#xff1a;transitive verb后面直接加宾语&#xff0c;并且必须加宾语。比如&#xff1a;I love you.I buy a book. 2.不及物动词&…

AI 智能对话 - ChatGLM2-6B 本地搭建入门

前情提要 这一个月来干了啥事情呢&#xff1f;AI 绘画搞了2周左右&#xff0c;SD 建筑绘图&#xff0c;训练 LORA &#xff0c;模型控制基本也上手了&#xff0c;可以按照预期生成自己想要的东西&#xff0c;那种控制感是挺开心的&#xff0c;不然你输入一句话生成 AI 图片完全…

Linux进程(三)---深入理解进程地址空间

目录 地址空间的划分及验证 所谓的地址空间是内存吗&#xff1f; 一种奇怪的现象(虚拟地址的引入) 什么是进程地址空间&#xff1f; 我们平常访问到的内存是物理内存吗&#xff1f; 深入理解区域划分 再谈奇怪的现象 fork()中为什么一个变量可以同时保存两个不同的值 …

网络安全—入职大厂经验之谈

大三想去实习&#xff0c;趁现在该干什么才能去大厂实习呢&#xff1f;想做一些事丰富一下自己的简历&#xff0c;只有打ctf&#xff1f;还是挖洞&#xff1f;非常迷茫。 或者入职转行网络安全行业应该怎么做&#xff1f;对于接下来的职业规划学习计划有什么打算&#xff1f; …

PETRv2: A Unified Framework for 3D Perception from Multi-Camera Images

PETRv2: A Unified Framework for 3D Perception from Multi-Camera Images 作者单位 旷视 目的 本文的目标是 通过扩展 PETR&#xff0c;使其有时序建模和多任务学习的能力 以此建立一个 强有力且统一的框架。 本文主要贡献&#xff1a; 将 位置 embedding 转换到 时序表…

漏洞复现 || Bitrix cms文件上传

免责声明 技术文章仅供参考&#xff0c;任何个人和组织使用网络应当遵守宪法法律&#xff0c;遵守公共秩序&#xff0c;尊重社会公德&#xff0c;不得利用网络从事危害国家安全、荣誉和利益&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此…

Go语言之流程控制语句,for循环

程序是由语句构成&#xff0c;而流程控制语句 是用来控制程序中每条语句执行顺序的语句。可以通过控制语句实现更丰富的逻辑以及更强大的功能。几乎所有编程语言都有流程控制语句&#xff0c;功能也都基本相似。 其流程控制方式有 顺序结构,分支结构,循环结构 1、switch比if el…

javaWeb之文件上传和下载

文件上传下载(场景): * 文件上传 * 客户端 * 文件上传页面(form) * 请求方式一定是POST. * 文件上传域(<input typefile>)必须具有name属性. * 表单的enctype属性值设置为"multipart/form-data". * 扩展:浏览器内核产品不同(不建…

剖析C语言字符串函数(超全)

目录 前言&#xff1a; 一、strlen函数 功能&#xff1a; 参数和返回值&#xff1a; 注意事项&#xff1a; 返回值是无符号的易错点&#xff1a; strlen函数的模拟实现 1、计数器算法 2、递归算法 3、指针减去指针 二、strcpy函数 功能&#xff1a; 参数和返回值 …

git使用代码

git init //生成一个.git的子目录&#xff0c;产生一个仓库。 git status //查看当前目录下所有文件的状态。 git aad . //将该目录下所有的文件提交到暂存区 git add文件名/将该目录下指定的文件提交到暂存区 git commit -m v1.0//将暂存区的文件提交到版本库 git log //…

网络协议与攻击模拟-21-HTTP协议

HTTP 协议 1、 HTTP 协议结构 2、在 Windows server 去搭建 web 服务器 3、分析 HTTP 协议流量 一、 HTTP 协议 1、概念 HTTP &#xff08;超文本传输协议&#xff09;是用于在万维网服务器上传输超文本&#xff08; HTML &#xff09;到本地浏览器的传输协议 属于 TCP / …

树与图的(深度 + 广度)优先遍历

目录 一、树与图的存储1.树的特性2.图的分类3.有向图的储存结构 二、树与图的深度优先遍历的运用树的重心题意分析代码实现 三、树与图的广度优先遍历的运用图中点的层次题意分析代码实现 一、树与图的存储 1.树的特性 树是一种特殊的图,具有以下两个重要特性: 无环 树是一个…

Redis数据类型 — Set

目录 Set内部实现 源码片段 Set 类型是一个无序并唯一的键值集合&#xff0c;它的存储顺序不会按照插入的先后顺序进行存储。一个集合最多可以存储 2^32-1 个元素。 Set 类型除了支持集合内的增删改查&#xff0c;同时还支持多个集合取交集、并集、差集。Set 的差集、并集和…

Bean 的作用域和生命周期

目录 一、 Bean 的作用域 1. 安装Lombok插件 1.1 Lombok 简介 1.2 Lombok 安装 2. 创建一个 User 对象&#xff0c;然后将 User 对象 存储到 Spring 容器中 2.1 创建User 对象 2.2 将User 对象存储到 Spring 中 2.3 修改 User 对象中的属性&#xff0c;然后看结果&#…

概率论的学习和整理--番外12:2个概率选择比较的题目

目录 1 题目 2 结论 3 算法 3.1 错误算法 3.2 算法1&#xff0c;用期望的方式解方式 3.3 算法2&#xff0c;直接解方程 3.4 算法3&#xff0c;用递归--等比数列求和来算 4 上述比较的意义-回到问题本身 1 题目 题目 3个A合成1个B 方案1&#xff1a;1/4 几率返还一个A…

【ONE·Linux || 地址空间与进程控制(二)】

总言 进程地址空间和进程控制相关介绍。 文章目录 总言2、进程控制续2.3、进程等待2.3.1、为什么需要进程等待2.3.2、阻塞式等待2.3.2.1、使用wait2.3.2.2、使用waitpid2.3.2.3、参数status基本介绍 2.3.3、一些细节与问题</font>2.3.3.1、进程独立性说明2.3.3.1、父进程…

【网络安全带你练爬虫-100练】第13练:文件的创建、写入

目录 目标&#xff1a;将数据写入到文件中 网络安全O 目标&#xff1a;将数据写入到文件中 开干 &#xff08;始于颜值&#xff09;打开一个&#xff0c;没有就会创建 with open(data.csv, modew, newline) as file: &#xff08;忠于才华&#xff09;开始写入数据 writer cs…

LinuxC/C++开发工具——make/makefile和gdb

linux开发工具 前言Linux项目自动化构建工具&#xff08;make/makefile&#xff09;makefile文件的组成如何使用make.PHONY关键字 项目清理 gdb调试器背景使用list&#xff08;l&#xff09;调试命令break&#xff08;b&#xff09;&#xff1a;设置断点info break&#xff1a;…

[STL] vector 模拟实现详解

目录 一&#xff0c;准备工作 二&#xff0c;push_back 1&#xff0c; 关于引用 2. 参数const 的修饰 补充 三&#xff0c;迭代器实现 四&#xff0c;Pop_back 五&#xff0c;insert 1. 补充——迭代器失效 六&#xff0c; erase 七&#xff0c;构造函数 1. 迭代…