1. 程式人生 > >阿里雲簡訊服務實現免密登陸

阿里雲簡訊服務實現免密登陸

使用阿里雲的簡訊服務,實現免密登陸。

一. 阿里雲簡訊服務申請。

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;
    }


}

其實只是想記錄下如何用阿里雲簡訊服務實現傳送驗證碼,所以後面的可以忽略啦。