SpringBoot實(shí)現(xiàn)短信發(fā)送及手機(jī)驗(yàn)證碼登錄
一、短信發(fā)送
1.1 阿里云短信服務(wù)
短信服務(wù)_企業(yè)短信營銷推廣_驗(yàn)證碼通知-阿里云
也可以在下面這個(gè)地方查看短信服務(wù)
1.1.1 設(shè)置短信簽名
短信簽名就是短信發(fā)送者的署名,表示發(fā)送方的身份
1.1.2 模板管理
申請(qǐng)下來之后可以點(diǎn)擊詳情進(jìn)行查看
其中模板CODE是自動(dòng)生成的,不用管,重點(diǎn)是模板的內(nèi)容
1.1.3 設(shè)置AccessKey
創(chuàng)建新的用戶
勾選上之后,我們?cè)诰幊檀a中就能使用
當(dāng)我們創(chuàng)建用戶成功后,就生成了一對(duì)AccessKey,即AccessKey ID(用戶名) 與AccessKey Secret(密碼)
很多人在這里的時(shí)候不小心沒截圖或者沒保存就關(guān)了,丟失了AccessKey,但是不要緊,還可以再次創(chuàng)建
還有就是如果別人知道了我們的AccessKey,那別人使用的時(shí)候會(huì)就花我們的錢。我們也可以把泄露的AccessKey禁用
之后再新增授權(quán)。這次授權(quán)的意思就是僅僅授予有關(guān)短信服務(wù)的,即是我們泄露了,別人也只能操作短信服務(wù),對(duì)我們的影響很小。
1.2 短信發(fā)送——代碼開發(fā)
參照官方文檔即可
1.2.1 導(dǎo)入maven坐標(biāo)
<dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.5.16</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-dysmsapi</artifactId> <version>2.1.0</version> </dependency>
1.2.2 調(diào)用API
/** * 短信發(fā)送工具類 */ public class SMSUtils { /** * 發(fā)送短信 * @param signName 簽名 * @param templateCode 模板 * @param phoneNumbers 手機(jī)號(hào) * @param param 參數(shù) */ public static void sendMessage(String signName, String templateCode,String phoneNumbers,String param){ DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "LTAI5tHRxs2FeCu5JcJTGbm2", "v0H4PaJpXSwNr6XChtlVYAgmQWgKRA"); IAcsClient client = new DefaultAcsClient(profile); SendSmsRequest request = new SendSmsRequest(); request.setSysRegionId("cn-hangzhou"); // 要發(fā)送給那個(gè)人的電話號(hào)碼 request.setPhoneNumbers(phoneNumbers); // 我們?cè)诎⒗镌圃O(shè)置的簽名 request.setSignName(signName); // 我們?cè)诎⒗镌圃O(shè)置的模板 request.setTemplateCode(templateCode); // 在設(shè)置模板的時(shí)候有一個(gè)占位符 request.setTemplateParam("{\"code\":\""+param+"\"}"); // request.setPhoneNumbers("1368846****");//接收短信的手機(jī)號(hào)碼 // request.setSignName("阿里云");//短信簽名名稱 // request.setTemplateCode("SMS_20933****");//短信模板CODE // request.setTemplateParam("張三");//短信模板變量對(duì)應(yīng)的實(shí)際值 try { SendSmsResponse response = client.getAcsResponse(request); System.out.println("短信發(fā)送成功"); }catch (ClientException e) { e.printStackTrace(); } } }
1.3 手機(jī)驗(yàn)證碼登錄
- 方便、快捷、無需注冊(cè)、直接登陸
- 使用短信驗(yàn)證碼作為登錄憑證,無序記憶密碼
- 安全
登錄流程: 輸入手機(jī)號(hào) -> 獲取驗(yàn)證碼 -> 輸入驗(yàn)證碼 -> 點(diǎn)擊登錄 -> 登陸成功
注意: 通過手機(jī)驗(yàn)證碼登錄,手機(jī)號(hào)是區(qū)分不同用戶的標(biāo)識(shí)
1.3.1 用戶數(shù)據(jù)庫表
因?yàn)槭峭ㄟ^手機(jī)和驗(yàn)證碼登錄的,所以沒有用戶名和密碼字段
1.3.2 修改過濾器
在寫代碼之前記得要在過濾器中定義不需要處理的請(qǐng)求路徑/user/sendMsg和/user/login
然后訪問路徑: http://localhost:8080/front/page/login.html
/** * 檢查用戶是否已經(jīng)完成登錄 * 過濾器與攔截器的區(qū)別:Filter對(duì)所有訪問進(jìn)行增強(qiáng)(在Tomcat服務(wù)器進(jìn)行配置),Interceptor僅針對(duì)SpringMVC的訪問進(jìn)行增強(qiáng) */ @Slf4j @WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*") //urlPatterns指定攔截哪些路徑 public class LoginCheckFilter implements Filter { // 此對(duì)象的作用:路徑匹配器, 匹配路徑時(shí)支持通配符 public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // servletRequest向下強(qiáng)制類型轉(zhuǎn)換 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; //1. 獲取本次請(qǐng)求的URI( URI:請(qǐng)求的資源路徑) String requestURI = request.getRequestURI(); log.info("攔截到請(qǐng)求:{}", request.getRequestURI()); // 定義不用處理的請(qǐng)求路徑 String[] urls = new String[]{ "/employee/login", "/employee/logout", "/backend/**", "/front/**", "/common/**", "/user/sendMsg", "/user/login" }; //2. 判斷本次請(qǐng)求是否需要處理(因?yàn)橛行┱?qǐng)求并不需要用戶登錄) boolean check = check(requestURI, urls); //3.如果不需要處理,則直接放行 if (check) { log.info("本次請(qǐng)求{}不需要處理", request.getRequestURI()); filterChain.doFilter(request, response); return; } //4-1.判斷登錄狀態(tài),如果已登錄,則直接放行.從session中獲取用戶,如果獲取到說明已經(jīng)登錄 if (request.getSession().getAttribute("employee") != null) { log.info("用戶已登錄,用戶id為{}", request.getSession().getAttribute("employee")); Long empId = (Long) request.getSession().getAttribute("employee"); BaseContext.setCurrentId(empId); filterChain.doFilter(request, response); return; } //4-2.判斷登錄狀態(tài),如果已登錄,則直接放行.從session中獲取用戶,如果獲取到說明已經(jīng)登錄 if (request.getSession().getAttribute("user") != null) { log.info("用戶已登錄,用戶id為{}", request.getSession().getAttribute("user")); Long userId = (Long) request.getSession().getAttribute("user"); BaseContext.setCurrentId(userId); filterChain.doFilter(request, response); return; } //5.如果未登錄則返回未登錄結(jié)果 log.info("資源路徑路徑:{},用戶未登錄{}", request.getRequestURI(), request.getSession().getAttribute("employee")); // 通過輸出流的方式向客戶端響應(yīng)數(shù)據(jù) (為什么要返回這個(gè)NOTLOGIN? 因?yàn)榍岸诵枰@個(gè)來進(jìn)行判定是否登錄) response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN"))); // filterChain.doFilter(request, response); 加上這個(gè)就無法實(shí)現(xiàn) } /** * 檢查本次請(qǐng)求是否需要放行 * * @param requestURI 請(qǐng)求的資源路徑 * @param urls 放過的路徑 * @return true 放行 */ public boolean check(String requestURI, String[] urls) { for (String url : urls) { boolean match = PATH_MATCHER.match(url, requestURI); if (match) { // 放行 return true; } } return false; } }
1.3.3 隨機(jī)生成驗(yàn)證碼的工具類
/** * 隨機(jī)生成驗(yàn)證碼工具類 */ public class ValidateCodeUtils { /** * 隨機(jī)生成驗(yàn)證碼 * @param length 長度為4位或者6位 * @return */ public static Integer generateValidateCode(int length){ Integer code =null; // 長度為4 if(length == 4){ code = new Random().nextInt(9999);//生成隨機(jī)數(shù),最大為9999 if(code < 1000){ code = code + 1000;//保證隨機(jī)數(shù)為4位數(shù)字 } // 長度為6 }else if(length == 6){ code = new Random().nextInt(999999);//生成隨機(jī)數(shù),最大為999999 if(code < 100000){ code = code + 100000;//保證隨機(jī)數(shù)為6位數(shù)字 } // 其他情況 }else{ throw new RuntimeException("只能生成4位或6位數(shù)字驗(yàn)證碼"); } return code; } /** * 隨機(jī)生成指定長度字符串驗(yàn)證碼 * @param length 長度 * @return */ public static String generateValidateCode4String(int length){ Random rdm = new Random(); String hash1 = Integer.toHexString(rdm.nextInt()); String capstr = hash1.substring(0, length); return capstr; } }
1.3.4 手機(jī)驗(yàn)證碼登錄-- 發(fā)送驗(yàn)證碼
兩次ajax請(qǐng)求:
1. 登錄頁面輸入手機(jī)號(hào),點(diǎn)擊【獲取驗(yàn)證碼】按鈕,頁面發(fā)送ajax請(qǐng)求,在服務(wù)端調(diào)用短信服務(wù)API給指定手機(jī)號(hào)發(fā)送驗(yàn)證碼短信
2. 在登錄頁面輸入驗(yàn)證碼,點(diǎn)擊【登錄】按鈕,發(fā)送ajax請(qǐng)求,在服務(wù)端處理登錄請(qǐng)求
@PostMapping("/sendMsg") public R<String> sendMsg(@RequestBody User user, HttpSession session){ // 1.獲取手機(jī)號(hào) String phone = user.getPhone(); if(StringUtils.isEmpty(phone)){ return R.error("短信發(fā)送失敗"); } // 2.隨機(jī)生成四位驗(yàn)證碼 String code = ValidateCodeUtils.generateValidateCode(4).toString(); // 3.調(diào)用阿里云提供的短信服務(wù) SMSUtils.sendMessage("張靖奇","",phone,code); // 4.需要將生成的驗(yàn)證碼保存到session中 session.setAttribute(phone,code); return R.success("驗(yàn)證碼短信發(fā)送成功"); }
1.3.5 手機(jī)驗(yàn)證碼登錄-- 驗(yàn)證驗(yàn)證碼
// 其實(shí)傳過來的phone:xxxx,code:xxx 也可以用map集合接收 @PostMapping("/login") public R<User> login(@RequestBody Map map, HttpSession session) { log.info(map.toString()); // 1. 獲取手機(jī)號(hào) String phone = map.get("phone").toString(); // 2. 獲取驗(yàn)證碼 String code = map.get("code").toString(); // 3. 從Session中獲取保存的驗(yàn)證碼 Object codeInSession = session.getAttribute(phone); // 4. 進(jìn)行驗(yàn)證碼比對(duì)(頁面提交的驗(yàn)證碼和Session中保存的驗(yàn)證碼比對(duì)) if (codeInSession != null && codeInSession.equals(code)) { // 5.對(duì)比成功,說明登錄成功 LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getPhone,phone); User user = userService.getOne(queryWrapper); if (user==null){ // 6. 如果新用戶,自動(dòng)注冊(cè) user = new User(); user.setPhone(phone); user.setStatus(1); userService.save(user); } session.setAttribute("user",user.getId()); return R.success(user); } return R.error("登錄失敗"); }
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)短信發(fā)送及手機(jī)驗(yàn)證碼登錄的文章就介紹到這了,更多相關(guān)SpringBoot 手機(jī)驗(yàn)證碼登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目jar和war打包部署方式詳解
這篇文章主要為大家介紹了SpringBoot項(xiàng)目jar和war打包部署方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09JVM內(nèi)存結(jié)構(gòu)劃分實(shí)例解析
這篇文章主要介紹了JVM內(nèi)存結(jié)構(gòu)劃分實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12OpenTelemetry?Java?SDK?高級(jí)用法解析
這篇文章主要介紹了OpenTelemetry?Java?SDK?的高級(jí)用法示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02java生成圖片驗(yàn)證碼返回base64圖片信息方式
這篇文章主要介紹了java生成圖片驗(yàn)證碼返回base64圖片信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08MyBatisPlus-QueryWrapper多條件查詢及修改方式
這篇文章主要介紹了MyBatisPlus-QueryWrapper多條件查詢及修改方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06PropertiesLoaderUtils 出現(xiàn)中文亂碼的解決方式
這篇文章主要介紹了PropertiesLoaderUtils 出現(xiàn)中文亂碼的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08