Java軟件報(bào)表軟件技術(shù)博客

          java報(bào)表軟件技術(shù)匯總 java報(bào)表軟件制作 報(bào)表軟件新聞
          posts - 355, comments - 100, trackbacks - 0, articles - 3
             :: 首頁(yè) :: 新隨筆 ::  :: 聚合  :: 管理

          在使用FineReport報(bào)表系統(tǒng)中處于賬戶安全考慮有些企業(yè)希望同一賬號(hào)在任意時(shí)刻智能在統(tǒng)一客戶端登錄那么當(dāng)A用戶在C1客戶端登陸后,該賬號(hào)又在另外一個(gè)C2客戶端登陸,服務(wù)器如何取判斷呢?

          開(kāi)發(fā)原理

          當(dāng)服務(wù)器在得知A在C1登陸后,在cookie里面寫入一個(gè)標(biāo)識(shí)ID~將瀏覽器標(biāo)記,然后以后的訪問(wèn)自然就能夠根據(jù)匹配用戶名和對(duì)應(yīng)的標(biāo)記來(lái)確定這個(gè)用戶是不是在換瀏覽器登陸了,當(dāng)匹配到用戶異地登陸,就要把之前已經(jīng)登陸的用戶先登出,再登陸新請(qǐng)求的用戶。當(dāng)然關(guān)閉頁(yè)面事件里要向后臺(tái)先發(fā)送一個(gè)請(qǐng)求,后臺(tái)要記得清除改用戶標(biāo)記的緩存。

          那么客戶端怎么知道自己的賬號(hào)在異地登陸了呢?

          這個(gè)就要基于心跳了~因?yàn)槲覀兊膆ttp不是長(zhǎng)連接的,所以只能模擬了,弄一個(gè)輪詢ajax不斷的問(wèn)服務(wù)器,我是否在異地登陸,因?yàn)橹胺?wù)器任何一個(gè)賬號(hào)登陸都會(huì)又一個(gè)ID標(biāo)識(shí),那么當(dāng)接收到一個(gè)客戶端心跳時(shí),我們只要拿出里面的ID和用戶名跟保存的匹配~匹配到存在該用戶名,但是ID不對(duì),那說(shuō)明一定是另外一個(gè)客戶端登陸了這個(gè)賬號(hào)了,這個(gè)時(shí)候就告知客戶端,你的賬號(hào)已經(jīng)異地登陸,然后前端提示刷新就可以了。

          如何實(shí)現(xiàn)?

          這里要用到FineReport提供的接口,RequestInterceptor

          接口內(nèi)容

          package com.fr.stable.fun;

           

          import com.fr.stable.fun.mark.Layer;

          import com.fr.stable.fun.mark.Mutable;

          import com.fr.stable.web.RequestCMDReceiver;

           

          /**

           * Created by richie on 16/8/9.

           * 請(qǐng)求攔截器,通過(guò)傳遞op和cmd進(jìn)行內(nèi)置請(qǐng)求的攔截

           
          */


          public interface RequestInterceptor extends Mutable, RequestCMDReceiver, Layer {

           

              String MARK_STRING 
          = "RequestInterceptor";

           

              
          int CURRENT_LEVEL = 1;

          }


          相關(guān)引用類

          package com.fr.stable.web;

           

          import javax.servlet.http.HttpServletRequest;

          import javax.servlet.http.HttpServletResponse;

           

          /**

           * Created by richie on 16/8/9.

           * 請(qǐng)求接收器

           
          */


          public interface RequestCMDReceiver {

           

              
          /**

               * cmd參數(shù)值

               * 
          @return cmd參數(shù)值

               
          */


              String getCMD();

           

              
          /**

               * 執(zhí)行

               * 
          @param req http請(qǐng)求

               * 
          @param res http應(yīng)答

               * 
          @param sessionID 會(huì)話ID

               * 
          @throws Exception 處理失敗則拋出異常

               
          */


              
          void actionCMD(HttpServletRequest req, HttpServletResponse res,

                             String sessionID) 
          throws Exception;

           

              
          /**

               * 執(zhí)行請(qǐng)求

               * 
          @param req http請(qǐng)求

               * 
          @param res http響應(yīng)

               * 
          @throws Exception 處理失敗則拋出異常

               
          */


              
          void actionCMD(HttpServletRequest req, HttpServletResponse res) throws Exception;

          }


          注冊(cè)方式

          <extra-core>

             
          <RequestInterceptor class="com.fr.plugin.xxx.youclassname" op="fs_load" cmd="login" pid="com.fr.plugin.xxx.name"/>

          </extra-core>

          其中pid的值應(yīng)該和插件的id值一致,通過(guò)這樣的注冊(cè)方式,就可以使用自己定義的處理邏輯來(lái)覆蓋掉默認(rèn)的登錄驗(yàn)證請(qǐng)求。

          以上,通過(guò)故意制造報(bào)錯(cuò)的方式我們能夠看到~FR登陸請(qǐng)求都是繼承于

          com.fr.fs.web.service.FSLoadLoginAction 這個(gè)類的~、

          進(jìn)一步反編譯JAR可以看到~這個(gè)類是繼承于

          com.fr.web.core.ActionNoSessionCMD  最后實(shí)現(xiàn) ActionCMD, RequestInterceptor

          那么正好,我們的插件主類就可以免去很多自己寫,直接繼承于FSLoadLoginAction就可以用來(lái)處理所有的自定義登陸請(qǐng)求

          【凡是需要在登陸時(shí)做得事情都可以在這里做】

          當(dāng)然actionCMD(HttpServletRequest req, HttpServletResponse res)這個(gè)執(zhí)行方法還是要重寫的~

          還有就是protected void signOnSuccess(HttpServletRequest req, HttpServletResponse res, PrintWriter writer, String url)這個(gè)登陸成功之后需要做一些上面說(shuō)的操作~

          下面是兩個(gè)代碼片段,主要就是處理登陸標(biāo)記和登出清除的.

          片段1

          @Override

                  
          public void actionCMD(HttpServletRequest req, HttpServletResponse res)

                      
          throws Exception {

                          String username 
          = WebUtils.getHTTPRequestParameter(req, Constants.FR_USERNAME);

                          String heartBeat 
          = WebUtils.getHTTPRequestParameter(req, "__heartbeat__");

                          
          if(ComparatorUtils.equals(heartBeat, "__active__")){

                                  
          if(StringUtils.isEmpty(username)){

                                          username 
          = WebUtils.getHTTPRequestParameter(req, "__username__");

                                          
          if(!StringUtils.isEmpty(username)){

                                                  req.getSession(
          true).removeAttribute("__username__");

                                          }


                                  }


                                  
          //如果用戶名不為空且已登錄的列表中不包含該用戶名說(shuō)明已經(jīng)被踢下線

                                  
          if(!StringUtils.isEmpty(username) && !log.containsKey(username)){

                                          writeResult(res,
          false);

                                          
          return ;

                                  }


                                  
          //如果在已登錄的列表中找到了該用戶名的記錄,但是ID不匹配也說(shuō)明被踢下線了

                                  
          if(log.containsKey(username)){

                                          String crtUUID 
          = WebUtils.getHTTPRequestParameter(req, "_sessionid_");

                                          SingleLoginBean logBean 
          = log.get(username);

                                          String oldId 
          = logBean.getId();

                                          
          if(!ComparatorUtils.equals(crtUUID,oldId)){

                                                  writeResult(res,
          false);

                                                  
          return;

                                          }
          else{

                                                  
          //將當(dāng)前時(shí)刻設(shè)置為最近活躍時(shí)刻

                                                  logBean.setWait4removeTime(
          new Date().getTime());

                                          }


                                  }


                                  writeResult(res,
          true);

                                  
          //登出太久不活躍的用戶 30S以上

                                  checkAllUser();

                                  
          return;

                          }


                          
          super.actionCMD(req, res);

                  }


          片段2

          protected void signOnSuccess(HttpServletRequest req, HttpServletResponse res, PrintWriter writer, String url) throws IOException, JSONException {

                          String username 
          = WebUtils.getHTTPRequestParameter(req, Constants.FR_USERNAME);

                          String uuid 
          = req.getSession(true).getId();

                          SingleLoginBean logBean 
          = new SingleLoginBean(uuid,req,res,req.getSession(true));

                          logBean.setWait4removeTime(
          new Date().getTime());

                          
          //后面的用戶登錄成功后需要先將舊的用戶轉(zhuǎn)移到等待刪除的列表中

                          remove4logout(req);

                          
          //將新登錄的用戶添加到已經(jīng)登錄的用戶中

                          log.put(username, logBean);

                          
          if ("true".equals(WebUtils.getHTTPRequestParameter(req, ParameterConsts.__REDIRECT__))) {

                      res.sendRedirect(url);

                  }
           else {

                      writer.print(JSONObject.create().put(
          "url", url));

                  }


              }


          下面就是JS輪詢了

          var askServer4Active = function(){

                          var sessionid 
          = getCrtSessionid();

                          
          if( sessionid == "" || sessionid == null ){

                                  
          return ;

                          }


                          var url 
          = FR.servletURL+"?op=fs_load&cmd=login&__heartbeat__=__active__&_sessionid_="+sessionid;

                          FR.ajax(
          {  

                                  url: url,  

                                  type: 
          "POST",  

                                  dataType:
          "JSON"

                                  success: function(msg)
          {  

                                          
          if(!msg.success){

                                                  
          if(active){

                                                          active 
          = false;

                                                          clearInterval(timer);

                                                          FR.Msg.alert(
          "警告","您的賬號(hào)已在其他客戶端登陸!\n如非本人授權(quán),請(qǐng)及時(shí)修改密碼!\n3秒后頁(yè)面將跳轉(zhuǎn)至登陸頁(yè)!");

                                                          setTimeout(function()
          {

                                                                  document.location 
          = FR.servletURL+"?op=fs";

                                                          }
          ,3000);

                                                  }


                                          }
          else{

                                                  active 
          = true;

                                          }


                                  }
            

                          }
          ); 

                  }
          ;

           



          主站蜘蛛池模板: 河南省| 清徐县| 南昌市| 偃师市| 绥滨县| 论坛| 从化市| 错那县| 礼泉县| 上杭县| 邮箱| 平山县| 南阳市| 富蕴县| 河池市| 杂多县| 长白| 东宁县| 海阳市| 桂平市| 黔西县| 石泉县| 贡觉县| 北碚区| 庆安县| 商洛市| 河池市| 长海县| 灵丘县| 邛崃市| 巨野县| 华亭县| 通江县| 冷水江市| 砀山县| 宁化县| 阜康市| 永安市| 雷州市| 望都县| 南丰县|