登錄之手機快捷登錄(ActiveMQ+阿里大於)

ActiveMQ 簡介

ActiveMQ 是 Apache 旗下產品,是一款優秀的消息中間件。主要解決應用耦合,異步消息,流量削鋒等問題,實現高性能,高可用。

你可以把 ActiveMQ 想象成一個大的容器,首先生產者把消息發送到這個大容器中,然後消費者監聽,如果有消息就從這個大容器中消費消息,起到一個緩衝的作用。

下載並運行

下載完成後進行解壓,進入 apache-activemq-5.15.3\bin\win64 目錄(32 爲系統進入 Win32),然後點擊運行 activemq.bat。
可以通過 http://localhost:8161/ 訪問 ActiveMQ 消息管理後臺頁面
點擊 Manage ActiveMQ broker 進行登錄,用戶名和密碼均爲:admin。
登錄後可以查看 Queues 消息隊列等信息

ActiveMQ 相關配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:amq="http://activemq.apache.org/schema/core"
   xmlns:jms="http://www.springframework.org/schema/jms"
   xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/jms
    http://www.springframework.org/schema/jms/spring-jms.xsd
    http://activemq.apache.org/schema/core
    http://activemq.apache.org/schema/core/activemq-core.xsd ">

<!-- 掃瞄包-->
<context:component-scan base-package="chen.dreamland.www.activemq" />
<!-- ActiveMQ 連接工廠 -->
<!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供-->
<!-- 如果連接網絡:tcp://ip:61616;未連接網絡:tcp://localhost:61616 以及用戶名,密碼-->
<amq:connectionFactory id="amqConnectionFactory" brokerURL="tcp://localhost:61616" userName="admin" password="admin"  />

<!-- Spring Caching連接工廠 -->
<!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
    <!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->
    <property name="targetConnectionFactory" ref="amqConnectionFactory"></property>
    <!-- 同上,同理 -->
    <!-- <constructor-arg ref="amqConnectionFactory" /> -->
    <!-- Session緩存數量 -->
    <property name="sessionCacheSize" value="100" />
</bean>

<!-- Spring JmsTemplate 的消息生產者 start-->
<!-- 定義JmsTemplate的Queue類型 -->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
    <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 -->
    <constructor-arg ref="connectionFactory" />
    <!-- 非pub/sub模型(發佈/訂閱),即隊列模式 -->
    <property name="pubSubDomain" value="false" />
</bean>

<!-- Spring JmsTemplate 的消息生產者 end -->

<!-- 消息消費者 start-->

<!-- 定義Queue監聽器 -->
<jms:listener-container destination-type="queue" container-type="default" connection-factory="connectionFactory" acknowledge="auto">
    <!-- 默認註冊bean名稱,應該是類名首字母小寫  -->
    <jms:listener destination="login_msg" ref="smsAuthenCode"/>
</jms:listener-container>
<!-- 消息消費者 end -->
</beans>

主要是 ActiveMQ 的連接配置以及生產者和消費者的配置。

web.xml 引入 applicationContext-activemq.xml
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
     	 classpath*:spring-mybatis.xml,
      	 classpath*:applicationContext-redis.xml,
      	 classpath*:applicationContext-activemq.xml
    </param-value>
 </context-param>

通過 web.xml 加載剛纔配置的 applicationContext-activemq.xml。

ActiveMQ 消息監聽器的創建(消費者)

配置文件配置完成並引入後,創建消息監聽器,監聽消息的存在。
在 chen.dreamland.www 包下新建 activemq 包,在 activemq 包下新建消息監聽器類 SmsAuthenCode.java:

@Component
    public class SmsAuthenCode implements MessageListener {
    public void onMessage(Message message) {
        MapMessage mapMessage = (MapMessage) message;
        // 調用SMS服務發送短信   SmsSystem阿里大於發送短信給客戶手機實現類

        try {
            // 大於發送短信 Map 來自ActiveMQ 生成者
            SendMessage.sendMessages( mapMessage.getString("code"), 
            mapMessage.getString("telephone") );
            System.out.println( "-----發送消息成功..."+mapMessage.getString("code"));
        } catch (Exception e) {//JMS
            e.printStackTrace();
        }
    }
    }

代碼解讀如下:

(1)不知道屬於哪一層時使用 @Component 註解,將對象交給 Spring 管理;

(2)消息監聽類實現消息監聽接口 MessageListener 重寫 onMessage 方法;

(3)將消息 message 強制轉換爲 MapMessage;

(4)從 MapMessage 中取出手機驗證碼和手機號,調用發送短信的方法將短信發送給用戶,下文將對這一部分做解釋。

阿里大於發送短信類 SendMessage 的創建

創建過程主要包括以下四步。

1.首先,下載阿里大於 SDK,選擇 Java下載。

2.然後,將 aliyun-java-sdk-core-3.3.1.jar 和 aliyun-java-sdk-dysmsapi-1.0.0.jar 打包到本地 Maven 倉庫。

這兩個文件分別在解壓目錄的 java/api_sdk/aliyun-java-sdk-core 和 java/api_sdk/aliyun-java-sdk-dysmsapi 下。
打開 CMD,輸入:

mvn install:install-file -Dfile=D:\安裝包\alidayu\java\api_sdk\aliyun-java-sdk-core\aliyun-java-sdk-core-3.3.1.jar -DgroupId=chen.dreamland.www -DartifactId=dayu-sdk-core -Dversion=3.3.1 -Dpackaging=jar -DgeneratePom=true -DcreateChecksum=true

回車,再輸入下面的代碼,回車:

mvn install:install-file -Dfile=D:\安裝包\alidayu\java\api_sdk\aliyun-java-sdk-dysmsapi\aliyun-java-sdk-dysmsapi-1.0.0.jar -DgroupId=chen.dreamland.www -DartifactId=dayu-sdk-dysmsapi -Dversion=1.0.0 -Dpackaging=jar -DgeneratePom=true -DcreateChecksum=true

注意: -Dfile 是你的 jar 包所在路徑。
3.接着,在 pom.xml 中引入剛纔打包好的阿里大於依賴:

    <dependency>
        <groupId>wang.dreamland.www</groupId>
        <artifactId>dayu-sdk-core</artifactId>
        <version>3.3.1</version>
    </dependency>
    <dependency>
        <groupId>wang.dreamland.www</groupId>
        <artifactId>dayu-sdk-dysmsapi</artifactId>
        <version>1.0.0</version>
    </dependency>

groupId 對應上面的 -DgroupId,artifactId 對應上面的 -DartifactId,version 對應上面的 -Dversion。
4.最後,在 activemq 包下新建發送短信類 SendMessage.java:
public class SendMessage {
private static String accessKeyId = “你的accessKeyId”;//你的accessKeyId,參考本文檔步驟2
private static String accessKeySecret = “你的accessKeySecret”;//你的accessKeySecret,參考本文檔步驟2
private static String setSignName = “你的短信簽名名稱”;
private static String dayutemplateCode = “你的短信模板CODE”;

public static void sendMessages(String code,String phone){
    //設置超時時間-可自行調整
    System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
    System.setProperty("sun.net.client.defaultReadTimeout", "10000");
    //初始化ascClient需要的幾個參數
    final String product = "Dysmsapi";//短信API產品名稱
    final String domain = "dysmsapi.aliyuncs.com";//短信API產品域名
    //替換成你的AK
    //初始化ascClient,暫時不支持多region
    IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId,
            accessKeySecret);
    try {
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
    } catch (ClientException e) {
        e.printStackTrace();
    }
    IAcsClient acsClient = new DefaultAcsClient(profile);
    //組裝請求對象
    SendSmsRequest request = new SendSmsRequest();
    //必填:待發送手機號。支持以逗號分隔的形式進行批量調用,批量上限爲20個手機號碼,批量調用相對於單條調用及時性稍有延遲,驗證碼類型的短信推薦使用單條調用的方式
    request.setPhoneNumbers(phone);
    //必填:短信簽名-可在短信控制檯中找到
    request.setSignName(setSignName);
    //必填:短信模板-可在短信控制檯中找到
    request.setTemplateCode(dayutemplateCode);
    //可選:模板中的變量替換JSON串,如模板內容爲"親愛的${name},您的驗證碼爲${code}"時,此處的值爲
    //"{\"number\":\"" + code + "\"}"
    request.setTemplateParam("{\"code\":\"" + code + "\"}");
    //可選:outId爲提供給業務方擴展字段,最終在短信回執消息中將此值帶回給調用者
    request.setOutId("yourOutId");
    //請求失敗這裏會拋ClientException異常
    SendSmsResponse sendSmsResponse = null;
    try {
        sendSmsResponse = acsClient.getAcsResponse(request);
    } catch (ServerException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClientException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    if(sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) {
        //請求成功
    }
}
}

手機快捷登錄流程

手機快捷登錄的流程,主要包括下面幾步。

1.手機號 input 框離焦事件判斷。

  • 判斷該手機號是否合法;
  • 如果手機號合法,則判斷該手機號是否是已激活用戶。

2.獲取驗證碼點擊事件。

  • 判斷步驟1是否返回 true;
  • 返回 true 則發送獲取驗證碼並同時設置倒計時 60s;
  • 倒計時60s結束才能點擊重新獲取。

3.驗證碼 input 框離焦事件。

  • 判斷驗證碼是否爲6位純數字(防止惡意登錄),是則返回 true。

4.登錄點擊事件

  • 判斷手機號和驗證碼均返回 true 時則提交表單。
這裏主要說下獲取驗證碼點擊事件,其他的和之前的原理一樣:

//獲取驗證碼
$(function () {
var go = document.getElementById(‘go’);

    go.onclick = function (ev){
        if(!flag2){
            $("#phone_span").text("手機號碼非法或者未註冊!").css("color","red");
        }else {
            //  發送短信給用戶手機..
            // 1 發送一個HTTP請求,通知服務器 發送短信給目標用戶
            var telephone =$("input[name='telephone']").val();// 用戶輸入的手機號
                // 用戶輸入手機號校驗通過
                $("#go").attr("disabled", "disabled");
                countDown(60);

                $.ajax({
                    method: 'POST',
                    url: '${ctx}/sendSms',
                    data : {
                        telephone : telephone
                    },
                    success:function(data) {
                        var tt = data["msg"];
                        if(tt){
                             alert("發送短信成功!");

                        }else{
                            alert("發送短信出錯,請聯繫管理員");
                        }
                    }
                });
            }


        var oEvent = ev || event;
        //js阻止鏈接默認行爲,沒有停止冒泡
        oEvent.preventDefault();

    }
});

//倒計時
function countDown(s){
    if(s <= 0){
        $("#go").text("重新獲取");
        $("#go").removeAttr("disabled");
        return;
    }
    /* $("#go").val(s + "秒後重新獲取");*/
    $("#go").text(s + "秒後重新獲取");
    setTimeout("countDown("+(s-1)+")",1000);
}

代碼解讀如下:

(1) $(function () {}); 代表頁面加載完成函數;

(2)通過 document.getElementById 獲取文檔對象 DOM,賦值給 go;

(3)給 go 綁定一個 onclick 點擊事件函數;

(4)如果檢驗手機號返回的是 flag2=false,則給予錯誤提示;

(5)如果檢驗手機號成功後,通過屬性過濾選擇器獲取 input 框對象,並通過對象的 val() 方法獲取手機號;

(6)通過 $("#go").attr(“disabled”, “disabled”) 方法將獲取驗證碼按鈕設置成不可點擊;

(7)調用 countDown 倒計時方法,如果倒計時到0秒後,按鈕顯示重新獲取,並移除 disabled 屬性,按鈕可再次點擊,如果 0<s<=60 則按鈕上顯示 s 秒後重新獲取,不可點擊,每秒 s-1;

(8)發送 AJAX 請求,映射 URL 爲 /sendSms,請求參數是手機號;

(9)success 回調函數返回的結果如果是true,則提示發送成功。否則提示發送失敗,聯繫管理員;

(10)oEvent.preventDefault() 阻止鏈接默認行爲。主要是爲了返回錯誤信息時停留在手機快捷登錄標籤。

後臺創建映射 URL 爲 /sendSms 的方法,如下:

@Autowired
private UserService userService;
@Autowired// redis數據庫操作模板
private RedisTemplate<String, String> redisTemplate;// jdbcTemplate HibernateTemplate

@Autowired
@Qualifier("jmsQueueTemplate")
private JmsTemplate jmsTemplate;// mq消息模板.

/**
 * 發送手機驗證碼
 * @param model
 * @param telephone
 * @return
 */
@RequestMapping("/sendSms")
@ResponseBody
public Map<String,Object> index(Model model, @RequestParam(value = "telephone",required = false) final String telephone ) {
    Map map = new HashMap<String,Object>(  );
    try { //  發送驗證碼操作
        final String code = RandStringUtils.getCode();
        redisTemplate.opsForValue().set(telephone, code, 60, TimeUnit.SECONDS);// 60秒 有效 redis保存驗證碼
        log.debug("--------短信驗證碼爲:"+code);
        // 調用ActiveMQ jmsTemplate,發送一條消息給MQ
        jmsTemplate.send("login_msg", new MessageCreator() {
            public Message createMessage(javax.jms.Session session) throws JMSException {
                MapMessage mapMessage = session.createMapMessage();
                mapMessage.setString("telephone",telephone);
                mapMessage.setString("code", code);
                return mapMessage;
            }
        });
    } catch (Exception e) {
        map.put( "msg",false );
    }
    map.put( "msg",true );
    return map;

}

代碼解讀如下:

(1)通過 @Autowired 註解注入 UserService、RedisTemplate,通過 @Autowired 和 @Qualifier 結合注入 JmsTemplate,參數是配置文件中配置的隊列 ID,主要是爲了區分有多個類型的情況,指定注入(這裏可以不指定),比如,如果再定義一個訂閱模式就需要加 @Qualifier 註解進行指定注入:

  <bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
   <!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 -->
      <constructor-arg ref="connectionFactory" />
       <!-- pub/sub模型(發佈/訂閱) -->
       <property name="pubSubDomain" value="true" />
   </bean>

(2)通過 RandStringUtils 工具類隨機生成六位數字驗證碼。

public class RandStringUtils {
	public static String getCode(){
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 6; i++) {
			sb = sb.append( getRandomString());
		}
		return sb.toString();
	}
	
	public static int getRandomString(){
		Random r = new Random();
		int num = r.nextInt(9);
		return num;
	}
	
	public static String getPhones(String[] arr){
		String str = "";
		if (arr!=null&&arr.length >0) {
			for (int i = 0; i < arr.length; i++) {
				str = str + arr[i] + ",";
			}
		}
		return str.substring(0,str.length()-1);
	}

	public static void main(String[] args) {
        int randomString = getRandomString();
        String code = getCode();
        System.out.println(randomString);
        System.out.println(code);
    }

}

(3)將6位隨機驗證碼保存到 Redis 中,時效爲60秒,key=手機號,value=驗證碼。

(4)調用 ActiveMQ 消息模板對象生產一條消息,發送給 MQ。

注意:第一個參數 login_msg 是配置文件中配置的目的地:

<jms:listener-container destination-type="queue" container-type="default" connection-factory="connectionFactory" acknowledge="auto">
        <!-- 默認註冊bean名稱,應該是類名首字母小寫  -->
        <jms:listener destination="login_msg" ref="smsAuthenCode"/>
    </jms:listener-container>

監聽器 smsAuthenCode 監聽到消息之後就會調用發送短信功能。
第二個參數是接口 MessageCreator,這裏傳入的是匿名內部類,通過 new 接口的形式並實現接口方法。
(5)如果有異常則把 false 放入 map,否則把 true 放入 map,最後返回 map。

登錄流程

登錄流程主要包括以下幾個步驟:

  1. 用戶點擊獲取驗證碼,後臺生成6位隨機驗證碼,一份存入 Redis 數據庫中,key=“手機號”,value=“驗證碼”;

  2. ActiveMQ 生產者發送消息給 ActiveMQ 中間件,等待消費者消費;

  3. ActiveMQ 消費者監聽到消息之後發送短信給用戶;

  4. 輸入驗證碼點擊登錄;

  5. 後臺獲取用戶的手機號以及驗證碼,根據用戶的手機號去 Redis 數據庫中取對應的驗證碼,然後與用戶輸入的驗證碼進行比對,一致則登錄成功,跳轉到 personal.jsp,否則跳轉到 login.jsp。

用戶輸入手機驗證碼後點擊登錄,對應的點擊事件如下:

 //登錄
    $("#phone_btn").click(function () {

        if(checkPhone()&& checkPhoneCode()){
            // 校驗用戶名和密碼
            $("#phone_span").text("").css("color","red");
            $("#phone_form").submit();
        }else {
            alert("請輸入手機號和6位驗證碼!");
        }

    });

以上代碼用於檢驗手機號和手機驗證碼,正確後提交表單,否則提示錯誤!

修改 doLogin 方法如下:

@RequestMapping("/doLogin")
    public String doLogin(Model model, @RequestParam(value = "username",required = false) String email,
                          @RequestParam(value = "password",required = false) String password,
                          @RequestParam(value = "code",required = false) String code,
                          @RequestParam(value = "telephone",required = false) String telephone,
                          @RequestParam(value = "phone_code",required = false) String phone_code,
                          @RequestParam(value = "state",required = false) String state,
                          @RequestParam(value = "pageNum",required = false) Integer pageNum ,
                          @RequestParam(value = "pageSize",required = false) Integer pageSize) {

        //判斷是否是手機登錄
        if (StringUtils.isNotBlank(telephone)) {
            //手機登錄
            String yzm = redisTemplate.opsForValue().get( telephone );//從redis獲取驗證碼
            if(phone_code.equals(yzm)){
                //驗證碼正確
                User user = userService.findByPhone(telephone);
                getSession().setAttribute("user", user);
                model.addAttribute("user", user);
                log.info("手機快捷登錄成功");
                return "/personal/personal";

            }else {
                //驗證碼錯誤或過期
                model.addAttribute("error","phone_fail");
                return  "../login";
            }

        } else {
            //賬號登錄

        if (StringUtils.isBlank(code)) {
            model.addAttribute("error", "fail");
            return "../login";
        }
        int b = checkValidateCode(code);
        if (b == -1) {
            model.addAttribute("error", "fail");
            return "../login";
        } else if (b == 0) {
            model.addAttribute("error", "fail");
            return "../login";
        }
        password = MD5Util.encodeToHex(Constants.SALT + password);
        User user = userService.login(email, password);
        if (user != null) {
            if ("0".equals(user.getState())) {
                //未激活
                model.addAttribute("email", email);
                model.addAttribute("error", "active");
                return "../login";
            }
            log.info("用戶登錄登錄成功");
            getSession().setAttribute("user", user);
            model.addAttribute("user", user);
            return "/personal/personal";
        } else {
            log.info("用戶登錄登錄失敗");
            model.addAttribute("email", email);
            model.addAttribute("error", "fail");
            return "../login";
        }

    }

    }

代碼解讀如下:

(1)主要是根據手機號是否爲空判斷是手機登錄還是賬號登錄,如果手機號不爲空則爲手機快捷登錄;

(2)根據手機號從 Redis 中獲取手機驗證碼,如果 Redis 中的驗證碼和用戶輸入的驗證碼一致,則登錄成功,根據手機號查詢用戶,將用戶 user 存入 Session 和 model 中,返回個人中心頁面;

(3)如果不一致,說明驗證碼錯誤或者過期,將 phone_fail 添加到 model 中,並返回到登錄頁面。

最後重新啓動項目測試,手機快捷登錄成功!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章