美文网首页
手机号码+手机验证码实现注册功能

手机号码+手机验证码实现注册功能

作者: 鹿白_tz | 来源:发表于2020-02-09 21:23 被阅读0次

<meta charset="utf-8">

1.注册功能后端实现

(1)注册用户数据库表与登录数据库表一致【t_user】

(2)手机验证码,需要借助阿里云服务器实现(也可以通过别的方式,现用阿里云)

(3)添加依赖pom.xml

 <!-- 阿里云OSS依赖 -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.8.3</version>
        </dependency>
        <!-- 阿里巴巴 druid数据源依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
        <!-- 阿里云短信依赖 -->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.0.3</version>
        </dependency>
        <!-- Redis 缓存依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <!-- 1.5的版本默认采用的连接池技术是jedis,2.0以上版本默认连接池是lettuce, 因为此次是采用jedis,所以需要排除lettuce的jar -->
            <exclusions>
                <exclusion>
                    <groupId>redis.clients</groupId>
                    <artifactId>jedis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- jedis客户端 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <!-- spring2.X集成redis所需common-pool2,使用jedis必须依赖它-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.5.0</version>
        </dependency>
    </dependencies>

(4)借助第三方工具redis可以对比验证码时效时间,判断验证码是否超时错误或者失效,此处redis工具需自己安装配置

application.properties

#redis配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制) 2.0区别 1.0+使用的是 spring.redis.pool.max-acitive
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 2.0区别
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接 2.0区别
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接 2.0区别
spring.redis.jedis.pool.min-idle=0

(5)util工具

1.SmsUtil

package com.scs.soft.zhihu.api.util;

import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;

/**
 * @ClassName SMSUtil
 * @Description 短信工具类
 * @Author
 * @Date 2019/12/2
 **/
public class SmsUtil {
    public static String send(String mobile) {
        DefaultProfile profile = DefaultProfile.getProfile(
                "******************",
                "*************************",
                "***********************");
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("SendSms");
        request.putQueryParameter("RegionId", "cn-hangzhou");
        request.putQueryParameter("PhoneNumbers", mobile);
        request.putQueryParameter("SignName", "************************");
        request.putQueryParameter("TemplateCode", "**************");
        String verifyCode = StringUtil.getVerifyCode();
        request.putQueryParameter("TemplateParam", "{\"code\":" + verifyCode + "}");
        try {
            CommonResponse response = client.getCommonResponse(request);
            System.out.println(response.getData());
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return verifyCode;
    }

    public static void main(String[] args) {
        System.out.println(send("************************"));
    }
}

2.VerifyUtil

package com.scs.soft.zhihu.api.util;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Random;

/**
 * @ClassName VerifyUtil
 * @Description TODO
 * @Date 2019/12/3
 **/
public class VerifyUtil {
    public static final String VERIFY_CODES = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijklimopqrstuvwxyz";
    private static Random random = new Random();


    /**
     * 使用系统默认字符源生成验证码
     * @param verifySize    验证码长度
     * @return
     */
    public static String generateVerifyCode(int verifySize){
        return generateVerifyCode(verifySize, VERIFY_CODES);
    }
    /**
     * 使用指定源生成验证码
     * @param verifySize    验证码长度
     * @param sources   验证码字符源
     * @return
     */
    public static String generateVerifyCode(int verifySize, String sources){
        if(sources == null || sources.length() == 0){
            sources = VERIFY_CODES;
        }
        int codesLen = sources.length();
        Random rand = new Random(System.currentTimeMillis());
        StringBuilder verifyCode = new StringBuilder(verifySize);
        for(int i = 0; i < verifySize; i++){
            verifyCode.append(sources.charAt(rand.nextInt(codesLen-1)));
        }
        return verifyCode.toString();
    }

    /**
     * 生成随机验证码文件,并返回验证码值
     * @param w
     * @param h
     * @param outputFile
     * @param verifySize
     * @return
     * @throws IOException
     */
    public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
        String verifyCode = generateVerifyCode(verifySize);
        outputImage(w, h, outputFile, verifyCode);
        return verifyCode;
    }

    /**
     * 输出随机验证码图片流,并返回验证码值
     * @param w
     * @param h
     * @param os
     * @param verifySize
     * @return
     * @throws IOException
     */
    public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException{
        String verifyCode = generateVerifyCode(verifySize);
        outputImage(w, h, os, verifyCode);
        return verifyCode;
    }

    /**
     * 生成指定验证码图像文件
     * @param w
     * @param h
     * @param outputFile
     * @param code
     * @throws IOException
     */
    public static void outputImage(int w, int h, File outputFile, String code) throws IOException{
        if(outputFile == null){
            return;
        }
        File dir = outputFile.getParentFile();
        if(!dir.exists()){
            dir.mkdirs();
        }
        try{
            outputFile.createNewFile();
            FileOutputStream fos = new FileOutputStream(outputFile);
            outputImage(w, h, fos, code);
            fos.close();
        } catch(IOException e){
            throw e;
        }
    }

    /**
     * 输出指定验证码图片流
     * @param w
     * @param h
     * @param os
     * @param code
     * @throws IOException
     */
    public static void outputImage(int w, int h, OutputStream os, String code) throws IOException{
        int verifySize = code.length();
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Random rand = new Random();
        Graphics2D g2 = image.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        Color[] colors = new Color[5];
        Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN,
                Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
                Color.PINK, Color.YELLOW };
        float[] fractions = new float[colors.length];
        for(int i = 0; i < colors.length; i++){
            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
            fractions[i] = rand.nextFloat();
        }
        Arrays.sort(fractions);

        g2.setColor(Color.GRAY);// 设置边框色
        g2.fillRect(0, 0, w, h);

        Color c = getRandColor(200, 250);
        g2.setColor(c);// 设置背景色
        g2.fillRect(0, 2, w, h-4);

        //绘制干扰线
        Random random = new Random();
        /**
         * 设置线条的颜色
         */
        g2.setColor(getRandColor(160, 200));
        for (int i = 0; i < 20; i++) {
            int x = random.nextInt(w - 1);
            int y = random.nextInt(h - 1);
            int xl = random.nextInt(6) + 1;
            int yl = random.nextInt(12) + 1;
            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
        }


        // 添加噪点
        float yawpRate = 0.05f;// 噪声率
        int area = (int) (yawpRate * w * h);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(w);
            int y = random.nextInt(h);
            int rgb = getRandomIntColor();
            image.setRGB(x, y, rgb);
        }
/***
 * 使图片扭曲
 */
        shear(g2, w, h, c);
        g2.setColor(getRandColor(100, 160));
        int fontSize = h-4;
        Font font = new Font("Algerian", Font.ITALIC, fontSize);
        g2.setFont(font);
        char[] chars = code.toCharArray();
        for(int i = 0; i < verifySize; i++){
            AffineTransform affine = new AffineTransform();
            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize/2, h/2);
            g2.setTransform(affine);
            g2.drawChars(chars, i, 1, ((w-10) / verifySize) * i + 5, h/2 + fontSize/2 - 10);
        }

        g2.dispose();
        ImageIO.write(image, "jpg", os);
    }

    private static Color getRandColor(int fc, int bc) {
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    private static int getRandomIntColor() {
        int[] rgb = getRandomRgb();
        int color = 0;
        for (int c : rgb) {
            color = color << 8;
            color = color | c;
        }
        return color;
    }

    private static int[] getRandomRgb() {
        int[] rgb = new int[3];
        for (int i = 0; i < 3; i++) {
            rgb[i] = random.nextInt(255);
        }
        return rgb;
    }

    private static void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private static void shearX(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = random.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(0, i, w1, 1, (int) d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int) d, i, 0, i);
                g.drawLine((int) d + w1, i, w1, i);
            }
        }

    }

    private static void shearY(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(40) + 10; // 50;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(i, 0, 1, h1, 0, (int) d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int) d, i, 0);
                g.drawLine(i, (int) d + h1, i, h1);
            }

        }

    }
}

3.StringUtil

package com.scs.soft.zhihu.api.util;

import java.util.Random;

/**
 * @ClassName StringUtil
 * @Description TODO
 * @Date 2019/12/2
 **/
public class StringUtil {
    private final static int LENGTH = 6;
    /**
     * 获取六位随机数短信验证码
     *
     * @return
     */
    public static String getVerifyCode() {
        Random random = new Random();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            stringBuilder.append(String.valueOf(random.nextInt(10)));
        }
        return stringBuilder.toString();
    }
    /**
     * 获取六位随机数字、字母字符串序列
     *
     * @return String
     */
    public static String getRandomString() {
        StringBuilder stringBuilder = new StringBuilder();
        Random random = new Random();
        int index;
        //生成四位随机字符
        for (int i = 0; i < LENGTH; i++) {
            //随机生成0、1、2三个整数,代表数字字符、大写字母、小写字母,保证验证码的组成比较正态随机
            index = random.nextInt(3);
            //调用本类封装的私有方法,根据编号获得对应的字符
            char result = getChar(index);
            //追加到可变长字符串
            stringBuilder.append(result);
        }
        return stringBuilder.toString();
    }

    private static char getChar(int item) {
        //数字字符范围
        int digitalBound = 10;
        //字符范围
        int charBound = 26;
        Random random = new Random();
        int index;
        char c;
        //根据调用时候的三个选项,生成数字、大写字母、小写字母三种不同的字符
        if (item == 0) {
            index = random.nextInt(digitalBound);
            c = (char) ('0' + index);
        } else if (item == 1) {
            index = random.nextInt(charBound);
            c = (char) ('A' + index);
        } else {
            index = random.nextInt(charBound);
            c = (char) ('a' + index);
        }
        return c;
    }
}

(6)

  • 封装统一的响应体
  • 调用 ResponseResult.success() 或 ResponseResult.success(Object data),
  • 不需要返回数据时调用前者, 需要返回数据时调用后者

(7)controller实现

 /**
     * 注册
     * @param userDto
     * @return
     */
        @PostMapping(value = "/signup")
    Result signUp(@RequestBody UserDto userDto) {
        return userService.sign(userDto);
    }

    /**
     * 得到验证码
     */
    @PostMapping(value = "/sms")
    Result getSms(@RequestBody UserDto userDto) {
        return userService.sendSms(userDto);
    }
    /**
     *  检查验证码是否正确
     */
    @PostMapping(value = "/sms/check")
    Result checkSms(@RequestBody UserDto userDto) {
        return userService.checkSms(userDto);
    }
(8)service接口实现(补充)
  /**
     * 注册
     *
     * @param userDto
     * @return
     */
    @Override
    public Result sign(UserDto userDto) {
        //调用验证功能
        Result result = userService.checkSms(userDto);
        //验证通过
        if (result.getCode() == 1) {
            String mobile = userDto.getMobile();
            User user;
            try {
                user = userMapper.findUserByMobile(mobile);
            } catch (SQLException e) {
                logger.error("根据手机号查询用户出现异常");
                return Result.failure(ResultCode.USER_SIGN_UP_FAIL);
            }
            //用户手机号存在,注册失败
            if (user != null) {
                return Result.failure(ResultCode.USER_HAS_EXISTED);
            } else {
                User saveUser=new User();
                saveUser.setMobile(mobile);
                saveUser.setNickname("用户"+mobile);
                saveUser.setIntroduction("你和冬天一起来了");
                saveUser.setAvatar("https://niit-soft.oss-cn-hangzhou.aliyuncs.com/avatar/default.png");
                try {
                    userMapper.insert(saveUser);
                } catch (SQLException e) {
                    logger.error("新增用户出现异常");
                    return Result.failure(ResultCode.USER_SIGN_UP_FAIL);
                }
                return  Result.success(saveUser);
            }
        }
        return  Result.failure(ResultCode.USER_VERIFY_CODE_ERROR);
    }
    @Override
    public Result sendSms(UserDto userDto) {
        String mobile = userDto.getMobile();
        DefaultProfile profile = DefaultProfile.getProfile(
                "*****************",
                "*****************************",
                "******************************");
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("SendSms");
        request.putQueryParameter("RegionId", "cn-hangzhou");
        request.putQueryParameter("PhoneNumbers", mobile);
        request.putQueryParameter("SignName", "************************");
        request.putQueryParameter("TemplateCode", "******************");
        String verifyCode = StringUtil.getVerifyCode();
        request.putQueryParameter("TemplateParam", "{\"code\":" + verifyCode + "}");
        CommonResponse response = null;
        try {
            response = client.getCommonResponse(request);
        } catch (ClientException e) {
            log.error("短信发送异常");
            return Result.failure(ResultCode.SMS_ERROR);
        } catch (com.aliyuncs.exceptions.ClientException e) {
            e.printStackTrace();
        }
        //resData样例:{"Message":"OK","RequestId":"0F3A84A6-55CA-4984-962D-F6F54281303E","BizId":"300504175696737408^0","Code":"OK"}
        String resData = response.getData();
        //将返回的JSON字符串转成JSON对象
        JSONObject jsonObject = JSONObject.parseObject(resData);
        if ("OK".equals(jsonObject.get("Code"))) {
            System.out.println(verifyCode);
            //存入redis,3分钟有效
            redisService.set(mobile, verifyCode, 3L);
            return Result.success(verifyCode);
        } else {
            return Result.failure(ResultCode.SMS_ERROR);
        }
    }

    @Override
    public Result checkSms(UserDto userDto) {
        String mobile = userDto.getMobile();
        String sms = userDto.getCode();
        System.out.println(sms);
        String correctSms = redisService.getValue(mobile, String.class);
        if (correctSms != null) {
            //将客户端传来的短信验证码和redis取出的短信验证码比对
            if (correctSms.equals(sms)) {
                return Result.success();
            } else {
                //验证码错误
                Result.failure(ResultCode.USER_VERIFY_CODE_ERROR);
            }
        }
        //验证码失效
        return Result.failure(ResultCode.USER_CODE_TIMEOUT);
}

(9)测试

2.前端代码部分展示

(1)界面

    <!-- 免密登陆 -->
            <div class="sign-box-tz" v-show="signUpistrue == true">
                <div class="singFlow-account">
                    <div class="account-left link"><input style="color: #8590A6;" placeholder="中国 +86"></input></div>
                    <svg class="Zi Zi--Select Select-arrow" style="color: #8590A6;" viewBox="0 0 24 24" width="24" height="22">
                        <path
                            d="M12 16.183l2.716-2.966a.757.757 0 0 1 1.064.001.738.738 0 0 1 0 1.052l-3.247 3.512a.758.758 0 0 1-1.064 0L8.22 14.27a.738.738 0 0 1 0-1.052.758.758 0 0 1 1.063 0L12 16.183zm0-9.365L9.284 9.782a.758.758 0 0 1-1.064 0 .738.738 0 0 1 0-1.052l3.248-3.512a.758.758 0 0 1 1.065 0L15.78 8.73a.738.738 0 0 1 0 1.052.757.757 0 0 1-1.063.001L12 6.818z"
                            fill-rule="evenodd"
                        ></path>
                    </svg>
                    <span><p style="color: #8590A6;">|</p></span>
                    <div class="account-right"><input style="color: #8590A6;" type="text" placeholder="手机号" v-model="userDto.mobile" ></input></div>
                </div>
                <div class="singFlow-Input">
                    <input style="color: #8590A6;" type="text" placeholder="输入6位数的短信验证" v-model="userDto.code" ></input>
                    <button class="xx link" style="background-color: white;" @click="getsms">获取短信验证码</button>
                </div>
                <div class="login-options"><input class="tz-text-1" placeholder="接受语收验证码"></input></div>
                <div class="login-btn"><button class="btn link" @click="signIn()">注册/登录</button></div>
                <div class="singFlow-tip d-inline-block">
                    <div class="tip-left">
                        <p style="margin-left: -42%;color: #8590A6;">未注册手机验证后自动登录</p>
                        <p style="color: #8590A6;">注册即代表同意《知乎协议》《隐私保护指引》</p>
                    </div>
                    <a href="https://www.zhihu.com/org/signup" class="sign">注册机构号</a>
                </div>
            </div>

(2)methods方法实现

image.png

相关文章

网友评论

      本文标题:手机号码+手机验证码实现注册功能

      本文链接:https://www.haomeiwen.com/subject/lxonxhtx.html