了解了一下掃碼登錄的原理,大致是網(wǎng)頁(yè)傳遞一個(gè)唯一標(biāo)識(shí)到二維碼中,用戶掃二維碼的時(shí)候,會(huì)跳轉(zhuǎn)帶著這個(gè)唯一標(biāo)識(shí)和移動(dòng)端自己登錄之后信息返回到服務(wù)器中,服務(wù)器根據(jù)唯一標(biāo)識(shí)判定是否掃碼登錄、登錄超時(shí)的問題,再根據(jù)登錄信息到庫(kù)里比對(duì),成功即將頁(yè)面進(jìn)行跳轉(zhuǎn)。
實(shí)現(xiàn)過程:
1.創(chuàng)建唯一標(biāo)識(shí)存儲(chǔ)CurrentTimeBean
package com.bean;
public class CurrentTimeBean {
private String uuid;
private String timestamp;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public CurrentTimeBean() {
}
@Override
public String toString() {
return "CurrentTimeBean [uuid=" + uuid + ", timestamp=" + timestamp
+ "]";
}
}
2.指定生成二維碼的參數(shù)類QRCodeParams
package com.util;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
public class QRCodeParams {
private String txt; //二維碼內(nèi)容
private String qrCodeUrl; //二維碼網(wǎng)絡(luò)路徑
private String filePath; //二維碼生成物理路徑
private String fileName; //二維碼生成圖片名稱(包含后綴名)
private String logoPath; //logo圖片
private Integer width = 300; //二維碼寬度
private Integer height = 300; //二維碼高度
private Integer onColor = 0xFF000000; //前景色
private Integer offColor = 0xFFFFFFFF; //背景色
private Integer margin = 1; //白邊大小,取值范圍0~4
private ErrorCorrectionLevel level = ErrorCorrectionLevel.L; //二維碼容錯(cuò)率
public String getTxt() {
return txt;
}
public void setTxt(String txt) {
this.txt = txt;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Integer getWidth() {
return width;
}
public void setWidth(Integer width) {
this.width = width;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
public String getQrCodeUrl() {
return qrCodeUrl;
}
public void setQrCodeUrl(String qrCodeUrl) {
this.qrCodeUrl = qrCodeUrl;
}
public String getLogoPath() {
return logoPath;
}
public void setLogoPath(String logoPath) {
this.logoPath = logoPath;
}
public Integer getOnColor() {
return onColor;
}
public void setOnColor(Integer onColor) {
this.onColor = onColor;
}
public Integer getOffColor() {
return offColor;
}
public void setOffColor(Integer offColor) {
this.offColor = offColor;
}
public ErrorCorrectionLevel getLevel() {
return level;
}
public void setLevel(ErrorCorrectionLevel level) {
this.level = level;
}
/**
* 返回文件后綴名
* @return
*/
public String getSuffixName(){
String imgName = this.getFileName();
if(imgName != null && !"".equals(imgName)){
String suffix=fileName.substring(fileName.lastIndexOf(".")+1);
return suffix;
}
return "";
}
public Integer getMargin() {
return margin;
}
public void setMargin(Integer margin) {
this.margin = margin;
}
}
二維碼生成工具類QRCodeUtil1
package com.util;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import com.exception.QRParamsException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
public class QRCodeUtil1 {
private static int width = 300; //二維碼寬度
private static int height = 300; //二維碼高度
private static int onColor = 0xFF000000; //前景色
private static int offColor = 0xFFFFFFFF; //背景色
private static int margin = 1; //白邊大小,取值范圍0~4
private static ErrorCorrectionLevel level = ErrorCorrectionLevel.L; //二維碼容錯(cuò)率
/**
* 生成二維碼
* @param params
* QRCodeParams屬性:txt、fileName、filePath不得為空;
* @throws QRParamsException
*/
public static void generateQRImage(QRCodeParams params)throws QRParamsException{
if(params == null
|| params.getFileName() == null
|| params.getFilePath() == null
|| params.getTxt() == null){
throw new QRParamsException("參數(shù)錯(cuò)誤");
}
try{
initData(params);
String imgPath = params.getFilePath();
String imgName = params.getFileName();
String txt = params.getTxt();
if(params.getLogoPath() != null && !"".equals(params.getLogoPath().trim())){
generateQRImage(txt, params.getLogoPath(), imgPath, imgName, params.getSuffixName());
}else{
generateQRImage(txt, imgPath, imgName, params.getSuffixName());
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 生成不帶logo的二維碼
* @param txt //二維碼內(nèi)容
* @param imgPath //二維碼保存物理路徑
* @param imgName //二維碼文件名稱
* @param suffix //圖片后綴名
*/
public static void generateQRImage(String txt, String imgPath, String imgName, String suffix){
File filePath = new File(imgPath);
if(!filePath.exists()){
filePath.mkdirs();
}
File imageFile = new File(imgPath,imgName);
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
// 指定糾錯(cuò)等級(jí)
hints.put(EncodeHintType.ERROR_CORRECTION, level);
// 指定編碼格式
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.MARGIN, margin); //設(shè)置白邊
try {
MatrixToImageConfig config = new MatrixToImageConfig(onColor, offColor);
BitMatrix bitMatrix = new MultiFormatWriter().encode(txt,BarcodeFormat.QR_CODE, width, height, hints);
// bitMatrix = deleteWhite(bitMatrix);
MatrixToImageWriter.writeToPath(bitMatrix, suffix, imageFile.toPath(), config);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成帶logo的二維碼圖片
* @param txt //二維碼內(nèi)容
* @param logoPath //logo絕對(duì)物理路徑
* @param imgPath //二維碼保存絕對(duì)物理路徑
* @param imgName //二維碼文件名稱
* @param suffix //圖片后綴名
* @throws Exception
*/
public static void generateQRImage(String txt, String logoPath, String imgPath, String imgName, String suffix) throws Exception{
File filePath = new File(imgPath);
if(!filePath.exists()){
filePath.mkdirs();
}
if(imgPath.endsWith("/")){
imgPath += imgName;
}else{
imgPath += "/"+imgName;
}
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
hints.put(EncodeHintType.ERROR_CORRECTION, level);
hints.put(EncodeHintType.MARGIN, margin); //設(shè)置白邊
BitMatrix bitMatrix = new MultiFormatWriter().encode(txt, BarcodeFormat.QR_CODE, width, height, hints);
File qrcodeFile = new File(imgPath);
writeToFile(bitMatrix, suffix, qrcodeFile, logoPath);
}
/**
*
* @param matrix 二維碼矩陣相關(guān)
* @param format 二維碼圖片格式
* @param file 二維碼圖片文件
* @param logoPath logo路徑
* @throws IOException
*/
public static void writeToFile(BitMatrix matrix,String format,File file,String logoPath) throws IOException {
BufferedImage image = toBufferedImage(matrix);
Graphics2D gs = image.createGraphics();
int ratioWidth = image.getWidth()*2/10;
int ratioHeight = image.getHeight()*2/10;
//載入logo
Image img = ImageIO.read(new File(logoPath));
int logoWidth = img.getWidth(null)>ratioWidth?ratioWidth:img.getWidth(null);
int logoHeight = img.getHeight(null)>ratioHeight?ratioHeight:img.getHeight(null);
int x = (image.getWidth() - logoWidth) / 2;
int y = (image.getHeight() - logoHeight) / 2;
gs.drawImage(img, x, y, logoWidth, logoHeight, null);
gs.setColor(Color.black);
gs.setBackground(Color.WHITE);
gs.dispose();
img.flush();
if(!ImageIO.write(image, format, file)){
throw new IOException("Could not write an image of format " + format + " to " + file);
}
}
public static BufferedImage toBufferedImage(BitMatrix matrix){
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for(int x=0;x<width;x++){
for(int y=0;y<height;y++){
image.setRGB(x, y, matrix.get(x, y) ? onColor : offColor);
}
}
return image;
}
public static BitMatrix deleteWhite(BitMatrix matrix){
int[] rec = matrix.getEnclosingRectangle();
int resWidth = rec[2] + 1;
int resHeight = rec[3] + 1;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
resMatrix.clear();
for (int i = 0; i < resWidth; i++) {
for (int j = 0; j < resHeight; j++) {
if (matrix.get(i + rec[0], j + rec[1]))
resMatrix.set(i, j);
}
}
return resMatrix;
}
private static void initData(QRCodeParams params){
if(params.getWidth() != null){
width = params.getWidth();
}
if(params.getHeight() != null){
height = params.getHeight();
}
if(params.getOnColor() != null){
onColor = params.getOnColor();
}
if(params.getOffColor() != null){
offColor = params.getOffColor();
}
if(params.getLevel() != null){
level = params.getLevel();
}
}
public static void main(String[] args) {
QRCodeParams params = new QRCodeParams();
params.setFileName("qrcode2.jpg");
params.setTxt("榆次丶吳彥祖");
params.setFilePath("E:\\");
// params.setLogoPath("E:\\qrcode1.jpg");帶logo圖片的二維碼參數(shù)
try {
generateQRImage(params);
} catch (QRParamsException e) {
e.printStackTrace();
}
}
}
3.對(duì)稱加密工具類EncryptionUtil ,用于加密二維碼中的唯一標(biāo)識(shí),
package com.security;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* 對(duì)稱加密aes工具類
* @author sxycw
*
*/
public class EncryptionUtil {
// 16進(jìn)制的字符數(shù)組
private final static String[] hexDigits =
{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
/**
* AES 加密
*
* @param content 需要加密的內(nèi)容
* @param aesKey 加密密鑰
* @return
*/
public static byte[] AESEncrypt(String content, String aesKey) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(aesKey.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 創(chuàng)建密碼器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param content 待解密內(nèi)容
* @param aesKey 解密密鑰
* @return
*/
public static byte[] AESDecrypt(byte[] content, String aesKey) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(aesKey.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 創(chuàng)建密碼器
cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(content);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 將二進(jìn)制轉(zhuǎn)換成16進(jìn)制
*/
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 將16進(jìn)制轉(zhuǎn)換為二進(jìn)制
*/
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
//byte轉(zhuǎn)16進(jìn)制
private static String byteArrayToHexString(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte tem : bytes) {
stringBuilder.append(byteToHexString(tem));
}
return stringBuilder.toString();
}
//16進(jìn)制轉(zhuǎn)byte[]
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* 加密方法
* @param content 明文
* @param aesKey 密鑰
* @return
*/
public static String FuncEncrypt(String content,String aesKey){
return parseByte2HexStr(AESEncrypt(content, aesKey));
}
/**
* 解密方法
* @param encrypt 密文
* @param aesKey 密鑰
* @return
* @throws UnsupportedEncodingException
*/
public static String FuncDecode(String encrypt,String aesKey) throws UnsupportedEncodingException{
return new String(AESDecrypt(parseHexStr2Byte(encrypt),aesKey),"utf-8");
}
public static void main(String[] args) throws UnsupportedEncodingException {
String content = "你爸爸";
String aesKey = "key";
System.out.println("加密后的>>>"+FuncEncrypt(content, aesKey));
System.out.println("解密后的>>>"+FuncDecode(FuncEncrypt(content, aesKey), aesKey));
}
}
設(shè)定密鑰級(jí)別枚舉類Secretkey
package com.Enum;
import java.util.HashMap;
/**
*
* @author sxycw
* 密鑰枚舉
*/
public enum Secretkey {
SecurityLevel_Low(0),
SecurityLevel_Middle(1),
SecurityLevel_High(2),
SecurityLevel_Top(3);
private int code;
private static HashMap<Integer, String> nameMap;
Secretkey(int code) {
this.code = code;
}
static {
nameMap = new HashMap<Integer, String>();
nameMap.put(0, "admin");
nameMap.put(1, "huangxiaoming");
nameMap.put(2, "wuyanzu");
nameMap.put(3, "wenshao");
}
public static String getName(Secretkey key) {
return getName(key.code);
}
public static String getName(int code) {
if (nameMap.containsKey(code)) {
return nameMap.get(code);
} else {
return null;
}
}
public String getName() {
return getName(code);
}
public String toString() {
return String.valueOf(code);
}
public int getCode() {
return code;
}
}
4.二維碼唯一標(biāo)識(shí)緩存模型QrCodeCache
package com.cache;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 二維碼緩存模型
* @author sxycw
* @param <T>
*/
public final class QrCodeCache<K,V>{
private final Lock lock = new ReentrantLock();
//設(shè)置最大緩存是
private final int maxCapacity;
//邊界池
private final Map<K,V> eden;
//持久池
private final Map<K,V> longterm;
/**
* 構(gòu)造方法
* @param maxCapacity
*/
public QrCodeCache(int maxCapacity) {
this.maxCapacity = maxCapacity;
this.eden = new ConcurrentHashMap<K,V>(maxCapacity);
this.longterm = new WeakHashMap<K,V>(maxCapacity);
}
/**
* 根據(jù)key取緩存值
* @param k
* @return
*/
public V get(K k) {
V v = this.eden.get(k);
if (v == null) {
lock.lock();
try{
v = this.longterm.get(k);
}finally{
lock.unlock();
}
if (v != null) {
this.eden.put(k, v);
}
}
return v;
}
/**
* 根據(jù)key存緩存值
* @param k
* @param v
*/
public void put(K k, V v) {
if (this.eden.size() >= maxCapacity) {
lock.lock();
try{
this.longterm.putAll(this.eden);
}finally{
lock.unlock();
}
this.eden.clear();
}
this.eden.put(k, v);
}
/**
* 根據(jù)key刪除緩存
* @param key
* @return
*/
public boolean remove(K k){
try{
if(eden.containsKey(k)) {
eden.remove(k);
}
return true;
}
catch(Exception ex){
return false;
}
}
}
5.本地生成的二維碼推流到瀏覽器ViewController
package com.controller;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller()
@RequestMapping("/view")
public class ViewController {
/**
* 得到本地二維碼圖片方法
* @param request
* @param response
*/
@RequestMapping("/getQrCodeView")
void getQrCodeView(HttpServletRequest request,HttpServletResponse response){
String imgUrl = request.getParameter("imgUrl");
//圖片完整的名稱
String baseUrl = imgUrl+".jpg";
//根目錄
String basePath = "E:\\";
//最終的圖片路徑
String path = basePath+baseUrl;//E:\\1501033727694_f448a295-b2f1-4eb5-a10b-1d2069ed1a45.jpg
//開啟io
FileInputStream fis;
OutputStream os;
try {
//讀取圖片
fis = new FileInputStream(path);
//讀到圖片
int i = fis.available();
//byte數(shù)組存儲(chǔ)pic字節(jié)
byte[] buff = new byte[i];
String sb = new String(buff,"utf-8");
System.out.println(sb);
fis.read(buff);
fis.close();
//寫到outputstream
response.setCharacterEncoding("utf-8");
os = new BufferedOutputStream(response.getOutputStream());
os.write(buff);
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.創(chuàng)建UserController,是否登錄的方法判定
package com.controller;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.Enum.Secretkey;
import com.alibaba.fastjson.JSON;
import com.bean.CurrentTimeBean;
import com.cache.QrCodeCache;
import com.exception.QRParamsException;
import com.security.EncryptionUtil;
import com.util.QRCodeParams;
import com.util.QRCodeUtil1;
@Controller
@RequestMapping("/u")
public class UserController {
//登錄超時(shí)時(shí)間
private static final int LONG_TIME_WAIT = 30000;//30s
//模擬登陸緩存
private QrCodeCache<String,String> qrCache = new QrCodeCache<String, String>(20);
//加密級(jí)別
private Secretkey scKey = Secretkey.SecurityLevel_High;
/**
* 二維碼確認(rèn)登錄方法
* @param request
* @param response
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping(value="/qrCodeLogin",method = RequestMethod.POST,
produces = "text/json;charset=UTF-8")
@ResponseBody
String qrCodeLogin(HttpServletRequest request,HttpServletResponse response)
throws UnsupportedEncodingException{
//執(zhí)行開始時(shí)間
long inTime = new Date().getTime();
//得到唯一標(biāo)識(shí)
String uuid = EncryptionUtil.FuncDecode(request.getParameter("uuid"), scKey.getName());
//是否登錄標(biāo)識(shí)
String isLogin = request.getParameter("isLogin");
Boolean bool = true;
int i = 0;
while (bool) {
synchronized(this){
try {
i++;
Thread.sleep(500);
System.out.println(">>>正在進(jìn)行"+i+"次輪詢");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String uidCache = (String) qrCache.get(uuid);
if(uidCache!=null){//若存在
String resInfo = null;
System.out.println(">>>登錄標(biāo)識(shí)"+isLogin);
//是否登錄
if("true".equals(isLogin)){
System.out.println("-------登錄成功------");
resInfo = "登錄成功,正在跳轉(zhuǎn)...";
}else if("false".equals(isLogin)){
resInfo = "登錄已取消";
}
//刪除緩存
qrCache.remove(uuid);
return "{\"success\":\"true\",\"info\":\""+resInfo+"\"}";
}else{//不存在
if(new Date().getTime() - inTime > LONG_TIME_WAIT){//超時(shí)
bool = false;
//刪除緩存
qrCache.remove(uuid);
System.out.println("-------請(qǐng)求超時(shí)------");
}
}
}
return "{\"success\":\"false\",\"info\":\"請(qǐng)求超時(shí),請(qǐng)重試!\"}";
}
/**
* 開啟二維碼登錄
* @return
* @throws QRParamsException
*/
@RequestMapping("/Login")
@ResponseBody
String Login() throws QRParamsException {
//生成隨機(jī)編碼
String uid = UUID.randomUUID().toString();
String ctm = String.valueOf(System.currentTimeMillis());
//設(shè)置二維碼參數(shù)
QRCodeParams params = new QRCodeParams();
params.setFileName(ctm+"_"+uid+".jpg");
params.setFilePath("e:\\");
//跳轉(zhuǎn)頁(yè)面加入二維碼中
params.setTxt("http://192.168.100.151:8080/SpMVC/rest/view/qrToLogin"
+ "?uuid="+EncryptionUtil.FuncEncrypt(uid, scKey.getName()));
//啟用二維碼工具類
QRCodeUtil1.generateQRImage(params);
//時(shí)間戳實(shí)體
CurrentTimeBean ctBean = new CurrentTimeBean();
ctBean.setTimestamp(ctm);
ctBean.setUuid(uid);
/*
* 添加緩存模擬登錄狀態(tài)
*/
qrCache.put(uid, uid);
return JSON.toJSONString(ctBean);
}
}
7.需要的兩個(gè)頁(yè)面
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>
<%
String path = request.getContextPath();//
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path;
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="<%=basePath %>/static/js/jquery.min.js"></script>
<script type="text/javascript" src="<%=basePath %>/static/js/jquery.qrcode.min.js"></script>
<title>主頁(yè)</title>
</head>
<script type="text/javascript">
function qrCode(){
var imgUrl = "";
var span = "";
$.ajax({
url:"<%=basePath %>/rest/u/Login",
dataType:"json",
type:"post",
success:function(data){
imgUrl = data.timestamp+"_"+data.uuid;
span += "<img alt='image' src='<%=basePath %>/rest/view/getQrCodeView?imgUrl="+imgUrl+"'/>";
$('#qrcode').html(span);
}
});
}
</script>
<body>
<input id="clickme" type="button" onclick="qrCode()" value="掃碼登錄!"/>
<div id="qrcode"></div>
</body>
</html>
toQrCodeLogin.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>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path;
String uuid = request.getParameter("uuid");
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="<%=basePath %>/static/js/jquery.min.js"></script>
<title>登錄確認(rèn)頁(yè)面</title>
</head>
<script type="text/javascript">
var uuid = "<%=uuid%>";
function verifyLogin(isLogin){
$.ajax({
url:"<%=basePath %>/rest/u/qrCodeLogin",
data:{uuid:uuid,isLogin:isLogin},
dataType:"json",
type:"post",
success:function(data){
console.log(data);
if(data.success == 'true'){
alert(data.info);
if(data.info == '登錄成功,正在跳轉(zhuǎn)...'){
window.location="<%=basePath %>/rest/";
}
}
}
});
}
</script>
<body>
<input type="button" value="確認(rèn)登陸" onclick="verifyLogin(true)">
<input type="button" value="取消" onclick="verifyLogin(false)">
</body>
</html>
8.測(cè)試
9.留存的問題
這個(gè)寫的還有問題,一個(gè)問題是緩存模型的監(jiān)控,需要再寫一個(gè)計(jì)時(shí)緩存監(jiān)控二維碼標(biāo)識(shí)的緩存模型,在超時(shí)后刪除對(duì)應(yīng)的鍵值對(duì);另一個(gè)問題是在實(shí)際的生產(chǎn)過程中,如果是手機(jī)登錄網(wǎng)頁(yè),用戶掃碼之后需要將用戶的id和二維碼唯一標(biāo)識(shí)綁定傳給服務(wù)器,再次傳入該用戶id前一個(gè)沒有做登錄的唯一標(biāo)識(shí)刪除;如果是網(wǎng)頁(yè)登錄手機(jī),也應(yīng)該是一樣的,還需要將用戶的信息一并寫到二維碼中;還有個(gè)輪詢的問題,不想看了改天再研究(看心情)。
10.感慨
響應(yīng)式頁(yè)面是個(gè)好東西啊,有空看看h5把。
-end-