当前位置:网站首页>21:第三章:开发通行证服务:4:进一步完善【发送短信,接口】;(在【发送短信,接口】中,调用阿里云短信服务和redis服务;一种设计思想:BaseController;)

21:第三章:开发通行证服务:4:进一步完善【发送短信,接口】;(在【发送短信,接口】中,调用阿里云短信服务和redis服务;一种设计思想:BaseController;)

2022-06-27 10:27:00 小枯林

说明:

(1)声明:

          ● 这个项目,前端自己就不部署了;;;自己就使用浏览器或者postman测试接口了;;;但是,对于每个接口,在前端页面上的表现,自己都会阐述清楚;;;同时,如果遇到一些“必须在前端页面上才能说清楚的事”,自己也都会阐述清楚来龙去脉;

          ● 最重要的是要理清思路;

          ● 由于阿里云短信服务,限制同一个手机号在一天内最多发送10条短信;;;所以,在已经确保阿里云短信服务已经调通的情况下,在开发阶段测试的时候,自己一般会把调用阿里云短信服务的那条语句给注释掉;(短信有限,且发且珍惜~~)

目录

零:本篇博客的合理性解释;

1.在【19:第三章:开发通行证服务:2:在程序中,打通阿里云短信服务;】,我们在程序中打通了阿里云短信服务;

2.在【20:第三章:开发通行证服务:3:在程序中,打通redis服务器;】中,我们在程序中打通了redis服务器;

3.本篇博客,就是完善【imooc-news-dev-user】这个用户微服务中的,【发送短信,接口】getSMSCode();

一:完善【imooc-news-dev-user】(这个用户微服务中)的【发送短信,接口】getSMSCode();

1.首先,完善【发送短信,接口】的定义;(即完善这个接口,在【imooc-news-dev-api】接口工程中的定义)

2.然后,完善【imooc-news-dev-user】(这个用户微服务中)的【短信验证码接口getSMSCode()】的实现;

二:效果;


零:本篇博客的合理性解释;

1.在【19:第三章:开发通行证服务:2:在程序中,打通阿里云短信服务;】,我们在程序中打通了阿里云短信服务;

(1)首先,我们在【imooc-news-dev-common】通用工程中,编写一个【调用阿里云短信服务,发送短信】的工具类:SMSUtils;;;自然,其他需要使用阿里云短信服务的地方(比如【imooc-news-dev-user】这个用户微服务)就可以调用这个工具类;

(2)然后,在【imooc-news-dev-user】这个需要调用阿里云短信服务的用户微服务中,我们编写【发送短信,接口】,去调用SMSUtils工具类;

          ● 首先,在【imooc-news-dev-api】接口工程中,定义了一个【发送短信,接口】getSMSCode();

          ● 然后,在【imooc-news-dev-user】这个用户微服务中,去实现这个接口; 

(3)当时主要目的是测试阿里云短信服务是否OK,所以这个接口写的很简单,并没有严格按照“前后端约定”去写;

2.在【20:第三章:开发通行证服务:3:在程序中,打通redis服务器;】中,我们在程序中打通了redis服务器;

(1)首先,我们在【imooc-news-dev-common】通用工程中,编写一个【操作redis】的工具类:RedisOperator;;;这个类中,我们自己编写了很多操作redis的方法;;;自然,其他需要redis服务的地方(比如【imooc-news-dev-user】这个用户微服务)就可以调用这个工具类;

(2)然后,在【imooc-news-dev-user】这个需要使用redis的用户微服务中,我们在配置文件中,配置redis服务器的url、用户名、密码等内容;

(3)然后,在【imooc-news-dev-user】这个需要使用redis的用户微服务中,我们在测试用的HelloController中,编写了一个接口,去实测redis是否OK;

(4)当时主要目的是测试我们的程序是否可以连上redis,所以我们在一个纯测试的接口中,测试了一下;

3.本篇博客,就是完善【imooc-news-dev-user】这个用户微服务中的,【发送短信,接口】getSMSCode();

让这个短信接口,完善在调用阿里云短信服务的逻辑,同时加入调用redis服务的逻辑;


一:完善【imooc-news-dev-user】(这个用户微服务中)的【发送短信,接口】getSMSCode();

1.首先,完善【发送短信,接口】的定义;(即完善这个接口,在【imooc-news-dev-api】接口工程中的定义)

package com.imooc.api.controller.user;


import com.imooc.grace.result.GraceJSONResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;

@Api(value = "用户注册登录",tags = {"用户注册登录的controller"})
@RequestMapping("passport")
public interface PassportControllerApi {

    /**
     * 获取短信验证码
     * @param mobile:调用这个接口的时候,需要传手机号;
     * @param request:我们这儿注入HttpServletRequest,通过其可以获取用户ip;
     * @return
     */
    @ApiOperation(value = "获得短信验证码", notes = "获得短信验证码", httpMethod = "GET")
    //前端那边的请求接口已经是“getSMSCode”了,所以自己在写后端接口的url的时候,别瞎写;
    @GetMapping("/getSMSCode")
    public GraceJSONResult getSMSCode(@RequestParam("mobile") String mobile, HttpServletRequest request);
}

说明:

(1)修改内容说明;

(2)因为在实际中,前端请求这个接口的时候,其完整url是【/passport/getSMSCode?mobile=' + mobile】;

          ● 所以,这个接口需要设置url,以和前端保持一致;同时,我们也需要接收前端参数mobile;

          ● 声明:其实,自己并没有部署前端(主要是懒,怕麻烦);自己想把更多的精力投入在后端上面;;所以,自己在测试本机后端接口的时候,就在postman或者浏览器上测试了;;;;但是,对于每个接口,在前端页面上的表现,自己都会说清楚;;;而且,遇到一些“必须在前端页面上才能说清楚的事”,自己也都会阐述清楚来龙去脉;

          ● 然后,我们这儿为了达到【同一个用户,在60秒内,不能重复发送验证码】的目的;;;这儿,是通过ip地址来判定是否是同一用户的;;;所以,我们注入了HttpServletRequest这个方法参数;;

2.然后,完善【imooc-news-dev-user】(这个用户微服务中)的【短信验证码接口getSMSCode()】的实现;

package com.imooc.user.controller;

import com.imooc.api.BaseController;
import com.imooc.api.controller.user.PassportControllerApi;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.utils.IPUtil;
import com.imooc.utils.SMSUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class PassportController extends BaseController implements PassportControllerApi {
    final static Logger logger = LoggerFactory.getLogger(PassportController.class);
    @Autowired
    private SMSUtils smsUtils;//注入,我们自己编写的,阿里云短信服务工具类;

    /**
     * 获取短信验证码
     * @return
     */
    @Override
    public GraceJSONResult getSMSCode(String mobile, HttpServletRequest request) {
        //根据用户ip,限制用户在60秒内,只能获得一次验证码;
        String userIp = IPUtil.getRequestIp(request);//调用工具类,根据当前请求,获得用户ip;
        redisOperator.setnx60s(MOBILE_SMSCODE + ":" + userIp, userIp);//向redis中存储当前用户请求的ip,但是过来60秒后,这个数据就会超时消失;

        String randomCode = (int) ((Math.random() * 9 + 1) * 100000) + "";//随机验证码,6位或4位都可以
        smsUtils.sendSMS(mobile, randomCode);

        //把后端,生成的、原生的验证码,存在redis中;后面,用户在前端输入验证码时,可以拿来验证;
        redisOperator.set(MOBILE_SMSCODE + ":" + mobile, randomCode, 30 * 60);

        return GraceJSONResult.ok();
    }

}

说明:

(1)修改内容说明;

(2)根据HttpServletRequest获取该请求的ip地址;

          ● IPUtil工具类:对于这个工具类中内容,暂时可以不去深究;同时,可以看到这个工具类我们并没有使用IoC管理,同时其中的方法,我们设为了static;(一个暂留的问题:对于一个工具类,什么时候建议使用IoC管理,什么时候建议不使用IoC管理???

package com.imooc.utils;

import javax.servlet.http.HttpServletRequest;

/**
 * 用户获得用户ip的工具类
 */
public class IPUtil {

    /**
     * 获取请求IP:
     * 用户的真实IP不能使用request.getRemoteAddr()
     * 这是因为可能会使用一些代理软件,这样ip获取就不准确了
     * 此外我们如果使用了多级(LVS/Nginx)反向代理的话,ip需要从X-Forwarded-For中获得第一个非unknown的IP才是用户的有效ip。
     * @param request
     * @return
     */
    public static String getRequestIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

(3)一个设计思想:在【api】接口工程中,定义一个BaseController,在这个BaseController中,注入一些(其他处理具体业务的微服务中的Controller中,大概率会用到的)对象;;;然后,其他处理具体业务的微服务中的Controller,可以继承这个BaseController;;;这样依赖,这些处理具体业务的微服务中的Controller,要想使用某些对象时,就不用再重复注入了;

          ● 为什么BaseController不使用IoC容器管理起来?原因有两点:(1)如果BaseController使用IoC容器管理起来的话,我们需要在PassportController中注入BaseController,这样一搞反而不优雅了,还不如上面的继承的做法;;(2)子类继承父类的时候,创建子类对象,会创建父类对象???

(4)把用户的ip,存到redis中;(PS:好像听说redis中值的过期时间,有时不太准哎……,所以,这种解决方案,有待商榷……在自己实际开发中,这种利用redis过期时间来解决业务问题的方案,慎选!

          ● 假设用户的ip是【192.147.43.3】,那么只要保证我们存放到redis中的key是【mobile_smscode:192.147.43.3】,即只要保证这个key是唯一且有ip信息就够了;;;;其value究竟是【192.147.43.3】还是【“aahldjfsjk”】都不重要;;;;;因为,只要redis中有key为【mobile_smscode:192.147.43.3】的这条数据,同一个用户如果在60内再次发起请求时,其向redis中存数据的时候,就会发现redis中已经有key为【mobile_smscode:192.147.43.3】的数据了,,,就判定是同一个用户想在60秒内重复调用,自然就会被拒绝;

(5)在后台,随机生成一个验证码;(这个很简单,没什么好说的)

(6)调用阿里云短信服务,去把我们在后台生成的验证码,以短信的形式发送给用户;

(7)然后,把我们生成的验证码存到redis中;(后面,用户在前端输入收到的验证码时,我们会把【用户在收到短信后,在前端页面输入的验证码】和【redis中存储的原生的验证码】进行比对)


二:效果;

(1)首先,全局install一下;

(2)然后,启动【user】的主启动类;

(3)然后,访问【user】的【"/getSMSCode"】接口;

 

原网站

版权声明
本文为[小枯林]所创,转载请带上原文链接,感谢
https://wgy-coder.blog.csdn.net/article/details/125468316