若依框架集成阿里短信验证码登录

186 阅读3分钟
  1. 申请阿里短信验证码服务获取服务信息如下
alibaba:
  cloud:
    sms:
      ACCESS_KEY_ID: LTAI5tBw***tWKr28G***
      AccessKey_Secret: 6nFNj***TZiwIALgLYN***
      endpoint: dysmsapi.ap-southeast-1.aliyuncs.com
      SignName: ***
      TemplateCode: SMS_***

2. 在用到验证码api接口的模块的pom中引入阿里的短信验证依赖

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>dysmsapi20170525</artifactId>
    <version>3.1.1</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>tea-openapi</artifactId>
    <version>0.3.6</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>tea-console</artifactId>
    <version>0.0.1</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>tea-util</artifactId>
    <version>0.2.23</version>
</dependency>

3. 在framework中创建SmsService功能服务,位置如下

image.png

package com.ylg.framework.sms;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class SmsService {

    @Value("${alibaba.cloud.sms.ACCESS_KEY_ID}")
    private  String ACCESS_KEY_ID;
    @Value("${alibaba.cloud.sms.AccessKey_Secret}")
    private  String AccessKey_Secret;

    @Value("${alibaba.cloud.sms.SignName}")
    private String SignName;

    @Value("${alibaba.cloud.sms.TemplateCode}")
    private String TemplateCode;

    /**
     * 使用阿里云短信服务发送验证码
     * @param phoneNumber 手机号
     * @param code 验证码
     * @return 是否发送成功
     */
    public boolean sendSms(String phoneNumber, String code) {
        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY_ID, AccessKey_Secret);
        IAcsClient client = new DefaultAcsClient(profile);

        SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(phoneNumber);
        request.setSignName(SignName);
        request.setTemplateCode(TemplateCode);
        request.setTemplateParam("{"code":"" + code + ""}");

        try {
            SendSmsResponse response = client.getAcsResponse(request);
            return "OK".equals(response.getCode());
        } catch (ClientException e) {
            e.printStackTrace();
            return false;
        }
    }
}

在framework下的TokenService中创建createToken方法(如果若依没有的话可以创建,否则直接引用原方法即可) image.png

/**
 * 创建令牌
 *
 * @param loginUser 用户信息
 * @return 令牌
 */
public String createToken(LoginUser loginUser)
{
    String token = IdUtils.fastUUID();
    loginUser.setToken(token);
    setUserAgent(loginUser);
    refreshToken(loginUser);

    Map<String, Object> claims = new HashMap<>();
    claims.put(Constants.LOGIN_USER_KEY, token);
    return createToken(claims);
}

4. 修改ISysUserService接口文件,添加根据手机号码查询用户selectUserByPhoneNumber接口方法

/**
 * 根据手机号码查询用户
 * @param phoneNumber
 * @return
 */
public SysUser selectUserByPhoneNumber(String phoneNumber);
@Override
public SysUser selectUserByPhoneNumber(String phoneNumber) {
    return userMapper.selectUserByPhoneNumber(phoneNumber);
}

mapper接口及mapper.xml的sql实现

import org.apache.ibatis.annotations.Param;
/**
 * 根据手机号码查询用户
 */
public SysUser selectUserByPhoneNumber(@Param("phoneNumber") String phoneNumber);
<select id="selectUserByPhoneNumber" parameterType="String" resultMap="SysUserResult">
   <include refid="selectUserVo"/>
   where u.phonenumber = #{phoneNumber} and u.del_flag = '0'
</select>

5. api接口文件controlller文件接口

package com.ylg.web.controller.system;


import java.util.Random;
import java.util.concurrent.TimeUnit;
import com.ylg.common.utils.MessageUtils;
import com.ylg.framework.manager.AsyncManager;
import com.ylg.framework.manager.factory.AsyncFactory;
import com.ylg.framework.sms.SmsService;
import com.ylg.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import com.ylg.common.constant.Constants;
import com.ylg.common.core.domain.AjaxResult;
import com.ylg.common.core.domain.entity.SysUser;
import com.ylg.common.core.domain.model.LoginUser;
import com.ylg.framework.web.service.SysPermissionService;
import com.ylg.framework.web.service.TokenService;


/**
 * 登录验证
 * 
 * @author ruoyi
 */
@RestController
public class SysLoginController
{


    @Autowired
    private SysPermissionService permissionService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private SmsService smsService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Autowired
    private ISysUserService userService;


    
    /**
     * 发送短信验证码
     * @param phoneNumber 手机号
     * @return AjaxResult
     */
    @PostMapping("/sms/send")
    public AjaxResult sendSms(@RequestParam String phoneNumber) {
        // 检查是否在1分钟内已经发送过验证码
        String sentTimeKey = phoneNumber + ":sentTime";
        if (redisTemplate.hasKey(sentTimeKey)) {
            return AjaxResult.error("获取验证码过于频繁,请稍后再试!");
        }

        // 生成6位随机验证码
        String code = String.valueOf(new Random().nextInt(899999) + 100000);
        //String code = "123456";
        // 将验证码存入 Redis,有效期5分钟
        redisTemplate.opsForValue().set(phoneNumber, code, 5, TimeUnit.MINUTES);

        // 记录发送时间,有效期1分钟
        redisTemplate.opsForValue().set(sentTimeKey, "1", 1, TimeUnit.MINUTES);

        // 调用短信服务发送验证码
        boolean isSent = smsService.sendSms(phoneNumber, code);

        if (isSent) {
            return AjaxResult.success("验证码发送成功");
        } else {
            return AjaxResult.error("验证码发送失败");
        }
    }

    /**
     * 短信验证码登录接口
     * @param phoneNumber 手机号
     * @param code 验证码
     * @return AjaxResult
     */
    @PostMapping("/loginWithSms")
    public AjaxResult loginWithSms(@RequestParam String phoneNumber, @RequestParam String code) {
        // 从 Redis 中获取验证码
        String cachedCode = redisTemplate.opsForValue().get(phoneNumber);

        if (cachedCode != null && cachedCode.equals(code)) {
            // 验证成功,返回登录凭证(例如Token)
            String token = generateToken(phoneNumber);  // 使用若依自带的Token生成逻辑
            return AjaxResult.success("登录成功", token);
        } else {
            return AjaxResult.error("验证码错误或已过期");
        }
    }

    /**
     * 根据手机号生成 JWT Token
     * @param phoneNumber 用户手机号
     * @return JWT Token
     */
    private String generateToken(String phoneNumber) {
        // 通过手机号获取用户信息
        SysUser sysUser = userService.selectUserByPhoneNumber(phoneNumber);

        if (sysUser == null) {
            throw new RuntimeException("用户不存在");
        }

        // 封装用户信息到 LoginUser 对象
        LoginUser loginUser = new LoginUser();
        loginUser.setUser(sysUser);  // 设置用户信息
        loginUser.setPermissions(permissionService.getMenuPermission(sysUser));  // 设置权限信息
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        //loginUser.setPermissions(sysUser.getRoles());
        // 生成 Token
        return tokenService.createToken(loginUser);
    }
}

6. postman调用接口的方法参数(post) 获取验证码,发送验证码到手机,返回发送成功信息 http://localhost:80/sms/send?phoneNumber=13444444444 通过手机号验证码登录,返回token http://localhost:80/loginWithSms?phoneNumber=13444444444&code=123456