若依前后端分离版ruoyi-vue:增加新的登录接口(新用户表),用于小程序或者APP获取token,并使用若依的验证方法,结合腾讯云短信验证码实现手机号+验证码登陆

1.新建—SmsController类

package com.wanuw.user.controller.login;

import com.wanuw.common.constant.Constants;
import com.wanuw.common.core.domain.AjaxResult;
import com.wanuw.user.service.SmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 手机端验证码
 *
 * @author dagang
 */
@RestController
@RequestMapping("/sms")
public class SmsController
{
    @Autowired
    private SmsService smsService;

    /**
     * 生成手机验证码
     */
    @PostMapping("/send")
    public AjaxResult sendSmsCode(@RequestParam(value = "phoneNumber") String phoneNumber) {
        String message = smsService.sendSmsCode(phoneNumber);
        return AjaxResult.success(message);
    }
    /**
     * 验证手机验证码
     */
    @PostMapping("/verify")
    public AjaxResult verifySmsCode(@RequestParam(value = "phoneNumber") String phoneNumber,
        @RequestParam(value = "smsCode") String smsCode) {
        AjaxResult ajax = AjaxResult.success();
        String token = smsService.verifySmsCode(phoneNumber, smsCode);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }
}


2.新建—SmsService类

package com.wanuw.user.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.wanuw.common.core.domain.user.member.DbUser;

/**
 * 手机端用户登录验证
 *
 * @author dagang
 */
public interface SmsService extends IService<DbUser> {

    /**
     * 生成手机验证码
     */
    String sendSmsCode(String phoneNumber);

    /**
     * 验证手机验证码
     */
    String verifySmsCode(String phoneNumber, String smsCode);

}

3.新建—SmsServiceImpl 实现层

此处注意注入的是:AppAuthenticationProvider

TokenService和RedisCache是若依框架自带

package com.wanuw.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tencentcloudapi.sms.v20190711.models.SendStatus;
import com.wanuw.common.config.tencent.SmsConfig;
import com.wanuw.common.constant.CacheConstants;
import com.wanuw.common.constant.Constants;
import com.wanuw.common.core.domain.model.LoginUser;
import com.wanuw.common.core.redis.RedisCache;
import com.wanuw.common.exception.ServiceException;
import com.wanuw.common.exception.user.CaptchaException;
import com.wanuw.common.exception.user.CaptchaExpireException;
import com.wanuw.common.exception.user.UserNotExistsException;
import com.wanuw.common.exception.user.UserPasswordNotMatchException;
import com.wanuw.common.utils.ChineseNameUtils;
import com.wanuw.common.utils.MessageUtils;
import com.wanuw.common.utils.SecurityUtils;
import com.wanuw.common.utils.tencent.SmsUtil;
import com.wanuw.framework.manager.AsyncManager;
import com.wanuw.framework.manager.factory.AsyncFactory;
import com.wanuw.framework.web.service.TokenService;
import com.wanuw.common.core.domain.user.member.DbUser;
import com.wanuw.member.mapper.DbUserMapper;
import com.wanuw.member.service.IDbUserService;
import com.wanuw.user.controller.login.AppAuthenticationProvider;
import com.wanuw.user.service.SmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 手机端用户登录验证
 *
 * @author dagang
 */
@Service
public class SmsServiceImpl extends ServiceImpl<DbUserMapper, DbUser> implements SmsService {

    @Autowired
    private TokenService tokenService;

    @Resource
    private AppAuthenticationProvider authenticationManager;

    @Resource
    private SmsConfig smsConfig;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IDbUserService dbUserService;
    /**
     * 创建手机验证码
     */
    @Override
    public String sendSmsCode(String phoneNumber) {
        // 下发手机号码,采用e.164标准,+[国家或地区码][手机号]
        String[] phoneNumbers = {"+86" + phoneNumber};
        // 生成6位随机数字字符串
        String smsCode = RandomUtil.randomNumbers(6);
        // 模板参数:若无模板参数,则设置为空(参数1为随机验证码,参数2为有效时间)
        String[] templateParams = {smsCode, smsConfig.getExpireTime().toString()};
        // 发送短信验证码
        SendStatus[] sendStatuses = SmsUtil.sendSms(smsConfig, templateParams, phoneNumbers);
        if ("Ok".equals(sendStatuses[0].getCode())) {
            // 创建短信验证码缓存的key并设置过期时间
            String key = CacheConstants.CAPTCHA_TELPHONE_CODE_KEY + phoneNumber;
            redisCache.setCacheObject(key, smsCode, smsConfig.getExpireTime(), TimeUnit.MINUTES);
            return "验证码发送成功";
        } else {
            return "验证码发送失败:" + sendStatuses[0].getMessage();
            //return "验证码发送失败:";
        }
    }

    /**
     * 验证手机验证码
     */
    @Override
    public String verifySmsCode(String phoneNumber, String smsCode) {
        //1.验证码校验
        checkCode(phoneNumber, smsCode);
        //2.检查用户手机号是否已经注册,若未注册,直接注册成用户
        LambdaQueryWrapper<DbUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DbUser::getUserTel, phoneNumber);
        DbUser dbUser = dbUserService.getOne(queryWrapper);

        if (dbUser == null) {
            //说明未注册过账户,开始注册账户
            DbUser dbUserNew = new DbUser();
            dbUserNew.setUserTel(phoneNumber);
            //添加默认密码123456
            dbUserNew.setPassword(SecurityUtils.encryptPassword("123456"));
            //这是个生成随机汉字名字的工具
            dbUserNew.setUserNickname(ChineseNameUtils.randomChineseName());
            dbUserNew.setUserName(phoneNumber);
            dbUserService.save(dbUserNew);
        }
        String username = phoneNumber;
        String password = "123456";
        //自定义用户名和密码

        // 用户验证
        Authentication authentication;
        try {
            // 原来其实就这么一句话:该方法会去调用UserDetailsServiceImpl.loadUserByUsername。指的是原来若依自定义的UserDetailsServiceImpl
            //此处会让人很迷惑,特别是对新手来说。其实就是调用了AppUserDetailsServiceImpl中的loadUserByUsername方法
            //而这个方法的是通过AppAuthenticationProvider中去发起的。所以这个authenticationManager  其实就是注入的AppAuthenticationProvider
            //这个地方一定要注意!!!!!
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            } else {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //recordLoginInfo(dbUser.getId());
        // 生成token
        return tokenService.createToken(loginUser);
    }

    /**
     *
     * @param phoneNumber 电话号码
     * @param smsCode  验证码
     */
    public void checkCode(String phoneNumber, String smsCode) {
        // 创建key
        String key = CacheConstants.CAPTCHA_TELPHONE_CODE_KEY + phoneNumber;
        String captcha = redisCache.getCacheObject(key);
        //校验传入数据是否为空
        if (phoneNumber == null || smsCode == null) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        //校验验证码位数
        if (smsCode.length() != 6) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // 判断指定key是否存在并且未过期
        if (captcha == null)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
            throw new CaptchaExpireException();
        }
        // 验证输入的验证码是否正确
        if (!smsCode.equalsIgnoreCase(captcha))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
            throw new CaptchaException();
        }
        // 验证成功后删除验证码缓存
        redisCache.deleteObject(key);
    }
}

4.新建—AppUserDetailsServiceImpl类

package com.wanuw.user.controller.login;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wanuw.common.core.domain.model.LoginUser;
import com.wanuw.common.exception.ServiceException;
import com.wanuw.common.utils.StringUtils;
import com.wanuw.common.core.domain.user.member.DbUser;
import com.wanuw.member.mapper.DbUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
 * 手机端验证码
 *
 * @author dagang
 */
@Slf4j
@Service
public class AppUserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private DbUserMapper dbUserMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //验证登录用户,使用的是mybatis-plus语法查询的用户数据
        //因为是手机号登录,上面形参其实是手机号码,可以改成phone,本人偷懒,未改
        DbUser dbUser = dbUserMapper.selectOne(new LambdaQueryWrapper<DbUser>()
                .eq(DbUser::getUserTel, username));

        if (StringUtils.isNull(dbUser)) {
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException("登录用户:" + username + " 不存在");
        } else if (dbUser.getDeleted() == 1) {
            log.info("登录用户:{} 已被删除.", username);
            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
        } else if (dbUser.getUserStatus() == 1) {
            log.info("登录用户:{} 已被停用.", username);
            throw new ServiceException("对不起,您的账号:" + username + " 已停用");
        }
        //返回UserDetails用户对象
        return createLoginUser(dbUser);
    }

    public UserDetails createLoginUser(DbUser dbUser) {
        /**
         * 参数一:第一个是用户的ID,用户后期使用SecurityUtils.getUserId()时获取上下文中存储的用户ID数据,若未设置,SecurityUtils.getUserId()获取数据位null
         * 参数二:整个用户数据传入,后面会保存在Redis中,登陆校验时会用到
         */
        return new LoginUser(dbUser.getId(),dbUser);
    }
}

5.新建—AppAuthenticationProvider

package com.wanuw.user.controller.login;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 手机端验证码
 *
 * @author dagang
 */
@Slf4j
@Component
public class AppAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private AppUserDetailsServiceImpl userDetailsService;

    @SneakyThrows
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 这个获取表单输入中返回的用户名,此处获取到的是手机号码
        String userName = authentication.getName();

        // 这里构建来判断用户是否存在和密码是否正确
        // 这里调用我们的自己写的AppUserDetailsServiceImpl获取用户的方法;
        UserDetails userInfo = userDetailsService.loadUserByUsername(userName);
        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        // 构建返回的用户登录成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, userInfo.getPassword(), authorities);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        //return authentication.equals(UsernamePasswordAuthenticationToken.class);
        // 这里直接改成 return true;表示是支持这个执行
        return true;
    }
}

6.修改—LoginUser

路径:src/main/java/com/wanuw/common/core/domain/model/LoginUser.java

添加新用户表实体类,一定要添加Getter和Setter,之前未添加导致存入Redis后没有用户实体类的信息,查找了好久,主要是我水平有限,可能水平高的人一下就会找到原因了。

package com.wanuw.common.core.domain.model;

import com.alibaba.fastjson2.annotation.JSONField;
import com.wanuw.common.core.domain.entity.SysUser;
import com.wanuw.common.core.domain.user.member.DbUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;

/**
 * 登录用户身份权限
 *
 * @author ruoyi
 */
public class LoginUser implements UserDetails
{
    private static final long serialVersionUID = 1L;

    /**
     * 用户ID
     */
    private Long userId;

    /**
     * 部门ID
     */
    private Long deptId;

    /**
     * 用户唯一标识
     */
    private String token;

    /**
     * 登录时间
     */
    private Long loginTime;

    /**
     * 过期时间
     */
    private Long expireTime;

    /**
     * 登录IP地址
     */
    private String ipaddr;

    /**
     * 登录地点
     */
    private String loginLocation;

    /**
     * 浏览器类型
     */
    private String browser;

    /**
     * 操作系统
     */
    private String os;

    /**
     * 权限列表
     */
    private Set<String> permissions;

    /**
     * 用户信息
     */
    private SysUser user;
    /**
     * App用户数据
     */
    private DbUser dbUser;

    public DbUser getDbUser() {
        return dbUser;
    }

    public void setDbUser(DbUser dbUser) {
        this.dbUser = dbUser;
    }

    @JSONField(serialize = false)
    @Override
    public String getPassword()
    {
        if (user != null) {
            return user.getPassword();
        } else {
            return dbUser.getPassword();
        }
    }
    @Override
    public String getUsername()
    {
        if (user != null) {
            return user.getUserName();
        } else {
            return dbUser.getUserTel();
        }
    }

    public LoginUser(Long userId, DbUser dbUser)
    {
        this.userId = userId;
        this.dbUser = dbUser;

    }

    public LoginUser()
    {
    }

    public LoginUser(SysUser user, Set<String> permissions)
    {
        this.user = user;
        this.permissions = permissions;
    }
    public LoginUser(DbUser dbUser)
    {
        this.dbUser = dbUser;
    }

    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
    {
        this.userId = userId;
        this.deptId = deptId;
        this.user = user;
        this.permissions = permissions;
    }

    public Long getUserId()
    {
        return userId;
    }

    public void setUserId(Long userId)
    {
        this.userId = userId;
    }

    public Long getDeptId()
    {
        return deptId;
    }

    public void setDeptId(Long deptId)
    {
        this.deptId = deptId;
    }

    public String getToken()
    {
        return token;
    }

    public void setToken(String token)
    {
        this.token = token;
    }


    /**
     * 账户是否未过期,过期无法验证
     */
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonExpired()
    {
        return true;
    }

    /**
     * 指定用户是否解锁,锁定的用户无法进行身份验证
     *
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonLocked()
    {
        return true;
    }

    /**
     * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
     *
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isCredentialsNonExpired()
    {
        return true;
    }

    /**
     * 是否可用 ,禁用的用户不能身份验证
     *
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isEnabled()
    {
        return true;
    }

    public Long getLoginTime()
    {
        return loginTime;
    }

    public void setLoginTime(Long loginTime)
    {
        this.loginTime = loginTime;
    }

    public String getIpaddr()
    {
        return ipaddr;
    }

    public void setIpaddr(String ipaddr)
    {
        this.ipaddr = ipaddr;
    }

    public String getLoginLocation()
    {
        return loginLocation;
    }

    public void setLoginLocation(String loginLocation)
    {
        this.loginLocation = loginLocation;
    }

    public String getBrowser()
    {
        return browser;
    }

    public void setBrowser(String browser)
    {
        this.browser = browser;
    }

    public String getOs()
    {
        return os;
    }

    public void setOs(String os)
    {
        this.os = os;
    }

    public Long getExpireTime()
    {
        return expireTime;
    }

    public void setExpireTime(Long expireTime)
    {
        this.expireTime = expireTime;
    }

    public Set<String> getPermissions()
    {
        return permissions;
    }

    public void setPermissions(Set<String> permissions)
    {
        this.permissions = permissions;
    }

    public SysUser getUser()
    {
        return user;
    }

    public void setUser(SysUser user)
    {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
    {
        return null;
    }
}

截图注意项:

7.修改—解决运行冲突

路径:src/main/java/com/wanuw/framework/config/SecurityConfig.java

添加注解:@Qualifier("userDetailsServiceImpl")

 此时启动项目不会报冲突的错,其实就是给起了一个不冲突的名字

千万千万要添加!!!

此时已经添加完成了。可以启动测试了!

==============================分割线===================================

附上:

1.DbUser实体类,做参考

package com.wanuw.common.core.domain.user.member;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.wanuw.common.annotation.Excel;
import com.wanuw.common.core.domain.BaseEntity;
import lombok.*;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 会员对象 db_user
 *
 * @author dagang
 * @date 2023-11-09
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("db_user")
public class DbUser extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * 会员主键
     */
    @TableId(value = "id")
    private Long id;
    /**
     * 用户名
     */
    @TableField(value = "user_name")
    @Excel(name = "用户名")
    private String userName;
    /**
     * 手机号
     */
    @TableField(value = "user_tel")
    @Excel(name = "手机号")
    private String userTel;
    /**
     * 密码
     */
    @TableField(value = "password")
    private String password;
    /**
     * 推广号(不能重复 无上级默认1010110)
     */
    @TableField(value = "user_code")
    private String userCode;
    /**
     * 用户昵称(注册时随机字母)
     */
    @TableField(value = "user_nickname")
    private String userNickname;
    /**
     * 头像
     */
    @TableField(value = "user_image")
    private String userImage;
    /**
     * 性别
     */
    @TableField(value = "user_sex")
    private Integer userSex;
    /**
     * 所在地(辽宁,沈阳)
     */
    @TableField(value = "user_adddress")
    private String userAdddress;
    /**
     * 会员简介
     */
    @TableField(value = "user_text")
    private String userText;
}

2.腾讯短信工具类SmsUtils

package com.wanuw.common.utils.tencent;

import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20190711.models.SendStatus;
import com.wanuw.common.config.tencent.SmsConfig;
import lombok.extern.slf4j.Slf4j;


@Slf4j
public class SmsUtil {

        public static SendStatus[] sendSms(SmsConfig smsConfig, String[] templateParams, String[] phoneNumbers) {

                try {
                        /* 必要步骤:
                         * 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
                         * 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
                         * 您也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
                         * 以免泄露密钥对危及您的财产安全。
                         * SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */
                        Credential cred = new Credential(smsConfig.getSecretId(), smsConfig.getSecretKey());

                        // 实例化一个http选项,可选,没有特殊需求可以跳过
                        HttpProfile httpProfile = new HttpProfile();
                        // 设置代理(无需要直接忽略)
                        // httpProfile.setProxyHost("真实代理ip");
                        // httpProfile.setProxyPort(真实代理端口);
                        /* SDK默认使用POST方法。
                         * 如果您一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */
                        httpProfile.setReqMethod("POST");
                        /* SDK有默认的超时时间,非必要请不要进行调整
                         * 如有需要请在代码中查阅以获取最新的默认值 */
                        httpProfile.setConnTimeout(60);
                        /* 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
                        httpProfile.setEndpoint("sms.tencentcloudapi.com");
                        /* 非必要步骤:
                         * 实例化一个客户端配置对象,可以指定超时时间等配置 */
                        ClientProfile clientProfile = new ClientProfile();
                        /* SDK默认用TC3-HMAC-SHA256进行签名
                         * 非必要请不要修改这个字段 */
                        clientProfile.setSignMethod("HmacSHA256");
                        clientProfile.setHttpProfile(httpProfile);
                        /* 实例化要请求产品(以sms为例)的client对象
                         * 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
                        SmsClient client = new SmsClient(cred, "ap-guangzhou",clientProfile);
                        /* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
                         * 您可以直接查询SDK源码确定接口有哪些属性可以设置
                         * 属性可能是基本类型,也可能引用了另一个数据结构
                         * 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */
                        SendSmsRequest req = new SendSmsRequest();
                        /* 填充请求参数,这里request对象的成员变量即对应接口的入参
                         * 您可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义
                         * 基本类型的设置:
                         * 帮助链接:
                         * 短信控制台: https://console.cloud.tencent.com/smsv2
                         * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
                        
                        /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */
                        // 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
                        req.setSmsSdkAppid(smsConfig.getAppId());
                        
                        /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
                        // 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
                        req.setSign(smsConfig.getSign());
                        
                        /* 模板 ID: 必须填写已审核通过的模板 ID */
                        // 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
                        req.setTemplateID(smsConfig.getTemplateId());
                        
                        /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */
                        req.setTemplateParamSet(templateParams);
                        
                        /* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
                         * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */
                        req.setPhoneNumberSet(phoneNumbers);
                        
                        /* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 */
                        String sessionContext = "";
                        req.setSessionContext(sessionContext);
                        /* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */
                        String extendCode = "";
                        req.setExtendCode(extendCode);
                        /* 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId,无需填写该字段。注:月度使用量达到指定量级可申请独立 SenderId 使用,详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。*/
                        String senderid = "";
                        req.setSenderId(senderid);
                        /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
                         * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
                        SendSmsResponse res = client.SendSms(req);
                        // 输出json格式的字符串回包
                        log.info("json格式字符串返回包:{}", SendSmsResponse.toJsonString(res));
                        return res.getSendStatusSet();
                        // 也可以取出单个值,您可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
                        // System.out.println(res.getRequestId());
                        
                        /* 当出现以下错误码时,快速解决方案参考
                         * [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                         * [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                         * [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                         * [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                         * 更多错误,可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
                         */
                } catch (TencentCloudSDKException e) {
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage());
                }
        }
}

3.SmsComfig配置类

package com.wanuw.common.config.tencent;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@ConfigurationProperties(prefix = "tencent.sms")
@Configuration
@Data
public class SmsConfig {
    /**
     * 腾讯云API密钥的SecretId
     */
    private String secretId;
    /**
     * 腾讯云API密钥的SecretKey
     */
    private String secretKey;
    /**
     * 短信应用的SDKAppID
     */
    private String appId;
    /**
     * 签名内容
     */
    private String sign;
    /**
     * 模板ID
     */
    private String templateId;
    /**
     * 过期时间
     */
    private Integer expireTime;
    /**
     * 缓存前缀
     */
    private String phonePrefix;

}

4.腾讯云yml配置

根据自己腾讯云实际情况填写,下面只是个样子,*为加密

tencent:
  sms:
    # 配置腾讯云API密钥的SecretId
    secretId: ********************************
    # 配置腾讯云API密钥的SecretKey
    secretKey: ******************************
    # 配置短信应用的SDKAppID
    appId: 1400*****
    # 配置签名内容
    sign: "******"
    # 配置模板ID
    templateId: 19*****
    # 配置过期时间
    expireTime: 5
    # 配置redis存储的key的前缀
    phonePrefix: tel_captcha_codes

测试:测试工具为Apifox

1.发送短信验证码

2.收取到的验证码

3.验证验证码,获取token

4.使用token获取数据

=============================结束================================

附言:

        本人是个新手,不懂的还有很多,若是有不完善或者不对的地方可以留言提醒,谢谢!

感谢:

参考文章链接:https://blog.csdn.net/yyongsheng/article/details/127584458