目錄
一. 創建阿里云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服務并獲取密鑰,開通短信服務
1.1 注冊阿里云服務器
點擊鏈接,直接選擇一種登錄方式注冊登陸即可。
阿里云登錄 - 歡迎登錄阿里云,安全穩定的云計算服務平臺
?1.2 開通短信服務
搜索短信服務,點擊跳轉然后有免費開通。
如下頁面,簡單過一遍"快速學習與測試"
?走完上面的流程,我們點擊"國內消息",然后如右側頁面所示,需要去申請"資質",只要是項目集成都需要申請"資質",資質申請審核完畢;到簽名頁面添加簽名,簽名會需要使用到申請的資質,簽名創建完畢后,添加我們的短信模板,自行選擇即可,也可以自定義模板。
創建完畢之后,一定要把"簽名","短信模板Code碼"記下來,一會在代碼中會用到。
這里以我的為例,簽名是"aliyunSM666"。
短信模板Code為"SMS_481015005"
?
1.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><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.5.16</version></dependency><!-- 短信服務 SDK --><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>2.1.0</version></dependency><!-- OSS SDK --><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version></dependency>
2.2 修改 yml 文件
如下圖所示,修改項目的 yml 文件,在 yml 文件中添加 aliyun 配置。
assessKeyId 就是上面第一步提到的 AccessKey ID;
accessKeySecret 就是上面提到的 AccessKey Secret;
regionId 激素hi上面提到的 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;// 創建并初始化阿里云短信服務的客戶端實例@Beanpublic IAcsClient acsClient() {DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);return new DefaultAcsClient(profile);}// 發送短信的核心邏輯,通過 IAcsClient 調用阿里云短信服務 APIpublic 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 {@Autowiredprivate 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 ServiceImpl<UserMapper, User> implements UserService {// 添加類頂部聲明private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);@Autowiredprivate UserMapper userMapper;@Autowiredprivate SmsService smsService;@Autowiredprivate StringRedisTemplate stringRedisTemplate;// 方法一: 根據用戶名查詢用戶是否已存在@Overridepublic 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. 將驗證碼存入RedisstringRedisTemplate.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 Result<Boolean> login(LoginDto loginDto, HttpServletRequest request, HttpServletResponse response){Result<Boolean> result = new Result<>();// 1. 登陸方式一: 手機號+短信登錄if (loginDto.getPhone() != null && loginDto.getCode() != null){List<User> 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 QueryWrapper<User>().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 QueryWrapper<User>().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");}
}