思路:
- 当用户打开页面时,在页面端产生一个随机字符串并以二维码的形式显示在页面中,接着将这个生成的随机字符串保存在服务器端的数据库中
- 手机客户端扫码后,解析出二维码中的随机字符串,之后将这个随机字符串连同手机上的账号及密码一同上传到服务器;服务器判断用户名和密码在数据库中是否存在,如果存在,就将用户名(也可以是其它字段)保存到所扫二维码中随机字符串所对应的记录中
- 在页面生成随机二维码的同时,该页面需不停地(每隔1秒或2秒)轮询数据库,通过判断当前随机字符串所在记录的用户名字段是否有值,如果没有则继续轮询;如果有值,则登录成功,就可以查询用户表获取该用户的信息并打开内容页面显示了
实现:
第一步:按照如下代码创建一个名为tb_login的数据库:
CREATE TABLE `tb_login` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`randdata` varchar(10) DEFAULT NULL,
`name` varchar(12) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
第二步:创建一个名为QRCodeServer的J2EE项目,拷贝MySQL的JDBC驱动到该项目中并加入类路径,然后按如下代码所示提供一个操作MySQL的工作类:
public class JDBCUtils {
private static String url="jdbc:mysql://localhost:3306/test";//连接字符串
private static String name = "root"; //用户名
private static String pass = "root"; //密码
static {// 1、加载驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, name, pass);
}
public static void free(ResultSet rs, Statement stmt, Connection conn) {
try { // 建议采用这种形式来释放资源,因为finally里面的一定会被释放
if (rs != null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null)
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
第三步:在QRCodeServer项目中,依次创建如下三个Servlet来实现具体的服务器端业务。
- SaveRandData:将产生的随机数保存到数据库中。
@WebServlet("/SaveRandData")
public class SaveRandData extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException {
String randdata = request.getParameter("randdata");
try {
Connection conn = JDBCUtils.getConnection();
String sql = "insert into tb_login (randdata) values (?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, randdata);
ps.executeUpdate();
JDBCUtils.free(null, ps, conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
doGet(request, response);
}
}
- QueryName:查询指定随机字符串所对应的用户名字段是否有值,此处为了突出重点,没有再在用户表中比对用户名。
@WebServlet("/QueryName")
public class QueryName extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException {
PrintWriter out = response.getWriter();
String randData = request.getParameter("randdata");
try {
Connection conn = JDBCUtils.getConnection();
String sql = "select name from tb_login where randdata = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, randData);
ResultSet rs = ps.executeQuery();
if (rs != null && rs.next()) {
String name = rs.getString(1);
if (name != null)
out.write("OK");
}
JDBCUtils.free(rs, ps, conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException {
doGet(request, response);
}
}
- CustomScanner:当手机客户端扫描二维码后将用户名传递过来后,将其保存在数据库中。
@WebServlet("/CustomScanner")
public class CustomScanner extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException {
PrintWriter out = response.getWriter();
String name = request.getParameter("name");
String randData = request.getParameter("randData");
try {
Connection conn = JDBCUtils.getConnection();
String sql = "update tb_login set name = ? where randdata= ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
ps.setString(2, randData);
int res = ps.executeUpdate();
if(res >0 ){
out.write("ok");
}else {
out.write("not ok");
}
JDBCUtils.free(null, ps, conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException {
doGet(request, response);
}
}
第四步:创建一个名为index.jsp的文件,
<%@ 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=ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="js/jquery.qrcode.min.js"></script>
<style type="text/css">
body{background:#D2D460;text-align:center;}
div{width:778px;margin:0 auto;background:#fff;text-align:left;}
</style>
</head>
<body >
<input type="hidden" name="randdata" value="" />
<div>
<br>
<div id="QRCode" style="margin-left: inherit;"></div>
<br><br>
<button id="btnQen">生成英文的code</button>
</div>
<script type="text/javascript">
var num = "";
$(function() {
$("#btnQen").click(function() { //生成二维码
num = "";
for (var i = 0; i < 10; i++)
num += Math.floor(Math.random() * 10);
$.ajax({ //将JavaScript产生的随机数存放到数据库中
url : "SaveRandData",
type : "post",
data : "randdata=" + num
});
$("#QRCode").qrcode(num); //任意字符串
});
});
function fun() { //移动端扫码登录成功后,重定向到内容页面index.html
$.ajax({
url : "QueryName",
type : "post",
data : "randdata=" + num,
success : function(res) {
if(res == "OK")
window.location.href="index.html";
}
});
};
setInterval("fun();", 13000);//轮询查询数据库
</script>
</body>
</html>
上面生成二维码时,使用的是jquery和qrcode插件,所以需要在项目中加入这两个js文件,具体放在webContent下的js文件夹中。其实生成二维码是时也可以使用别的网站提供的工具,比如借助联图网http://www.liantu.com/的"http://qr.topscan.com/api.php?text=随机数",此时只需要在index.jsp页面的body标签中加入如下代码即可:
<img src="http://qr.topscan.com/api.php?text=9876543" width="400px" />
第五步:创建一个Android项目,将QRCode的类库加到项目中,然后在功能清单文件的相应位置处中加入如下内容:
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.INTERNET"/>
<activity
android:name="com.zxing.activity.CaptureActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
第六步:在主布局文件中提供一个onClick属性值为scanQRCodeLogin的Button控件,当用户单击这个按扭时,打开二维码扫描器,扫描成功后,自动打开内容页面,最终MainActivity类的代码如下所示:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void scanQRCodeLogin(View view) {
Intent intent = new Intent(this, CaptureActivity.class);
startActivityForResult(intent, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
String randData = data.getStringExtra("result");
final String url = "http://192.168.20.60:8080/QRCodeServer
/CustomScanner?name=1234&randData="+randData;
Log.i("TAG", url + " **");
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) (new
URL(url).openConnection());
conn.setRequestMethod("GET");
conn.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
运行程序:先将服务器启动起来,打index.jsp,单击其中的按钮,生成一个二维码;然后运行Android项目到真机中,单击界面中的按钮打开二维码扫描器,一旦扫描成功,网页就会自动打开内容页面。