java中二維碼的使用(二)類qq手機(jī)的掃碼登錄功能

了解了一下掃碼登錄的原理,大致是網(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è)試

前臺(tái)返回成功信息.png
前臺(tái)返回登錄取消.png

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-

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評(píng)論 3 428
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,339評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,713評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評(píng)論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,893評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,201評(píng)論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,397評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,631評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評(píng)論 1 293
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,128評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,347評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容