目录一. 前置准备工作创建阿里云OSS服务获取密钥开通短信服务1.1 注册阿里云服务器1.2 开通短信服务1.3 创建对象存储OSS服务1.4 RAM用户授权短信权限1.5 新增用户并授权用户短信权限1.6 获取 AccessKey ID 和 AccessKey Secret二. 项目实战创建项目编写代码集成短信发送2.1 引入依赖2.2 修改 yml 文件2.3 创建阿里云短信配置类2.4 编写 SmsService 短信发送模板业务类2.5 在业务中调用短信发送的方法一. 前置准备工作创建阿里云OSS服务获取密钥开通短信服务这里我们就从申请注册短信模板0基础开始方便小白同学也能快速上手如果你已经准备好了短信发送的账户密钥等信息直接跳至第二部分项目实战。1.1 注册阿里云服务器点击链接直接选择一种登录方式注册登陆即可。阿里云登录 - 欢迎登录阿里云安全稳定的云计算服务平台1.2 开通短信服务搜索短信服务点击跳转然后有免费开通。如下页面简单过一遍快速学习与测试走完上面的流程我们点击国内消息然后如右侧页面所示需要去申请资质只要是项目集成都需要申请资质资质申请审核完毕到签名页面添加签名签名会需要使用到申请的资质签名创建完毕后添加我们的短信模板自行选择即可也可以自定义模板。创建完毕之后一定要把签名短信模板Code码记下来一会在代码中会用到。这里以我的为例签名是aliyunSM666。短信模板Code为SMS_4810150051.3 创建对象存储OSS服务左上角搜索输入对象存储即可找到对象存储OSS服务点击跳转。然后来到如下页面点击 Bucket——创建Bucket 一步步操作即可。这里需要记一个点如果创建的地域是北京那么下面在配置 yml 文件时aliyun.regionId 的值就是 cn-beijing如果是杭州就是 cn-hangzhou如果是上海就是cn-shanghai。1.4 RAM用户授权短信权限创建完毕 Bucket 完毕后点击创建的 Bucket 进入详情页。然后前往RAM控制台1.5 新增用户并授权用户短信权限来到RAM控制台先新增一名用户然后点击添加权限在权限策略中输入短信即可搜索到直接授权给用户即可。如果之前已经创建过用户直接授权即可也可以像下图所示到授权页面选择指定用户授权不能1.6 获取 AccessKey ID 和 AccessKey Secret点击右上角用户头像跳转到 AccessKey 详情页如果没有则新建一个即可注意AssessKey Secret 新建后不会再展示首次新建请保存如果遗忘只需禁用现有的AssessKey再新建一个即可。二. 项目实战创建项目编写代码集成短信发送2.1 引入依赖!-- 阿里云核心 SDK -- dependency groupIdcom.aliyun/groupId artifactIdaliyun-java-sdk-core/artifactId version4.5.16/version /dependency !-- 短信服务 SDK -- dependency groupIdcom.aliyun/groupId artifactIdaliyun-java-sdk-dysmsapi/artifactId version2.1.0/version /dependency !-- OSS SDK -- dependency groupIdcom.aliyun.oss/groupId artifactIdaliyun-sdk-oss/artifactId version3.15.1/version /dependency2.2 修改 yml 文件如下图所示修改项目的 yml 文件在 yml 文件中添加 aliyun 配置。assessKeyId 就是上面第一步提到的 AccessKey IDaccessKeySecret 就是上面提到的 AccessKey SecretregionId 就是上面提到的 cn-shanghai根据自己创建的 Bucket 区域而定。2.3 创建阿里云短信配置类在下面配置类中首先获取 yml 配置文件中设置的值然后编写返回实例的方法并将值赋值给新建实例。然后使用短信客户端实例调用 API 接口发送短信这里写为一个方法传递手机号签名短信模板Code值然后再其他业务类型注入此配置类的实例调用 sendSms 方法根据业务需求传递不同的短信Code发送不同的短信。Configuration public class AliyunSmsConfig { // 1.阿里云账号的accessKeyId Value(${aliyun.assessKeyId}) private String accessKeyId; // 2.阿里云账号的accessKeySecret Value(${aliyun.assessKeySecret}) private String accessKeySecret; // 3.阿里云账号的regionId Value(${aliyun.regionId}) private String regionId; // 创建并初始化阿里云短信服务的客户端实例 Bean public IAcsClient acsClient() { DefaultProfile profile DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret); return new DefaultAcsClient(profile); } // 发送短信的核心逻辑通过 IAcsClient 调用阿里云短信服务 API public SendSmsResponse sendSms(String phoneNumber, String signName, String templateCode, String templateParam) throws Exception { IAcsClient client acsClient(); SendSmsRequest request new SendSmsRequest(); request.setPhoneNumbers(phoneNumber); request.setSignName(signName); request.setTemplateCode(templateCode); request.setTemplateParam(templateParam); return client.getAcsResponse(request); } }2.4 编写 SmsService 短信发送模板业务类在上面我们已经定义好了发送短信的方法按道理来说直接 Autowired 注入 配置的 Bean 实例就可以了但是我们观察上方方法其实还不够简洁因为 sendSms 方法中只有 phone 手机号一个参数是需要用户传递的其他的参数是不需要的。因此我们最好再进行一层抽取将每一种发送短信验证码的方法写成一个 Service 业务类这样一来其他类当要发送短信的时候直接调用封装好的 Service 中的方法即可优化了原本的业务类代码。如下代码所示定义一个发送登录验证码的方法 sendLoginVerificationCode还可以定义发送修改密码的验证码 sendUpdatePasswordVerificationCode还可以定义付钱的验证码方法 sendPayMoneyVerificationCode。当然啦这里只是举个例子实际业务项目中需要发送的短信验证码肯定也是多种多样的可以定义多个方法也可以定义一个通用方法都是可以的小编这里就采用这种常用写法啦。Service Slf4j public class SmsService { Autowired private AliyunSmsConfig aliyunSmsConfig; public boolean sendLoginVerificationCode(String phoneNumber, String code) { try { SendSmsResponse response aliyunSmsConfig.sendSms( phoneNumber, aliyunSMS666, SMS_481015005, {\code\:\ code \} ); log.info(response.getCode()); return OK.equals(response.getCode()); } catch (Exception e) { e.printStackTrace(); return false; } } public boolean sendUpdatePasswordVerificationCode(String phoneNumber, String code) { try { SendSmsResponse response aliyunSmsConfig.sendSms( phoneNumber, aliyunSMS666, SMS_481015005, {\code\:\ code \} ); log.info(response.getCode()); return OK.equals(response.getCode()); } catch (Exception e) { e.printStackTrace(); return false; } } public boolean sendPayMoneyVerificationCode(String phoneNumber, String code) { ...... } }2.5 在业务中调用短信发送的方法上面的工作都做完之后我们就可以进入到真正的业务层了如下代码其实非常简单这里小编定义了五个方法其实这里的 sendCode 方法是可以不用要的毕竟代码不多也可以直接将代码写入到 login 登陆方法中。selectByUsername 查询用户方法sendCode发送短信方法login 登陆方法createUser 创建新用户方法generateTokenByUid 生成用户 token 方法逻辑很简单都有注释大家一步一步看即可。Service public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService { // 添加类顶部声明 private static final Logger logger LoggerFactory.getLogger(UserServiceImpl.class); Autowired private UserMapper userMapper; Autowired private SmsService smsService; Autowired private StringRedisTemplate stringRedisTemplate; // 方法一 根据用户名查询用户是否已存在 Override public Boolean selectByUsername(String username) { return !userMapper.selectByUserName(username).isEmpty(); } /* 方法二 发送短信方法 */ public Boolean sendCode(String phone) { // 1. 生成6位随机数作为验证码 SecureRandom secureRandom new SecureRandom(); String code String.valueOf(100000 secureRandom.nextInt(900000)); // 2. 将验证码存入Redis stringRedisTemplate.opsForValue().set(phone,String.valueOf(code),5, java.util.concurrent.TimeUnit.MINUTES); // 3. 发送验证码 try { logger.info(准备发送验证码,手机号{}验证码{},phone,code); return smsService.sendLoginVerificationCode(phone, code); } catch (Exception e) { logger.error(短信发送失败手机号{}错误信息{}, phone, e.getMessage(), e); return false; } } /* 方法三用户登录注册接口综合方法 */ public ResultBoolean login(LoginDto loginDto, HttpServletRequest request, HttpServletResponse response){ ResultBoolean result new Result(); // 1. 登陆方式一: 手机号短信登录 if (loginDto.getPhone() ! null loginDto.getCode() ! null){ ListUser users userMapper.selectUserByPhone(loginDto.getPhone()); if (users.isEmpty()){ createUser(loginDto); } String code stringRedisTemplate.opsForValue().get(loginDto.getPhone()); if (code ! null code.equals(loginDto.getCode())){ generateTokenByUid(users.get(0).getId(), request, response); result.setCode(200); result.setData(true); result.setMsg(success); }else{ result.setCode(999); result.setMsg(验证码错误); result.setData(false); } return result; } // 2. 登陆方式二:手机号密码登录 else if (loginDto.getUsername() ! null loginDto.getPassword() ! null){ User user userMapper.selectOne(new QueryWrapperUser().eq(username, loginDto.getUsername())); if (user ! null MD5Utils.md5HashWithSalt(loginDto.getPassword(), user.getSalt()).equals(user.getPassword())){ generateTokenByUid(user.getId(), request, response); result.setCode(200); result.setData(true); result.setMsg(success); }else{ result.setCode(999); result.setMsg(密码错误); result.setData(false); } result.setCode(999); result.setMsg(未知错误); result.setData(false); return result; } // 3. 登陆方式三用户名密码登录 else if (loginDto.getUsername() ! null || loginDto.getPassword() ! null) { User user userMapper.selectOne(new QueryWrapperUser().eq(username, loginDto.getUsername())); if (user ! null MD5Utils.md5HashWithSalt(loginDto.getPassword(), user.getSalt()).equals(user.getPassword())){ generateTokenByUid(user.getId(), request, response); result.setCode(200); result.setData(true); result.setMsg(success); }else if (user null loginDto.getPassword() ! null loginDto.getPassword2() ! null loginDto.getPassword().equals(loginDto.getPassword2())){ User newUser createUser(loginDto); generateTokenByUid(newUser.getId(), request, response); }else { result.setCode(999); result.setMsg(密码错误); result.setData(false); } } result.setCode(999); result.setMsg(未知错误); result.setData(false); return result; } /* 方法四创建新用户方法 */ public User createUser(LoginDto loginDto){ User user new User(); user.setPhone(loginDto.getPhone()); user.setStatus(0); if (loginDto.getUsername() ! null){ user.setUsername(loginDto.getUsername()); // 生成随机密码盐值字符串 String salt UUID.randomUUID().toString().replaceAll(-, ); user.setSalt(salt); user.setPassword(MD5Utils.md5HashWithSalt(loginDto.getPassword(), salt)); } user.setAvatar(https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg); user.setCreated(LocalDateTime.now()); userMapper.insert(user); return user; } /* 方法五生成 Token 方法*/ public void generateTokenByUid(Long uid, HttpServletRequest request, HttpServletResponse response){ String token JwtUtils.generateToken(uid); response.setHeader(Authorization, token); response.setHeader(Access-control-Expose-Headers, Authorization); } }