整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          按鍵精靈教程飛Q短信驗證碼范例測試模板分享

          按鍵精靈教程飛Q短信驗證碼范例測試模板分享

          豬腳本(原飛豬腳本)以按鍵精靈教學為主,涉及UiBot,Python,Lua等腳本編程語言,教學包括全自動辦公腳本,游戲輔助腳本,引流腳本,網頁腳本,安卓腳本,IOS腳本,注冊腳本,點贊腳本,閱讀腳本以及網賺腳本等各個領域。想制作腳本和學習按鍵精靈的朋友可以添加按鍵精靈學習交流群:554127455 學習路上不再孤單,金豬腳本伴你一同成長.

          答案顯然是有的,今天就給大家介紹下飛Q手機短信驗證平臺并分享下06大大基于這個平臺寫的命令庫與范例調用模板

          [tr=rgb(154,205,50)]飛Q手機短信驗證碼系統

          關于該平臺的介紹具體可以查看:

          飛q短信驗證碼官網幫助中心:http://sms.xudan123.com/help.html

          飛q短信驗證碼平臺小視頻介紹:http://www.iqiyi.com/w_19rrp70df9.html?list=19rro6qr82

          [tr=rgb(154,205,50)]模板范例下載

          天縱命令.rar (36.58 K, 下載次數:132)

          該范例有用到表格控件,請使用最新版表格控件版本導入,下載地址:

          【7月24日】按鍵2014.03版新增表格與瀏覽器控件(附例子與源碼),新版本更穩定!

          下載解壓后把天縱BASE.qml與天縱VCode.qml這倆個文件導入到命令庫,然后再導入范例就可以進行調試了

          友情免費提供測試賬號:dd8878dd 密碼:15987530(余額只有2元)

          飛Q獲取驗證碼的一般流程

          [tr=rgb(154,205,50)]范例摸板測試演示

          以搜房網賬號手動模擬注冊為例

          冊界面



          手動驗證獲取短信驗證碼


          代碼原文

          pringSecurity-9-實現通過手機短信進行認證功能

          手機短信流程分析

          手機號登錄的時候是不需要密碼登錄的,而是通過短信驗證碼實現免密登錄。具體步驟如下 :

          1. 向手機發送驗證碼,第三方短信發送平臺,如阿里云短信
          2. 手機獲取驗證碼后,在表單中輸入驗證碼
          3. 使用自定義過濾器SmsCodeValidateFilter
          4. 短信校驗通過后,使用自定義手機認證過濾器SmsCodeAuthenticationFilter校驗手機號碼是否存在
          5. 自定義SmsCodeAuthenticationToken提供給SmsCodeAuthenticationFilter
          6. 自定義SmsCodeAuthenticationProvider提供給AuthenticationManager
          7. 創建針對手機號查詢用戶信息的SmsCodeUserDetailsService,提交給SmsCodeAuthenticationProvider
          8. 自定義SmsCodeSecurityConfig配置類將上面組件連接起來
          9. SmsCodeSecurityConfig添加到LearnSrpingSecurity安全配置的過濾器鏈上

          創建短信發送接口

          • 定義發生短信的服務接口com.security.learn.sms.SmsCodeSend

          代碼如下:

          public interface SmsCodeSend {
              boolean sendSmsCode(String mobile, String code);
          }
          
          • 實現短信發生服務接口com.security.learn.sms.impl.SmsCodeSendImpl代碼如下
          @Slf4j
          public class SmsCodeSendImpl implements SmsCodeSend {
              @Override
              public boolean sendSmsCode(String mobile, String code) {
                  String sendCode = String.format("你好你的驗證碼%s,請勿泄露他人。", code);
                  log.info("向手機號" + mobile + "發送的短信為:" + sendCode);
                  return true;
              }
          }
          

          :因為這里是示例所以就沒有真正的使用第三方發送短信平臺。

          • smsCodeSend注入到容器實現如下
          @Configuration
          public class Myconfig {
              @Bean
              public PasswordEncoder passwordEncoder(){
                  return  new BCryptPasswordEncoder();
              }
              @Bean
              @ConditionalOnMissingBean(SmsCodeSend.class)
              public SmsCodeSend smsCodeSend(){
                  return  new SmsCodeSendImpl();
              }
          }
          

          手機登錄頁與發送短信驗證碼

          • 創建SmsController實現短信發送驗證碼的API,代碼如下:
          @Controller
          public class SmsController {
              public static final String SESSION_KEY = "SESSION_KEY_MOBILE_CODE";
              @RequestMapping("/mobile/page")
              public String toMobilePage(){
                  return "login-mobile";
              }
              @Autowired
              private SmsCodeSend smsCodeSend;
              /**
               *生成手機驗證碼并發送
               * @param request
               * @return
               */
              @RequestMapping("/code/mobile")
              @ResponseBody
              public String smsCode(HttpServletRequest request){
                  // 1. 生成一個手機驗證碼
                  String code = RandomStringUtils.randomNumeric(4);
                  request.getSession().setAttribute(SESSION_KEY, code);
                  String mobile = request.getParameter("mobile");
                  smsCodeSend.sendSmsCode(mobile, code);
                  return "200";
              }
          }
          
          • src\main\resources\templates文件夾下添加login-mobile.html靜態頁面,具體實現如下
          <!--suppress ALL-->
          <!DOCTYPE html>
          <html xmlns:th="http://www.thymeleaf.org">
          <head>
              <meta charset="utf-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <title>springboot葵花寶典手機登錄</title>
              <!-- Tell the browser to be responsive to screen width -->
              <meta name="viewport" content="width=device-width, initial-scale=1">
          </head>
          <body >
          <div >
              <a href="#">springboot葵花寶典手機登錄</a>  <br>
              <a th:href="@{/login/page}" href="login.html" >
                  <span>使用密碼驗證登錄</span>
              </a> <br>
              <form th:action="@{/mobile/form}" action="index.html" method="post">
                  <span>手機號碼</span> <input id="mobile" name="mobile" type="text" class="form-control" placeholder="手機號碼"><br>
                  <span>驗證碼</span> <input type="text" name="smsCode" class="form-control" placeholder="驗證碼">  <a id="sendCode" th:attr="code_url=@{/code/mobile?mobile=}" href="#"> 獲取驗證碼 </a><br>
                  <!-- 提示信息, 表達式紅線沒關系,忽略它 -->
                  <div th:if="${param.error}">
                      <span th:text="${session.SPRING_SECURITY_LAST_EXCEPTION?.message}" style="color:#ff0000"></span>
                  </div>
                  <span>記住我</span><input type="checkbox" name="remember-me-test" >  <br>
                  <button type="submit" class="btn btn-primary btn-block">登錄</button>
              </form>
          </div>
          <script th:src="@{/plugins/jquery/jquery.min.js}" src="plugins/jquery/jquery.min.js"></script>
          <script>
              // 發送驗證碼
              $("#sendCode").click(function () {
                  var mobile = $('#mobile').val().trim();
                  if(mobile == '') {
                      alert("手機號不能為空");
                      return;
                  }
                  var url = $(this).attr("code_url") + mobile;
                  $.get(url, function(data){
                      alert(data === "200" ? "發送成功": "發送失敗");
                  });
              });
          </script>
          </body>
          </html>
          
          • LearnSrpingSecurityconfigure(HttpSecurity http)方法中添加手機免密登錄允許的url
            .and().authorizeRequests()
                          .antMatchers("/login/page","/code/image","/mobile/page","/code/mobile").permitAll()
          

          短信驗證碼校驗過濾器 SmsCodeValidateFilter

          短信驗證碼的校驗過濾器,實際上和圖片驗證過濾器原理一致。都都是繼承OncePerRequestFilter實現一個Spring環境下的過濾器。@Component注解不可少其核心校驗規則如下:

          • 登錄時候手機號碼不可為空
          • 登錄時手機輸入碼不可為空
          • 登錄時輸入的短信驗證碼必須和“謎底”中的驗證碼一致
          @Component
          public class SmsCodeValidateFilter extends OncePerRequestFilter {
              @Autowired
              MyAuthenticationFailureHandler failureHandler;
              @Override
              protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
                  if (request.getRequestURI().equals("/mobile/form")
                          && request.getMethod().equalsIgnoreCase("post")) {
                      try {
                          validate(request);
                      }catch (AuthenticationException e){
                          failureHandler.onAuthenticationFailure(
                                  request,response,e);
                          return;
                      }
                  }
                  filterChain.doFilter(request,response);
              }
              private void validate(HttpServletRequest request)  {
                  //獲取session中的手機驗證碼
                  HttpSession session = request.getSession();
                  String sessionCode = (String)request.getSession().getAttribute(SmsController.SESSION_KEY);
                  // 獲取用戶輸入的驗證碼
                  String inpuCode = request.getParameter("smsCode");
                  //手機號
                  String mobileInRequest = request.getParameter("mobile");
                  if(StringUtils.isEmpty(mobileInRequest)){
                      throw new ValidateCodeException("手機號碼不能為空!");
                  }
                  if(StringUtils.isEmpty(inpuCode)){
                      throw new ValidateCodeException("短信驗證碼不能為空!");
                  }
                  if(StrUtil.isBlank(sessionCode)){
                      throw new ValidateCodeException("短信驗證碼不存在!");
                  }
                  if(!sessionCode.equalsIgnoreCase(inpuCode)){
                      throw new ValidateCodeException("輸入的短信驗證碼錯誤!");
                  }
                  session.removeAttribute(SmsController.SESSION_KEY);
              }
          }
          

          實現手機認證SmsCodeAuthenticationFilter過濾器

          創建com.security.learn.filter.SmsCodeAuthenticationFilter,仿照UsernamePassword AuthenticationFilter進行代碼實現,不過將用戶名、密碼換成手機號進行認證,短信驗證碼在此部分已經沒有用了,因為我們在SmsCodeValidateFilter已經驗證過了

          public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
              public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
              private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY ;    //請求中攜帶手機號的參數名稱
              private boolean postOnly = true;    //指定當前過濾器是否只處理POST請求
              public SmsCodeAuthenticationFilter() {
                  //指定當前過濾器處理的請求
                  super(new AntPathRequestMatcher("//mobile/form", "POST"));
              }
              public Authentication attemptAuthentication(
                      HttpServletRequest request,
                      HttpServletResponse response)
                      throws AuthenticationException {
                  if (this.postOnly && !request.getMethod().equals("POST")) {
                      throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
                  }
                  //從請求中獲取手機號碼
                  String mobile = this.obtainMobile(request);
                  if (mobile == null) {
                      mobile = "";
                  }
                  mobile = mobile.trim();
                  SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
                  this.setDetails(request, authRequest);
                  return this.getAuthenticationManager().authenticate(authRequest);
              }
              /**
               * 從從請求中獲取手機號碼
               * @param request
               * @return
               */
              protected String obtainMobile(HttpServletRequest request) {
                  return request.getParameter(this.mobileParameter);
              }
              /**
               * 將請求中的Sessionid和host主句ip放到SmsCodeAuthenticationToken中
               * @param request
               * @param authRequest
               */
              protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
                  authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
              }
              public void setMobileParameter(String mobileParameter) {
                  Assert.hasText(mobileParameter, "Username parameter must not be empty or null");
                  this.mobileParameter = mobileParameter;
              }
              public void setPostOnly(boolean postOnly) {
                  this.postOnly = postOnly;
              }
              public final String getMobileParameter() {
                  return this.mobileParameter;
              }
          }
          

          封裝手機認證Token SmsCodeAuthenticationToken

          創建com.security.learn.filter.SmsCodeAuthenticationToken,仿照UsernamePasswordAuthenticationToken進行代碼實現

          public class SmsCodeAuthenticationToken  extends AbstractAuthenticationToken {
              private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
              //存放認證信息,認證之前存放手機號,認證之后存放登錄的用戶
              private final Object principal;
              /**
               * 開始認證時,SmsCodeAuthenticationToken 接收的是手機號碼, 并且 標識未認證
               * @param mobile
               */
              public SmsCodeAuthenticationToken(String mobile) {
                  super(null);
                  this.principal = mobile;
                  this.setAuthenticated(false);
              }
              /**
               *  當認證通過后,會重新創建一個新的SmsCodeAuthenticationToken,來標識它已經認證通過,
               * @param principal 用戶信息
               * @param authorities 用戶權限
               */
              public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
                  super(authorities);
                  this.principal = principal;
                  super.setAuthenticated(true);//表示認證通過
              }
              /**
               * 在父類中是一個抽象方法,所以要實現, 但是它是密碼,而當前不需要,則直接返回null
               * @return
               */
              public Object getCredentials() {
                  return null;
              }
              /**
               * 手機號獲取
               * @return
               */
              public Object getPrincipal() {
                  return this.principal;
              }
              public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
                  if (isAuthenticated) {
                      throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
                  }
                  super.setAuthenticated(false);
              }
              public void eraseCredentials() {
                  super.eraseCredentials();
              }
          }
          

          手機認證提供者 SmsCodeAuthenticationProvider

          創建com.security.learn.filter.SmsCodeAuthenticationProvider,提供給底層的ProviderManager代碼實現如下

          public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
              @Autowired
              @Qualifier("smsCodeUserDetailsService")
              private UserDetailsService userDetailsService;
              public UserDetailsService getUserDetailsService() {
                  return userDetailsService;
              }
              public void setUserDetailsService(UserDetailsService userDetailsService) {
                  this.userDetailsService = userDetailsService;
              }
              /**
               * 處理認證:
               * 1. 通過 手機號 去數據庫查詢用戶信息(UserDeatilsService)
               * 2. 再重新構建認證信息
               * @param authentication
               * @return
               * @throws AuthenticationException
               */
              @Override
              public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                  //利用UserDetailsService獲取用戶信息,拿到用戶信息后重新組裝一個已認證的Authentication
                  SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken)authentication;
                  UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());  //根據手機號碼拿到用戶信息
                  if(user == null){
                      throw new AuthenticationServiceException("無法獲取用戶信息");
                  }
                  SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user,user.getAuthorities());
                  authenticationResult.setDetails(authenticationToken.getDetails());
                  return authenticationResult;
              }
              /**
               * AuthenticationManager挑選一個AuthenticationProvider
               * 來處理傳入進來的Token就是根據supports方法來判斷的
               * @param aClass
               * @return
               */
              @Override
              public boolean supports(Class<?> aClass) {
                  return SmsCodeAuthenticationToken.class.isAssignableFrom(aClass);
              }
          }
          

          手機號獲取用戶信息 SmsCodeUserDetailsService

          創建com.security.learn.impl.SmsCodeUserDetailsService類,不要注入PasswordEncoder

          @Slf4j
          @Component("smsCodeUserDetailsService")
          public class SmsCodeUserDetailsService implements UserDetailsService {
              @Override
              public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
                  log.info("請求的手機號是:" + mobile);
                  return new User(mobile, "", true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
              }
          }
          

          因為測試就沒有去數據庫中獲取手機號

          自定義管理認證配置 SmsCodeSecurityConfig

          最后我們將以上實現進行組裝,并將以上接口實現以配置的方式告知Spring Security。因為配置代碼比較多,所以我們單獨抽取一個關于短信驗證碼的配置類SmsCodeSecurityConfig,繼承自SecurityConfigurerAdapter。將上面定義的組件綁定起來,添加到容器中:

          注意添加@Component注解

          @Component
          public class SmsCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
              
              @Autowired
              @Qualifier("smsCodeUserDetailsService")
              private SmsCodeUserDetailsService smsCodeUserDetailsService;
              @Resource
              private SmsCodeValidateFilter  smsCodeValidateFilter;
              @Override
              public void configure(HttpSecurity http) throws Exception {
                  //創建手機校驗過濾器實例
                  SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
                  //接收 AuthenticationManager 認證管理器
                  smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
                  //處理成功handler
                  //smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
                  //處理失敗handler
                  //smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
                  smsCodeAuthenticationFilter.setRememberMeServices(http.getSharedObject(RememberMeServices.class));
                  // 獲取驗證碼提供者
                  SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
                  smsCodeAuthenticationProvider.setUserDetailsService(smsCodeUserDetailsService);
                  //在用戶密碼過濾器前面加入短信驗證碼校驗過濾器
                  http.addFilterBefore(smsCodeValidateFilter, UsernamePasswordAuthenticationFilter.class);
                  //在用戶密碼過濾器后面加入短信驗證碼認證授權過濾器
                  http.authenticationProvider(smsCodeAuthenticationProvider)
                          .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
              }
          }
          

          綁定到安全配置 LearnSrpingSecurity

          1. . 向 LearnSrpingSecurity中注入 SmsCodeValidateFilter和 SmsCodeSecurityConfig實例
          2. 將 SmsCodeValidateFilter實例添加到 UsernamePasswordAuthenticationFilter 前面
            http.csrf().disable() //禁用跨站csrf攻擊防御,后面的章節會專門講解
                          //.addFilterBefore(codeValidateFilter, UsernamePasswordAuthenticationFilter.class)
                          .addFilterBefore(smsCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)
          
          1. 在 LearnSrpingSecurity#confifigure(HttpSecurity http) 方法體最后調用 apply 添加 SmsCodeSecurityConfig
                          .and()
                          .apply(smsCodeSecurityConfig)
          

          具體實現如圖

          實現手機登錄RememberMe功能

          實現分析

          1. UsernamePasswordAuthenticationFilter過濾器中有一個RememberMeServices引用,它的父類AbstractAuthenticationProcessingFilter,提供提供的 setRememberMeServices方法。
          2. 而在實現手機短信驗證碼登錄時,我們自定了一個 MobileAuthenticationFilter 也一樣的繼承了AbstractAuthenticationProcessingFilter 它,我們只要向其 setRememberMeServices 方法手動注入一 個 RememberMeServices 實例即可。

          代碼實現

          1. com.security.learn.config.SmsCodeSecurityConfig中向SmsCodeAuthenticationFilter中注入RememberMeServices實例
           smsCodeAuthenticationFilter.setRememberMeServices(http.getSharedObject(RememberMeServices.class));
          

          • 檢查 記住我 的 input 標簽的 name="remember-me-test"
          <span>記住我</span><input type="checkbox" name="remember-me-test" >  <br>
          
          • rememberMeParameter設置from表單“自動登錄”勾選框的參數名稱。如果這里改了,from表單中checkbox的name屬性要對應的更改。如果不設置默認是remember-me。
          • rememberMeCookieName設置了保存在瀏覽器端的cookie的名稱,如果不設置默認也是remember-me。如下圖中查看瀏覽器的cookie。

          測試

          重啟項目,訪問 http://localhost:8888/mobile/page輸入手機號與驗證碼, 勾選 記住我 , 點擊登錄

          查看數據庫中中 persistent_logins 表的記錄

          關閉瀏覽器, 再重新打開瀏覽器訪問http://localhost:8888 , 發現會跳轉回用戶名密碼登錄頁,而正常應該勾選了 記住我 , 這一步應該是可以正常訪問的.

          錯誤原因

          數據庫中 username 為 手機號 1333383XXXX, 當你訪問http://localhost:8888默認RememberMeServices 是調

          用 CustomUserDetailsService 通過用戶名查詢, 而 當前在 CustomUserDetailsService 判斷了用戶名為 admin才通

          過認證, 而此時傳入的用戶名是 1333383XXXX, 所以查詢不到 1333383XXXX用戶數據

          錯誤解決方式

          數據庫中的 persistent_logins 表為什么存儲的是手機號?原因是當前在 SmsCodeUserDetailsService中返回的 User 對象中的 username 屬性設置的是手機號 mobile,而應該設置這個手機號所對應的那個用戶名. 比如當前username 的值

          @Slf4j
          @Component("smsCodeUserDetailsService")
          public class SmsCodeUserDetailsService implements UserDetailsService {
              @Override
              public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
                  log.info("請求的手機號是:" + mobile);
                  return new User("admin", "", true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
              }
          }
          

          :我們這里實際上是寫為固定admin了實際上需要通過數據庫根據手機號獲取用戶信息。

          關閉瀏覽再打開訪問http://localhost:8888就無需手動登錄認證了。因為默認采用的 CustomUserDetailsService 查詢可查詢到用戶名為 admin 的信息,即認證通過

          如果您覺得本文不錯,歡迎關注,點贊,收藏支持,您的關注是我堅持的動力!

          原創不易,轉載請注明出處,感謝支持!如果本文對您有用,歡迎轉發分享!


          主站蜘蛛池模板: 毛片无码一区二区三区a片视频| 国产精品女同一区二区久久| 一区二区三区影院| 亚洲一区中文字幕久久| 久久99国产一区二区三区| 亚洲一区二区三区亚瑟| 久久久久一区二区三区| 精品国产伦一区二区三区在线观看| 日韩经典精品无码一区| 尤物精品视频一区二区三区 | 日韩AV无码一区二区三区不卡毛片| 黄桃AV无码免费一区二区三区| 亚洲一区AV无码少妇电影| 日韩精品无码一区二区三区AV | 人妻体体内射精一区二区| 日本精品少妇一区二区三区| 久久一本一区二区三区| 久久国产免费一区| 精品国产日韩一区三区| 无码精品蜜桃一区二区三区WW| 色妞AV永久一区二区国产AV| 波多野结衣一区视频在线| 日亚毛片免费乱码不卡一区| a级午夜毛片免费一区二区| 高清一区二区三区日本久| 国产人妖在线观看一区二区| 无码人妻精品一区二区三区9厂| 精品日韩一区二区| 国偷自产一区二区免费视频| 亚洲AV无码国产一区二区三区| 一区二区国产在线播放| 中文字幕在线不卡一区二区| 视频一区视频二区在线观看| 91福利视频一区| 亚洲国产精品无码久久一区二区 | 中文字幕一区二区三区四区| 精品国产一区二区三区色欲| 99久久精品国产高清一区二区| 日本一区二区三区不卡视频| 亚洲色精品VR一区区三区| 亚洲乱码国产一区三区|