【DWR】扫一扫登陆

网上关于扫一扫登陆的地方越来越多,但是关于这个扫一扫的实现的文章却甚少,我在做这个工程之前,弄了许久都没有资料。其实这个可以用DWR来实现,DWR(Direct Web Remoting)是一个用于改善web页面与Java类交互的远程服务器端Ajax开源框架,可以帮助开发人员开发包含AJAX技术的网站。它可以允许在浏览器里的代码使用运行在WEB服务器上的JAVA函数,就像它就在浏览器里一样,拥有运行在WEB上但是不需要浏览器插件的好处。

当然,扫一扫也不是乱扫就能够登陆的,关键是你的手机有一定的用户信息,将其获取出来,送到PC端。本文是基于微信公众平台的扫一扫,只要用户利用微信里面的扫一扫功能,就能获取微信用户资料然后登陆。

首先,如何配置好DWR,已经在《【DWR】Helloworld》(点击打开链接)一文中说明,这里不再赘述如何准备好dwr.jar,放到工程里面的lib文件夹,主要是说明这个微信扫一扫登陆是如何实现的。


一、基本目标

1、首先有一个这样的页面waitforscan.jsp,里面就一个等待用户扫描的二维码,上面输出的信息只是为了调试:


2、用微信的扫一扫功能对准这个二维码一扫:


3、就会弹出授权页面,用户一点确定,手机与电脑同时跳转到登陆成功页面,登陆成功页面输出微信的所有信息。:P为了笔者的个人安全,这里就不展示那个登陆页面了~

【DWR】扫一扫登陆_第1张图片


二、基本思想

1、关键是利用Dwr与Servlet对Httpsession与ScriptSession的操作。Httpsession是打开浏览器之后的过程,用户打开浏览器到关闭浏览器的一个过程对应一个Httpsession,这样我们就能够知道到底是哪个用户的哪个浏览器需要跳转,把用户的微信信息写入哪个浏览器;ScriptSession就是当前页的ID,这样就可以知道手机扫描二维码成功之后,能够告知waitforscan.jsp需要跳转。如果用户刷新此页,则Httpsession不改变,ScriptSession改变。

2、本工程的目录结构如下,记得在Servlet与Dwr需要的包,与Oauth认证需要的json解析包,具体可以看我《【Servlet】最简单的Servlet JavaWeb程序》(点击打开链接)、《【DWR】Helloworld》(点击打开链接)与《【Servlet】基于Jsp的微信Oauth2认证》(点击打开链接):

【DWR】扫一扫登陆_第2张图片

3、数据流程图如下:


4、数据流图如下:

【DWR】扫一扫登陆_第3张图片


三、制作过程

1、Web.xml

指明/scanhandle对应wx.loginqrcode.Scanhandle这个Java,同时启动Dwr,具体还可以参考我之前的《【Servlet】最简单的Servlet JavaWeb程序》(点击打开链接)与《【DWR】Helloworld》(点击打开链接)

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>


	<!-- 我需要用到dwr技术 -->
	<servlet>
		<servlet-name>dwr-invoker</servlet-name>
		<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
		<init-param>
			<param-name>debug</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>pollAndCometEnabled</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>crossDomainSessionSecurity</param-name>
			<param-value>false</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dwr-invoker</servlet-name>
		<url-pattern>/dwr/*</url-pattern>
	</servlet-mapping>

	<!-- 微信扫二维码登陆部分-->
	<servlet>
		<servlet-name>scanhandle</servlet-name>
		<servlet-class>wx.loginqrcode.Scanhandle</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>scanhandle</servlet-name>
		<url-pattern>/scanhandle</url-pattern>
	</servlet-mapping>
	
</web-app>

2、Dwr.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"  
    "http://getahead.ltd.uk/dwr/dwr20.dtd">  
<dwr>  
    <allow>  
        <create creator="new" javascript="dwrfuc" scope="application">  
            <param name="class" value="wx.loginqrcode.Dwr"/>  
        </create>  
  </allow>  
</dwr>  

指明Dwr的处理于wx.loginqrcode.Dwr这个java中,同时以后调用dwr里面的参数则用到dwr.XX()这个方法,这里在《【DWR】Helloworld》( 点击打开链接)已经说明了。


3、Dwr.java

这个java是整个工程的核心,请比对后面的jsp页面与java进行查看

package wx.loginqrcode;

import java.util.*;
import javax.servlet.http.*;

import org.directwebremoting.*;
import org.directwebremoting.proxy.dwr.*;

public class Dwr  {
		//创造两个MAP数据结构,一个是存放标识+ScriptSession,另一个是标识+HttpSession,HttpSession要被其他java操作,所以是public
		private static Map<String, ScriptSession> scrSessionMap = new HashMap<String, ScriptSession>();
		public static Map<String, HttpSession> httpSessionMap = new HashMap<String, HttpSession>();
		//这个标识就是httpSessionId
		public void getwebindex(String httpSessionId) {
			//Dwr通过以下的两种方式取得ScriptSession与HttpSession
			ScriptSession sessions = WebContextFactory.get().getScriptSession();
			HttpSession httpSession = WebContextFactory.get().getHttpServletRequest().getSession();			
			httpSessionMap.put(httpSessionId, httpSession);
			scrSessionMap.put(httpSessionId, sessions);
			//再把HttpSession打回给waitforscan.jsp,这个HttpSession与waitforscan.jsp里面利用jsp生成的HttpSession相同
			Util utilAll = new Util(sessions);
			utilAll.addFunctionCall("recid", httpSessionId);
		}

		public static void send(String to, String msg) {
			//这里是对于Scanhandle.java传过来消息进行处理,其实无论传递过来什么,都可以告诉waitforscan.jsp要跳转,这个msg是没有意义的
			ScriptSession sessions = scrSessionMap.get(to);
			Util utilAll = new Util(sessions);
			utilAll.addFunctionCall("ifscanmsgrec", msg);
		}

}


4、waitforscan.jsp

这一页用到了Jquery与Jquery_qrcode_master插件生成二维码,所以大家对比于上面的工程目录看到我的有js文件夹,关于如何用到Jquery与Jquery_qrcode_master插件生成二维码,可以参考《【jQuery】使用jquery-qrcode插件把网址转化成二维码,手机扫一扫即可访问》(点击打开链接)这里不赘述,很简单的,注意有兼容性的判断,二维码必须在上下左右留些空位,否则,用户扫描很困难,即使你的二维码成功,手机也是很难识别的。这页,甚至一下的页面应该对照着Dwr.java查看,因为Dwr.java是这个工程的核心

<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- 首先要引入3个在src文件夹里面的js文件 -->
<script type="text/javascript" src="js/jquery-1.11.1.js"></script>
<script type="text/javascript" src="js/jquery.qrcode.js"></script>
<script type="text/javascript" src="js/qrcode.js"></script>
<!-- 我需要用到dwr技术 -->
<script type='text/javascript' src='../dwr/engine.js'></script>
<script type='text/javascript' src='../dwr/util.js'></script>
<!-- 注意这里,因为刚才在dwr.xml写了javascript="dwrfuc"一句,所以这里必须引入dwrfun.js,上面则是指定动作 -->
<script type='text/javascript' src='../dwr/interface/dwrfuc.js'></script>
<title>欢迎扫码登陆</title>
</head>
<body>
	欢迎扫码登陆:你此次会话的HttpSessionID为:<span id="debug"></span>
	<!-- 新建一个id为qrcodeTable图层给这个二维码使用 -->
	<div id="qrcodeTable" style="margin-left:50px;margin-top:50px"></div>
</body>
</html>
<!-- 再于脚本处加入如下代码即可 -->
<script>	
	//利用jsp生成一个HttpSessionID并且发送到dwr的getwebindex注册
	var httpSessionId = "<%=request.getSession().getId()%>";
	
	window.onload = function() {
		dwr.engine.setActiveReverseAjax(true);
		dwrfuc.getwebindex(httpSessionId);
	}	

	function recid(dwrmsg){
		document.getElementById("debug").innerHTML = httpSessionId;
		//用Jquery_qrcode_master插件生成二维码对于谷歌浏览器必须用到canvas这种方式,否则生成的二维码会出错,无法扫描
		//因此有了如下的二维码判断
		var isChrome = navigator.userAgent.toLowerCase().match(/chrome/) != null;
		if (!isChrome) {
			jQuery('#qrcodeTable').qrcode({
				render	: "table",
				text	: "http://192.168.0.1/wechattest/scanhandle?httpSessionId="+httpSessionId
			});
		}
		else{
			jQuery('#qrcodeTable').qrcode({
					render	: "canvas",
					text	: "http://192.168.0.1/wechattest/scanhandle?httpSessionId="+httpSessionId
				});			
		}
	}
	
	//如果被扫描了,收到dwrmsg放过来的信息,马上跳转到welcome.jsp
	function ifscanmsgrec(dwrmsg){
		window.location.href = "welcome.jsp";
	}
</script>

5、ScanHandle.java

这里部分用到了Oauth认证,请参考《【Servlet】对基于Jsp的微信Oauth2认证的改进》(点击打开链接)

package wx.loginqrcode;

//首先由于oauth.java在其他的包里面,所以我们要引入这个包
import wx.oauth.*;

//io异常需要
import java.io.*;

//servlet需要
import javax.servlet.*;
import javax.servlet.http.*;

public class Scanhandle extends HttpServlet {
	//没有下面这句eclipse会出警告
	private static final long serialVersionUID = 1L;
	//微信Oauth认证用到了get方法
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		//首先清空一下Session,以免多用户扫描时候Session冲突,然后请求httpSessionId这个参数
		request.getSession().invalidate();
		String httpSessionId = request.getParameter("httpSessionId");
		/**
		 * @param code 这是微信提供的东西code
		 * @param jsonstring 用户信息json字符串
		 */
		String code = request.getParameter("code");
		String jsonstring = null;
		//如果没有code,就向微信提供的网址请求
		//state里面还可以带上你需要拿到用户信息之后进一步处理的参数
		if (code == null) {
			/**
			 * @param appid 微信提供给你的appid
			 * @param redirect_uri 你自己的服务器地址/工程名/本页的Servelt名字
			 * @param state 把我们的httpSessionID带到state中,待Oauth认证成功之后可以取回
			 */
			response.sendRedirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=http://192.168.0.1/wechattest/scanhandle&response_type=code&scope=snsapi_userinfo&state="+ httpSessionId +"#wechat_redirect");
		} else {
			//如果扫描成功之后,需要用state参数更新httpSessionId,否则由于Java开头有String httpSessionId = request.getParameter("httpSessionId");这句,会导致httpSessionId为空
			httpSessionId = request.getParameter("state");
			//向Oauth.java拿到了用户信息之后,存到手机Session
			jsonstring = new Oauth().getUserinfo(code);
			request.getSession().setAttribute("jsonstring", jsonstring);
			//通过httpSessionId取得对应的pc_httpSession,把记录用户信息的json字符串压入这个httpSession,这样pc就能收到用户微信信息
			HttpSession pc_httpSession = Dwr.httpSessionMap.get(httpSessionId);
			pc_httpSession.setAttribute("jsonstring", jsonstring);
			//然后发信息给waitforscan.jsp告诉其跳转,这里什么信息是不重要的,因为waitforscan.jsp根本没有对这个消息进行处理
			Dwr.send(httpSessionId, jsonstring);
			//如果手机自己跳转
			request.getRequestDispatcher("loginqrcode/welcome.jsp").forward(
					request, response);
		}
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

	}
}

6、welcome.jsp

登陆成功页面,非常简单,就是取用户信息,然后利用相应的json吧将其擦写,输出,

为了避免恶意用户直接通过输入浏览器地址访问这样,进行了抛出异常处理

至于微信服务器返回的用户微信信息json字符串,可以参考其开发者文档(点击打开链接)

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ page import="com.alibaba.fastjson.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>欢迎登陆</title>
</head>
<body>
	<%
	String nickname = null;
	String headimgurl = null;
	try{
		String jsonstring = request.getSession().getAttribute("jsonstring").toString();
		JSONObject jobject = JSON.parseObject(jsonstring);
		nickname = jobject.getString("nickname");
		headimgurl = jobject.getString("headimgurl");
	}catch (Exception e) {
		out.print("请正常打开此页");		
	}
	%>
	欢迎登陆<%=nickname %>,<img src="<%=headimgurl %>" />
</body>
</html>

至此,整个利用DWR做出来的扫一扫登陆页面就完工了,可以对其进行进一步的修改,比如绑定用户的微信信息与你网站的登陆信息,然后利用用户的微信信息在数据库取出你网站的登陆信息,这样就算微信不开放扫码登陆接口给你,你也可以利用微信的Oauth认证取得用户微信登陆信息的接口,自己做一个利用微信登陆你的网站的工程,登陆接口也好像没有这个接口,仅有合作企业才有,当然,你这样也为了微信做了广告。毕竟是基于对微信的开发。

你可能感兴趣的:(servlet,DWR,微信,OAuth,扫一扫)