- 申请阿里短信验证码服务获取服务信息如下
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功能服务,位置如下
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方法(如果若依没有的话可以创建,否则直接引用原方法即可)
/**
* 创建令牌
*
* @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