前言
《DWR实现后台推送消息到Web页面》一文中已对DWR作了简介,并列出了集成步骤。本文中再一次使用到DWR,用以实现扫一扫登录功能。
业务场景
web端首页点击“登陆”按钮,弹出二维码,用户进入企业号应用后点击“扫一扫”按钮,扫描二维码后web端自动跳转到已登录界面。
主要技术
- DWR —— 后台调用前端JS实现comet技术
- 微信JSSDK —— 实现扫一扫功能
主要流程
备注
- 为了达到点击登陆按钮弹出二维码窗口时即刷新二维码,所以选择用iframe装载二维码;
- 为了控制指定浏览器跳转页面,所以后台必须要记录浏览器与服务器之间的httpSession实例;
- DWR自身不保存scriptSession和httpSession的关系,所以需要自己保存其对应关系;
实现步骤
1、导入jar包(若用maven则添加依赖关系)
地址: http://directwebremoting.org/dwr/downloads/index.html
2、编写ScriptSessionListener
public class DWRScriptSessionListener implements ScriptSessionListener { //维护一个Map key为ScriptSession的Id, value为HttpSession对象 public static final MaphttpMap = new HashMap (); /** * ScriptSession创建事件 */ public void sessionCreated(ScriptSessionEvent event) { WebContext webContext = WebContextFactory.get(); HttpSession httpSession = webContext.getSession(); ScriptSession scriptSession = event.getSession(); Map httpMapObj = new HashMap<>(); httpMapObj.put("HttpSession", httpSession); httpMapObj.put("HttpServletRequest", webContext.getHttpServletRequest()); httpMapObj.put("HttpServletResponse", webContext.getHttpServletResponse()); httpMap.put(scriptSession.getId(), httpMapObj); System.out.println("httpSession: " + httpSession.getId() + " scriptSession: " + scriptSession.getId() + "is created!"); //创建连接后触发前端保存ScriptSessionId DwrUtil t = new DwrUtil(); List args = new ArrayList(); args.add(scriptSession.getId()); t.invokeJavascriptFunctionBySessionId(scriptSession.getId(), "saveScriptSessionId", args); } /** * ScriptSession销毁事件 */ public void sessionDestroyed(ScriptSessionEvent event) { ScriptSession scriptSession = event.getSession(); Map httpMapObj = httpMap.remove(scriptSession.getId()); // 移除scriptSession HttpSession httpSession = (HttpSession)httpMapObj.get("HttpSession"); // httpMap.remove(scriptSession.getId()); //移除scriptSession System. out.println( "httpSession: " + httpSession.getId() + " scriptSession: " + scriptSession.getId() + "is destroyed!"); } }
3、编写ScriptSessionManager
public class DWRScriptSessionManager extends DefaultScriptSessionManager { public DWRScriptSessionManager() { // 绑定一个ScriptSession增加销毁事件的监听器 this.addScriptSessionListener(new DWRScriptSessionListener()); System.out.println("bind DWRScriptSessionListener"); } }
4、在web.xml中增加ScriptSessionManager 的配置
<servlet> <servlet-name>dwr-invokerservlet-name> <servlet-class> org.directwebremoting.servlet.DwrServlet servlet-class> <init-param> <param-name>pollAndCometEnabledparam-name> <param-value>trueparam-value> init-param> <init-param> <param-name >org.directwebremoting.extend.ScriptSessionManager param-name> <param-value >com.gzkit.service.dwr.DWRScriptSessionManager param-value> init-param> servlet> <servlet-mapping> <servlet-name>dwr-invokerservlet-name> <url-pattern>/dwr/*url-pattern> servlet-mapping>
5、业务代码
/** * 扫一扫登录 * @author:LiuZhuoJun * @Description: 如果登录成功则触发前端页面跳转,如果不成功则返回错误信息 * @param account 用户账号 * @param scriptSessionId * @return * @date:2016年11月4日 */ @RequestMapping(value = "scanLogin") @ResponseBody public JkReturnJson scanLogin(String account, String scriptSessionId) { JkReturnJson jkReturnJson = new JkReturnJson(); if(Utils.isBlank(account) || Utils.isBlank(scriptSessionId)){ jkReturnJson.setStatusCode(ConstantsErrCode.JK_PARAM_ERR); jkReturnJson.setStatusMsg(ConstantsErrCode.JK_PARAM_ERR_MSG); jkReturnJson.setUserMsg("account、scriptSessionId字段为必需"); return jkReturnJson; } /* 从httpMap中获取对应的httpSession */ Map httpMapObj = DWRScriptSessionListener.httpMap.get(scriptSessionId); if(httpMapObj == null){ jkReturnJson.setStatusCode(ConstantsErrCode.BUSSINESS_ERR); jkReturnJson.setStatusMsg("httpSession不存在"); jkReturnJson.setUserMsg("二维码已失效,请刷新页面"); return jkReturnJson; } HttpSession httpSession = (HttpSession)httpMapObj.get("HttpSession"); HttpServletRequest httpServletRequest = (HttpServletRequest)httpMapObj.get("HttpServletRequest"); /* 根据传入的account获取对应用户对象 */ /* 此处代码省略 */ /* 将用户对象设入httpSession中 */ httpSession.setAttribute(ResourceUtil.LOCAL_CLINET_USER, user); /* 登录成功触发前端跳转页面 */ DwrUtil t = new DwrUtil(); List args = new ArrayList(); t.invokeJavascriptFunctionBySessionId(scriptSessionId,"loginSuccess",args); jkReturnJson.setUserMsg("登录成功"); return jkReturnJson; }
6、web首页
6.1 首页二维码iframe结构
<div class="modal fade" id="qrcodeModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog" style="top: 100px; width:360px" > <div class="modal-content" style="width: 360px; height:450px"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×span><span class="sr-only">Closespan>button> <h4 class="modal-title text-center " id="myModalLabel">扫码登陆h4> div> <div class="modal-body"> <iframe id="qrcodeframe" width="100%" height="270px" frameborder="0">iframe> div> <div class="modal-footer"> <p class="text-center">请关注电建宝应用并使用"扫一扫"登陆电建宝管理后台p> div> div> div> div>
6.2 二维码页面
注意要引入/dwr/engine.js和/dwr/util.js才能实现页面与后台的DWR连接。(js是动态生成的,无需导入js文件)
<body> <div id="qrcode" style="margin: auto auto; width: 250px;">div> <script type='text/javascript' src='/dwr/engine.js'>script> <script type='text/javascript' src='/dwr/util.js'>script> <script type="text/javascript" src="/plug-in/mobile/third/qrcode/jquery.qrcode.min.js">script> <script type="text/javascript" src="/webpage/homePage/js/qrcode.js">script> body>
6.3 二维码页面js
$(function(){ initDwr(); //设置每隔5分钟刷新二维码 setInterval("reloadPage()",5*60*1000); }); //初始化dwr function initDwr(){ dwr.engine.setActiveReverseAjax(true); } //保存scriptSessionId function saveScriptSessionId(scriptSessionId){ $("#qrcode").html(""); $("#qrcode").qrcode({width: 250,height: 250,text: scriptSessionId}); } //登录成功操作 function loginSuccess(){ window.parent.location.href = "/loginController.do?login"; } //刷新页面 function reloadPage(){ location.reload(); }
7、企业号调用微信SDK实现扫一扫功能
代码略
相关链接:
DWR官网
http://directwebremoting.org/dwr/
DWR入门讲解(前端)
http://directwebremoting.org/dwr/introduction/getting-started.html
DWR入门讲解(后台)
http://directwebremoting.org/dwr/documentation/server/javaapi.html
DWR下载地址
http://directwebremoting.org/dwr/downloads/index.html
DWR JavaDoc
http://directwebremoting.org/dwr/javadoc/
微信JSSDK
http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS-SDK%E6%8E%A5%E5%8F%A3
附录一 DwrUtil.java
public class DwrUtil { /** * 调用页面javascript函数 * @param functionName * @param args */ public void invokeJavascriptFunction (String _funcName, List _args){ final String funcName = _funcName; final List args = _args; Browser.withAllSessions(new Runnable(){ private ScriptBuffer script = new ScriptBuffer(); public void run(){ //拼接javascript script = script.appendScript(funcName+"("); for(int i=0; i){ if(i != 0){ script = script.appendScript(","); } script = script.appendData(args.get(i)); } script.appendScript(")"); //System.out.println(script.toString()); Collection sessions = Browser.getTargetSessions(); for (ScriptSession scriptSession : sessions){ scriptSession.addScript(script); } } }); } public void invokeJavascriptFunctionBySessionId (String sessionId, String _funcName, List _args){ final String funcName = _funcName; final List args = _args; Browser.withSession(sessionId, new Runnable(){ private ScriptBuffer script = new ScriptBuffer(); public void run(){ //拼接javascript script = script.appendScript(funcName+"("); for(int i=0; i ){ if(i != 0){ script = script.appendScript(","); } script = script.appendData(args.get(i)); } script.appendScript(")"); //System.out.println(script.toString()); Collection sessions = Browser.getTargetSessions(); for (ScriptSession scriptSession : sessions){ scriptSession.addScript(script); } } }); } }
附录二 服务器使用nginx导致DWR的js无法加载的解决办法
nginx默认是开启代理缓冲的,而DWR的js是动态生成的,无法缓冲。要解决dwr js加载问题需在nginx配置中增加以下语句
proxy_buffering off;