阿里雲簡訊服務實現免密登陸
阿新 • • 發佈:2019-02-20
使用阿里雲的簡訊服務,實現免密登陸。
一. 阿里雲簡訊服務申請。
1、首先,得有個阿里雲賬號,在控制檯找到簡訊服務。
2、 在簽名管理中根據步驟添加簽名,這個之後就是你驗證碼簡訊中最開始的【】中的內容,譬如阿里雲的簡訊,【阿里雲】xxx 。申請可能稽核不通過,它會提示你原因,我一開始未通過是因為在申請說明中需要填寫你專案的網址,或者專案名稱。這個簽名後面會用到。
3、在模板管理中新增模板,上面有模板庫讓你參考,譬如,“您的驗證碼:${code},您正進行身份驗證,打死不告訴別人!”,根據自己需求可以再改改,只要不是一些敏感內容即可。通過後有個模板CODE後面會用到,SMS_字首加一串數字的那個。
二. 金鑰申請&下載簡訊傳送API的SDK
如圖:
在Access Key管理中建立,可以獲得公鑰和私鑰,之後會使用。
我使用的是JAVA的,SDK包裡包括一個aliyun-java-sdk-core包,一個alicom-dysms-api包。
下載後放入專案中,我選擇新建一個lib資料夾,然後再pom檔案中新增依賴
<!-- 阿里雲簡訊服務 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId >
<version>3.3.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/aliyun-java-sdk-core-3.3.1.jar</systemPath>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</ artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/aliyun-java-sdk-dysmsapi-1.0.0.jar</systemPath>
</dependency>
然後在配置檔案中新增阿里雲簡訊的配置,我是在全域性配置檔案application.properties中新增,
# 阿里雲簡訊
aliyun.sms.accessKey=你的
aliyun.sms.accessKeySecret=你的
aliyun.sms.template.code=SMS_你的
三. 傳送驗證碼功能
可以參考其樣例程式。
介面就不貼了。
@Service
public class SmsServiceImpl implements ISmsService, InitializingBean {
@Value("${aliyun.sms.accessKey}")
private String accessKey;
@Value("${aliyun.sms.accessKeySecret}")
private String scretKey;
@Value("${aliyun.sms.template.code}")
private String templateCode;
private IAcsClient acsClient;
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String SMS_CODE_CONTENT_PREFIX = "SMS::CODE:CONTENT::";
private static final String[] NUMS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
private static final Random random = new Random();
@Override
public ServiceResult<String> sendSms(String telephone) {
// 檢查間隔時間
String gapKey = "SMS::CODE:INTERVAL::" + telephone;
//從redis快取中通過key獲取
String result = redisTemplate.opsForValue().get(gapKey);
//獲取得到說明快取中已經有驗證碼資訊了,稍後再請求
if (result != null) {
return new ServiceResult<String>(false, "請求次數太頻繁,請稍後再試!");
}
// 否則,獲取簡訊驗證碼
String code = generateRandomSmsCode();
//簡訊模板引數
String templateParam = String.format("{\"code\": \"%s\"}", code);
// 組裝請求物件
SendSmsRequest request = new SendSmsRequest();
// 使用post提交
request.setMethod(MethodType.POST);
// 代發手機號
request.setPhoneNumbers(telephone);
// 給模板傳參
request.setTemplateParam(templateParam);
// 簡訊模板 SMS_CODE
request.setTemplateCode(templateCode);
// 簡訊簽名
request.setSignName("Orcas棧");
boolean success = false;
try {
SendSmsResponse response = acsClient.getAcsResponse(request);
//判斷sms驗證碼傳送是否成功
if ("OK".equals(response.getCode())) {
success = true;
} else {
return new ServiceResult<String>(false, "驗證碼傳送失敗!");
}
} catch (ClientException e) {
e.printStackTrace();
}
if (success) {
// 簡訊傳送成功, 存入redis快取中
redisTemplate.opsForValue().set(gapKey, code, 60, TimeUnit.SECONDS);
redisTemplate.opsForValue().set(SMS_CODE_CONTENT_PREFIX + telephone, code, 10, TimeUnit.MINUTES);
return ServiceResult.of(code);
} else {
return new ServiceResult<String>(false, "服務忙,請稍後重試!");
}
}
@Override
public String getSmsCode(String telephone) {
return this.redisTemplate.opsForValue().get(SMS_CODE_CONTENT_PREFIX + telephone);
}
@Override
public void remove(String telephone) {
this.redisTemplate.delete(SMS_CODE_CONTENT_PREFIX + telephone);
}
//
@Override
public void afterPropertiesSet() throws Exception {
// 設定超時時間
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
// 初始化ascClient
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKey, scretKey);
String product = "Dysmsapi"; // 簡訊API產品名稱,固定
String domain = "dysmsapi.aliyuncs.com"; // 簡訊API產品域名,固定
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
this.acsClient = new DefaultAcsClient(profile);
}
/**
* 6位驗證碼生成器
* @return
*/
private static String generateRandomSmsCode() {
StringBuilder sb = new StringBuilder();
//NUMS[10]
for (int i = 0; i < 6; i++) {
int index = random.nextInt(10);
sb.append(NUMS[index]);
}
return sb.toString();
}
}
Controller層:
@GetMapping("/sms/code")
@ResponseBody
public ApiResponse smsCode(@RequestParam("telephone") String telephone) {
if (!LoginUserUtil.checkTelephone(telephone)) {
return ApiResponse.ofMessage(HttpStatus.BAD_REQUEST.value(), "請輸入正確的手機號!");
}
ServiceResult<String> result = smsService.sendSms(telephone);
if (result.isSuccess()) {
return ApiResponse.ofSuccess("傳送成功!");
} else {
return ApiResponse.ofMessage(HttpStatus.BAD_REQUEST.value(), result.getMessage());
}
}
成功簡訊示例:
四. 登陸功能
使用的Spring Security管理,部分程式碼省略。
public class AuthFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
private IUserService userService;
@Autowired
private ISmsService smsService;
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String name = obtainUsername(request);
if (!Strings.isNullOrEmpty(name)) {
//使用者名稱密碼登陸
request.setAttribute("username", name);
return super.attemptAuthentication(request, response);
}
//手機登陸
String telephone = request.getParameter("telephone");
if (Strings.isNullOrEmpty(telephone) || !LoginUserUtil.checkTelephone(telephone)) {
throw new BadCredentialsException("Wrong telephone number");
}
User user = userService.findUserByTelephone(telephone);
String inputCode = request.getParameter("smsCode");
String sessionCode = smsService.getSmsCode(telephone);
if (Objects.equals(inputCode, sessionCode)) {
if (user == null) {
//手機號未註冊 註冊該使用者
user = userService.addUserByPhone(telephone);
}
return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
} else {
throw new BadCredentialsException("smsCodeError");
}
}
}
@Service
public class UserServiceImpl implements IUserService{
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Override
public User findUserByName(String userName) {
User user = userRepository.findByName(userName);
if (user == null) {
return null;
}
List<Role> roles = roleRepository.findRolesByUserId(user.getId());
if (roles == null || roles.isEmpty()) {
throw new DisabledException("許可權非法");
}
List<GrantedAuthority> authorities = new ArrayList<>();
roles.forEach(role -> authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())));
user.setAuthorityList(authorities);
return user;
}
@Override
public User findUserByTelephone(String telephone) {
User user = userRepository.findUserByPhoneNumber(telephone);
if (user == null) {
return null;
}
List<Role> roles = roleRepository.findRolesByUserId(user.getId());
if (roles == null || roles.isEmpty()) {
throw new DisabledException("許可權非法");
}
List<GrantedAuthority> authorities = new ArrayList<>();
roles.forEach(role -> authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())));
user.setAuthorityList(authorities);
return user;
}
@Override
@Transactional
public User addUserByPhone(String telephone) {
User user = new User();
user.setPhoneNumber(telephone);
user.setName(telephone.substring(0, 3) + "****" + telephone.substring(7, telephone.length()));
user.setCreatTime(new Date());
user.setLastUpdateTime(new Date());
user.setLastLoginTime(new Date());
user = userRepository.save(user);
Role role = new Role();
role.setName("USER");
role.setUserId(user.getId());
roleRepository.save(role);
user.setAuthorityList(Lists.newArrayList(new SimpleGrantedAuthority("ROLE_USER")));
return user;
}
}
其實只是想記錄下如何用阿里雲簡訊服務實現傳送驗證碼,所以後面的可以忽略啦。