阿里云第三方短信认证(验证码登录)

背景

由于阿里云短信服务(测试除外)需要备案+资质,个人测试开发者想要搞到以上两种需要花费点经历时间,所以up找到了第三方服务(阿里云 云市场)赋链接

但是:在第三方发送短信的时候(运营商规定),想要定制模板,也还是需要签名-需要申请,现在签名只能是公司全称,需要您提供营业执照,法人或者代办人身份证号,姓名

以下是up与客服的聊天记录

所以只要没有公司,购买了也还是只能用测试模板,但是至少能收到验证码,请谨慎选择

1.找到入口

[三网短信接口_API专区_云市场-阿里云

2.购买服务

3.查看AppKey

4.测试接口

5.使用

5.1引入依赖

<!--        短信认证相关依赖start-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.2.1</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>9.3.7.v20160115</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
<!--        短信认证相关依赖end-->
5.2导入HttpUtils

(这是在请求示例中要求的,在下一步我直接封装了一个,通过code和手机号就可以调用接口的方法)

package com.melody.utils;

/**
 * @Author: zengxz
 * @Description: HttpUtils(短信服务需要的http类)
 * @DateTime: 2025-05-12 17:28
 **/

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.junit.platform.commons.util.StringUtils;

public class HttpUtils {

    /**
     * get
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @return
     * @throws Exception
     */
    public static HttpResponse doGet(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys)
            throws Exception {
        HttpClient httpClient = wrapClient(host);

        HttpGet request = new HttpGet(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        return httpClient.execute(request);
    }

    /**
     * post form
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param bodys
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      Map<String, String> bodys)
            throws Exception {
        HttpClient httpClient = wrapClient(host);

        HttpPost request = new HttpPost(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        if (bodys != null) {
            List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();

            for (String key : bodys.keySet()) {
                nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
            }
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
            formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
            request.setEntity(formEntity);
        }

        return httpClient.execute(request);
    }

    /**
     * Post String
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      String body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);

        HttpPost request = new HttpPost(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        if (StringUtils.isNotBlank(body)) {
            request.setEntity(new StringEntity(body, "utf-8"));
        }

        return httpClient.execute(request);
    }

    /**
     * Post stream
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      byte[] body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);

        HttpPost request = new HttpPost(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        if (body != null) {
            request.setEntity(new ByteArrayEntity(body));
        }

        return httpClient.execute(request);
    }

    /**
     * Put String
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPut(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys,
                                     String body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);

        HttpPut request = new HttpPut(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        if (StringUtils.isNotBlank(body)) {
            request.setEntity(new StringEntity(body, "utf-8"));
        }

        return httpClient.execute(request);
    }

    /**
     * Put stream
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPut(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys,
                                     byte[] body)
            throws Exception {
        HttpClient httpClient = wrapClient(host);

        HttpPut request = new HttpPut(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        if (body != null) {
            request.setEntity(new ByteArrayEntity(body));
        }

        return httpClient.execute(request);
    }

    /**
     * Delete
     *
     * @param host
     * @param path
     * @param method
     * @param headers
     * @param querys
     * @return
     * @throws Exception
     */
    public static HttpResponse doDelete(String host, String path, String method,
                                        Map<String, String> headers,
                                        Map<String, String> querys)
            throws Exception {
        HttpClient httpClient = wrapClient(host);

        HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        return httpClient.execute(request);
    }

    private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
        StringBuilder sbUrl = new StringBuilder();
        sbUrl.append(host);
        if (!StringUtils.isBlank(path)) {
            sbUrl.append(path);
        }
        if (null != querys) {
            StringBuilder sbQuery = new StringBuilder();
            for (Map.Entry<String, String> query : querys.entrySet()) {
                if (0 < sbQuery.length()) {
                    sbQuery.append("&");
                }
                if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
                    sbQuery.append(query.getValue());
                }
                if (!StringUtils.isBlank(query.getKey())) {
                    sbQuery.append(query.getKey());
                    if (!StringUtils.isBlank(query.getValue())) {
                        sbQuery.append("=");
                        sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
                    }
                }
            }
            if (0 < sbQuery.length()) {
                sbUrl.append("?").append(sbQuery);
            }
        }

        return sbUrl.toString();
    }

    private static HttpClient wrapClient(String host) {
        HttpClient httpClient = new DefaultHttpClient();
        if (host.startsWith("https://")) {
            sslClient(httpClient);
        }

        return httpClient;
    }

    private static void sslClient(HttpClient httpClient) {
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                public void checkClientTrusted(X509Certificate[] xcs, String str) {

                }
                public void checkServerTrusted(X509Certificate[] xcs, String str) {

                }
            };
            ctx.init(null, new TrustManager[] { tm }, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = httpClient.getConnectionManager();
            SchemeRegistry registry = ccm.getSchemeRegistry();
            registry.register(new Scheme("https", 443, ssf));
        } catch (KeyManagementException ex) {
            throw new RuntimeException(ex);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException(ex);
        }
    }
}
5.3调用
package com.melody.melodicmusic;

import com.melody.utils.HttpUtils;
import com.melody.utils.JwtUtil;
import com.melody.utils.ThreadLocalUtil;
import org.apache.http.HttpResponse;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.HashMap;
import java.util.Map;


@SpringBootTest
class MelodicMusicApplicationTests {


    /**
     * token生成test
     */
    @Test
    void tokentest(){
//        如果用查到用户名
//                如果查询到的用户名与输入的密码相同
//                把信息放入token返回
                // 创建一个包含用户数据的Map
                Map<String, Object> claims = new HashMap<>();
                claims.put("userId",1);
                claims.put("username", "user1");
                // 生成JWT
                String token = JwtUtil.genToken(claims);
                System.out.println(token);
    }

    /**
     * 解析token测试
     */
    @Test
    void tokenOKtest(){
//Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsidXNlcklkIjoxLCJ1c2VybmFtZSI6InVzZXIxIn0sImV4cCI6MTc0ODMzMTAxN30.SYFjmlvWmeonCjcE7_CLIn43S4M4W6wKJifS16hW-gA
//        (7个字符)
        Map<String, Object> tokenMap = JwtUtil.parseToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsidXNlcklkIjoxLCJ1c2VybmFtZSI6InVzZXIxIn0sImV4cCI6MTc0ODMzMTAxN30.SYFjmlvWmeonCjcE7_CLIn43S4M4W6wKJifS16hW-gA");
        ThreadLocalUtil.set(tokenMap);
        System.out.println(ThreadLocalUtil.get().toString());
        // 从 userInfo 中获取 userid
        Object userId = tokenMap.get("userId");
        System.out.println(userId.toString());

    }

    /**
     * 短信验证测试
     */
    @Test
    void noteVerificationTest(){
        sendMsg("6666","18760372913");
    }

    /**
     * 请求方法
     * @param code
     * @param phone
     */
    public void sendMsg(String code, String phone) {
        //调用的URL
        String host = "https://dfsns.market.alicloudapi.com";
        String path = "/data/send_sms";
        //请求方式
        String method = "POST";
        //自己的appcode----------------------------------Start----------------------------------------------
        String appcode = "86b7a3fbc3eb4fee87a3eee40b95ba1a";
        //自己的appcode----------------------------------end----------------------------------------------
        Map<String, String> headers = new HashMap<>();
        //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 自己的appcode
        headers.put("Authorization", "APPCODE " + appcode);
        //根据API的要求,定义相对应的Content-Type
        headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        Map<String, String> querys = new HashMap<>();
        Map<String, String> bodys = new HashMap<>();
        bodys.put("content", "code:" + code);
        bodys.put("template_id", "CST_ptdie100");  //注意,CST_ptdie100该模板ID仅为调试使用,调试结果为"status": "OK" ,即表示接口调用成功,然后联系客服报备自己的专属签名模板ID,以保证短信稳定下发
        bodys.put("phone_number", phone);
        try {
            HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
            System.out.println("--------test-------------这是测试请求的请求返回值"+response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

测试完,是真的能收到了

之后自行保存sendMsg()函数,建议封装个工具类(后面附带)可以重复使用

6.springboot调用

原理:随机生成4为数字验证码,根据手机号设置redis 5分钟过期,登录和验证的时候根据手机号查询redis,查询验证码是否正确

6.1发送验证码

package com.melody.controller;

/**
 * @Author: zengxz
 * @Description: 请求验证码controller类
 * @DateTime: 2025-05-13 17:04
 **/
import com.melody.common.component.SmsComponent;
import com.melody.entry.Result;
import com.melody.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Random;

@RestController
@RequestMapping("/authCode")
public class VerificationCodeController {

    @Autowired
    private RedisUtil redisUtil; // 注入 Redis 工具类
    @Autowired
    private SmsComponent smsComponent;

    /**
     * 请求验证码接口
     * @param phone 用户ID(可以是手机号、邮箱或其他唯一标识)
     * @return 返回生成的验证码(实际项目中应发送到用户手机或邮箱,这里直接返回用于测试)
     */
    @GetMapping
    public Result<String> requestVerificationCode(@RequestParam String phone) {
        // 1.生成4位数字验证码
        String verificationCode = generateVerificationCode();
        // 2.发送验证码
        smsComponent.sendMsg(verificationCode, phone);
        // 3.将验证码存储到 Redis,设置过期时间为5分钟
        redisUtil.setVerificationCode("phone:code:" + phone, verificationCode, 60*5);
        // 4.返回验证码(实际项目中可以返回一个成功消息,验证码通过其他方式发送给用户)
        System.out.println("验证码已发送,验证码为:" + verificationCode);
        return Result.success("验证码已发送");
    }

    /**
     * 生成4位数字验证码
     * @return 验证码字符串
     */
    private String generateVerificationCode() {
        Random random = new Random();
        return String.format("%04d", random.nextInt(10000)); // 生成0000到9999的随机数
    }
}

6.2引入redis依赖

<!--redis坐标-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

6.3封装redis工具

package com.melody.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
/**
 * @Author: zengxz
 * @Description: redis验证码工具
 * @DateTime: 2025-05-13 16:52
 **/


    @Component
    public class RedisUtil {

        @Autowired
        private StringRedisTemplate stringRedisTemplate;

        /**
         * 设置验证码到 Redis,并设置过期时间
         * @param key Redis 键
         * @param value 验证码值
         * @param timeout 过期时间(秒)
         */
        public void setVerificationCode(String key, String value, long timeout) {
            stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
        }

        /**
         * 从 Redis 获取验证码
         * @param key Redis 键
         * @return 验证码值
         */
        public String getVerificationCode(String key) {
            return stringRedisTemplate.opsForValue().get(key);
        }

        /**
         * 删除验证码
         * @param key Redis 键
         */
        public void deleteVerificationCode(String key) {
            stringRedisTemplate.delete(key);
        }
    }

6.4实现验证码注册和验证码登录

dto等参数请根据自己需求修改

controller

package com.melody.controller;

import com.melody.entry.Result;
import com.melody.entry.UserInfo;
import com.melody.model.dto.UserSaveDto;
import com.melody.model.dto.UserRegisterDto;
import com.melody.service.UserInfoService;
import com.melody.utils.ThreadLocalUtil;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @Author: zengxz
 * @Description: 用户信息
 * @DateTime: 2025-05-08 21:58
 **/
@RestController
@RequestMapping("/user")
public class UserInfoController {

@Resource
private UserInfoService userInfoService;

/**
 * 用户注册
 * @param userRegisterDto
 * @return
 */
@PostMapping("/register")
public Result<String> userRegister(@RequestBody UserRegisterDto userRegisterDto){
    return userInfoService.userRegister(userRegisterDto);
}
/**
 * 验证码登录
 */
@PostMapping("/codeLogin")
public Result<String> codeLogin(@RequestBody UserSaveDto userSaveDto) {
    return  userInfoService.codeLogin(userSaveDto);
}
}

service就免了,我们直接看serviceImpl

package com.melody.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.melody.common.component.SmsComponent;
import com.melody.entry.Result;
import com.melody.entry.UserInfo;
import com.melody.model.dto.UserRegisterDto;
import com.melody.model.dto.UserSaveDto;
import com.melody.model.vo.UserInfoVo;
import com.melody.service.UserInfoService;
import com.melody.mapper.UserInfoMapper;
import com.melody.utils.JwtUtil;
import com.melody.utils.RedisUtil;
import com.melody.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
* @author zxz
* @description 针对表【user_info(用户信息表)】的数据库操作Service实现
* @createDate 2025-05-09 10:31:18
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
    implements UserInfoService{
        @Autowired
        private RedisUtil redisUtil;
/**
 * 用户注册
 * @param userRegisterDto
 * @return
 */
@Override
public Result<String> userRegister(UserRegisterDto userRegisterDto) {
    // 1.判断信息是否完全
    if (userRegisterDto == null || userRegisterDto.getUserName() == null || userRegisterDto.getPhone() == null || userRegisterDto.getPassword() == null){
        return Result.error("请填写完整信息");
    }
    // 2.redis获取验证码
    String redisCode = redisUtil.getVerificationCode( "phone:code:"+userRegisterDto.getPhone());
    if (redisCode == null){
        return Result.error("验证码过期");
    }
    // 3.判断验证码是否正确
    if (!redisCode.equals(userRegisterDto.getVerificationCode())){
        return Result.error("验证码错误");
    }
    // 4.插入数据库语句
    UserInfo userInfo = new UserInfo();
    userInfo.setUserName(userRegisterDto.getUserName());
    userInfo.setPhone(userRegisterDto.getPhone());
    userInfo.setPassword(userRegisterDto.getPassword());
    userInfo.setType(1);
    if (save(userInfo)){
        // 5.返回token
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId",userInfo.getUserId() );
        claims.put("username", userInfo.getUserName());
        // 生成JWT
        String token = JwtUtil.genToken(claims);
        return Result.success(token);
    }
    return Result.error("注册失败");
}

/**
 * 验证码登录
 * @param userSaveDto
 * @return
 */
@Override
public Result<String> codeLogin(UserSaveDto userSaveDto) {
    if (userSaveDto == null || userSaveDto.getPhone() == null || userSaveDto.getCode() == null){
        return Result.error("请填写完整信息");
    }
    String redisCode = redisUtil.getVerificationCode( "phone:code:"+userSaveDto.getPhone());
    if (redisCode == null){
        return Result.error("验证码过期");
    }
    UserInfo one = null;
    if (redisCode.equals(userSaveDto.getCode())){
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getPhone,userSaveDto.getPhone());
        one = getOne(queryWrapper);
        // 创建一个包含用户数据的Map
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId",one.getUserId() );
        claims.put("username", one.getUserName());
        // 生成JWT
        String token = JwtUtil.genToken(claims);
        return Result.success(token);
    }
    return Result.error("验证码错误");
}

        }        
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值