SpringBoot——短信发送、手机验证码登录

目录

一、短信发送

1.1 阿里云短信服务

1.1.1 设置短信签名

1.1.2 模板管理

1.1.3 设置AccessKey

1.2 短信发送——代码开发

1.2.1 导入maven坐标

1.2.2 调用API

1.2  手机验证码登录

1.2.1 用户数据库表

1.2.2  修改过滤器

1.2.3   随机生成验证码的工具类

1.2.4 手机验证码登录-- 发送验证码

1.2.5 手机验证码登录-- 验证验证码


一、短信发送

1.1 阿里云短信服务

短信服务_企业短信营销推广_验证码通知-阿里云

也可以在下面这个地方查看短信服务 

1.1.1 设置短信签名

短信签名就是短信发送者的署名,表示发送方的身份

1.1.2 模板管理

 申请下来之后可以点击详情进行查看

     其中模板CODE是自动生成的,不用管,重点是模板的内容

1.1.3 设置AccessKey

 

创建新的用户

勾选上之后,我们在编程代码中就能使用

 

当我们创建用户成功后,就生成了一对AccessKey,即AccessKey ID(用户名) 与AccessKey Secret(密码)

很多人在这里的时候不小心没截图或者没保存就关了,丢失了AccessKey,但是不要紧,还可以再次创建

还有就是如果别人知道了我们的AccessKey,那别人使用的时候会就花我们的钱。我们也可以把泄露的AccessKey禁用

之后再新增授权。这次授权的意思就是仅仅授予有关短信服务的,即是我们泄露了,别人也只能操作短信服务,对我们的影响很小。

1.2 短信发送——代码开发

参照官方文档即可

新手指引_短信服务-阿里云帮助中心

1.2.1 导入maven坐标

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.16</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>2.1.0</version>
        </dependency>

1.2.2 调用API


/**
 * 短信发送工具类
 */
public class SMSUtils {

	/**
	 * 发送短信
	 * @param signName 签名
	 * @param templateCode 模板
	 * @param phoneNumbers 手机号
	 * @param param 参数
	 */
	public static void sendMessage(String signName, String templateCode,String phoneNumbers,String param){
		DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "LTAI5tHRxs2FeCu5JcJTGbm2", "v0H4PaJpXSwNr6XChtlVYAgmQWgKRA");

		IAcsClient client = new DefaultAcsClient(profile);

		SendSmsRequest request = new SendSmsRequest();

		request.setSysRegionId("cn-hangzhou");
//	    要发送给那个人的电话号码
		request.setPhoneNumbers(phoneNumbers);
//      我们在阿里云设置的签名
		request.setSignName(signName);
//	    我们在阿里云设置的模板
		request.setTemplateCode(templateCode);
//	    在设置模板的时候有一个占位符
		request.setTemplateParam("{\"code\":\""+param+"\"}");

//		request.setPhoneNumbers("1368846****");//接收短信的手机号码
//		request.setSignName("阿里云");//短信签名名称
//		request.setTemplateCode("SMS_20933****");//短信模板CODE
//		request.setTemplateParam("张三");//短信模板变量对应的实际值

		try {
			SendSmsResponse response = client.getAcsResponse(request);
			System.out.println("短信发送成功");
		}catch (ClientException e) {
			e.printStackTrace();
		}
	}

}

1.2  手机验证码登录

  • 方便、快捷、无需注册、直接登陆
  • 使用短信验证码作为登录凭证,无序记忆密码
  • 安全

登录流程:  输入手机号  ->  获取验证码  ->  输入验证码  ->  点击登录  ->  登陆成功

注意: 通过手机验证码登录,手机号是区分不同用户的标识

1.2.1 用户数据库表

因为是通过手机和验证码登录的,所以没有用户名和密码字段

1.2.2  修改过滤器

 在写代码之前记得要在过滤器中定义不需要处理的请求路径/user/sendMsg和/user/login

 然后访问路径: http://localhost:8080/front/page/login.html


/**
 * 检查用户是否已经完成登录
 * 过滤器与拦截器的区别:Filter对所有访问进行增强(在Tomcat服务器进行配置),Interceptor仅针对SpringMVC的访问进行增强
 */
@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")  //urlPatterns指定拦截哪些路径
public class LoginCheckFilter implements Filter {

    //  此对象的作用:路径匹配器,  匹配路径时支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//      servletRequest向下强制类型转换
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;


        //1. 获取本次请求的URI( URI:请求的资源路径)
        String requestURI = request.getRequestURI();

        log.info("拦截到请求:{}", request.getRequestURI());
        // 定义不用处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**",
                "/user/sendMsg",
                "/user/login"
        };

        //2. 判断本次请求是否需要处理(因为有些请求并不需要用户登录)
        boolean check = check(requestURI, urls);

        //3.如果不需要处理,则直接放行
        if (check) {
            log.info("本次请求{}不需要处理", request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }

        //4-1.判断登录状态,如果已登录,则直接放行.从session中获取用户,如果获取到说明已经登录
        if (request.getSession().getAttribute("employee") != null) {
            log.info("用户已登录,用户id为{}", request.getSession().getAttribute("employee"));

            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);

            filterChain.doFilter(request, response);
            return;
        }

        //4-2.判断登录状态,如果已登录,则直接放行.从session中获取用户,如果获取到说明已经登录
        if (request.getSession().getAttribute("user") != null) {
            log.info("用户已登录,用户id为{}", request.getSession().getAttribute("user"));

            Long userId = (Long) request.getSession().getAttribute("user");
            BaseContext.setCurrentId(userId);

            filterChain.doFilter(request, response);
            return;
        }

        //5.如果未登录则返回未登录结果
        log.info("资源路径路径:{},用户未登录{}", request.getRequestURI(), request.getSession().getAttribute("employee"));
//           通过输出流的方式向客户端响应数据   (为什么要返回这个NOTLOGIN?  因为前端需要这个来进行判定是否登录)
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
//        filterChain.doFilter(request, response);  加上这个就无法实现

    }

    /**
     * 检查本次请求是否需要放行
     *
     * @param requestURI 请求的资源路径
     * @param urls       放过的路径
     * @return true 放行
     */
    public boolean check(String requestURI, String[] urls) {
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) {
//           放行
                return true;
            }
        }
        return false;
    }
}

1.2.3   随机生成验证码的工具类

/**
 * 随机生成验证码工具类
 */
public class ValidateCodeUtils {
    /**
     * 随机生成验证码
     * @param length 长度为4位或者6位
     * @return
     */
    public static Integer generateValidateCode(int length){
        Integer code =null;

//      长度为4
        if(length == 4){
            code = new Random().nextInt(9999);//生成随机数,最大为9999
            if(code < 1000){
                code = code + 1000;//保证随机数为4位数字
            }

//      长度为6
        }else if(length == 6){
            code = new Random().nextInt(999999);//生成随机数,最大为999999
            if(code < 100000){
                code = code + 100000;//保证随机数为6位数字
            }
//       其他情况
        }else{
            throw new RuntimeException("只能生成4位或6位数字验证码");
        }
        return code;
    }

    /**
     * 随机生成指定长度字符串验证码
     * @param length 长度
     * @return
     */
    public static String generateValidateCode4String(int length){
        Random rdm = new Random();
        String hash1 = Integer.toHexString(rdm.nextInt());
        String capstr = hash1.substring(0, length);
        return capstr;
    }
}

1.2.4 手机验证码登录-- 发送验证码

两次ajax请求:

   1. 登录页面输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信

   2. 在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求

    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user, HttpSession session){
//      1.获取手机号
        String phone = user.getPhone();

        if(StringUtils.isEmpty(phone)){
            return R.error("短信发送失败");
        }
//      2.随机生成四位验证码
        String code = ValidateCodeUtils.generateValidateCode(4).toString();

//      3.调用阿里云提供的短信服务
        SMSUtils.sendMessage("张靖奇","",phone,code);

//      4.需要将生成的验证码保存到session中
         session.setAttribute(phone,code);

        return R.success("验证码短信发送成功");
    }

1.2.5 手机验证码登录-- 验证验证码

    //  其实传过来的phone:xxxx,code:xxx 也可以用map集合接收
    @PostMapping("/login")
    public R<User> login(@RequestBody Map map, HttpSession session) {
        log.info(map.toString());

//      1. 获取手机号
        String phone = map.get("phone").toString();

//      2. 获取验证码
        String code = map.get("code").toString();

//      3. 从Session中获取保存的验证码
        Object codeInSession = session.getAttribute(phone);

//      4. 进行验证码比对(页面提交的验证码和Session中保存的验证码比对)
        if (codeInSession != null && codeInSession.equals(code)) {
//          5.对比成功,说明登录成功

            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone,phone);
            User user = userService.getOne(queryWrapper);
            if (user==null){
//          6. 如果新用户,自动注册
                user = new User();
                user.setPhone(phone);
                user.setStatus(1);
               userService.save(user);
            }
            session.setAttribute("user",user.getId());
            return R.success(user);
        }

        return R.error("登录失败");
    }

### Spring Boot 实现发送短信验证码 #### 创建发送短信的服务类 为了实现发送短信验证码,在Spring Boot项目中可以创建一个新的Java类用于处理发送逻辑。此服务类负责生成随机验证码并调用第三方API完成实际的消息传递操作[^1]。 ```java @Service public class SmsService { @Value("${aliyun.sms.templateCode}") private String templateCode; public void sendSms(String phoneNumber) { // Generate a random verification code (e.g., 6 digits) Random r = new Random(); StringBuilder sb = new StringBuilder(6); while(sb.length() < 6){ sb.append(r.nextInt(10)); } String verifyCode = sb.toString(); // Store the generated code into Redis with expiration time set to two minutes. redisTemplate.opsForValue().set(phoneNumber, verifyCode, 2, TimeUnit.MINUTES); // Call AliCloud SMS API here using the provided phone number and template code... System.out.println("Sending SMS: " + phoneNumber + ", Code:" + verifyCode); // For demonstration purposes only // Actual implementation would involve calling an external service like Alibaba Cloud's SDK or RESTful APIs. } } ``` 该`sendSms()`方法接受电话号码作为输入参数,生成六位数长度的随机字符串作为验证代码,并将其存储在Redis缓存中设置过期时间为两分钟。之后通过调用外部SMS网关(此处假设为阿里云),将消息发送给指定用户[^4]。 #### 注册 Bean 和配置文件 为了让上述组件能够被自动装配进应用程序上下文中,需确保其标记了适当注解(如上面例子中的 `@Service`)。另外还需要定义相应的属性值来填充这些bean实例所需的变量,比如阿里云短信模板ID等信息: ```yaml aliyun: sms: accessKeyId: YOUR_ACCESS_KEY_ID secretAccessKey: YOUR_SECRET_ACCESS_KEY signName: YourSignName templateCode: TEMPLATE_CODE ``` 以上YAML片段展示了部分可能需要配置的关键项;具体取决于所使用的SDK版本以及个人账户详情。 #### 构建控制器暴露HTTP端点 最后一步是在Web层面上构建REST Controller以公开对外接口供前端或其他微服务调用。这里展示了一个简单的GET请求处理器样例,它会触发一次新的验证码发送过程[^3]。 ```java @RestController @RequestMapping("/api/v1/sms") public class SmsController { @Autowired private SmsService smsService; @GetMapping("/{phoneNumber}") public ResponseEntity<?> sendVerificationCode(@PathVariable String phoneNumber) throws Exception{ try { Pattern pattern = Pattern.compile("^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$"); Matcher matcher = pattern.matcher(phoneNumber); if (!matcher.matches()) throw new IllegalArgumentException("Invalid Phone Number"); smsService.sendSms(phoneNumber); return ResponseEntity.ok(Map.of("message", "Verification code sent successfully")); } catch(IllegalArgumentException e){ return ResponseEntity.badRequest().body(e.getMessage()); } } } ``` 这段代码实现了基本的身份验证流程控制——首先检查传入手机号是否合法,接着委托给之前提到过的业务逻辑去执行具体的动作。如果一切顺利,则返回成功响应;反之则给出相应提示说明失败原因。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱布朗熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值