Realize mobile phone scanning QR code login

  • Reference documentation
Realize website QR code scanning and login
http://blog.csdn.net/jiang1013nan/article/details/22651439

Realize mobile phone scanning QR code to log in
http://www.daxueit.com/article/2581.html

  • Thinking out:
1. Generate a unique code (identifier + sessionid + timestamp, symmetric encryption) and convert it into a QR code.

2. The APP scans the QR code, verifies the validity of the QR code, and informs the server that the scan is successful (the APP needs to be logged in)

3. The server receives the scan success notification (verifiable), and pushes the message (push mechanism) To the browser side, the QR code conversion status is scanned

4. At the same time, the authorization address is returned to the APP side, and the APP side jumps to the authorization interface. There are two operations: confirm and cancel. Click to transfer the unique code, token, and status to the service. Terminal

5. The server receives the authorization result and notifies the browser to change to the state.

6. The APP receives the authorization result and changes the state.

7. The QR code adds an invalidation mechanism.

  • thinking of existence
  1. The non-client scan result is a string
  ● Solution: Change the unique code to "authorization link + unique code", after the client scan, it will first verify and then jump to the authorization page; non-client scan has no verification information and Client login information, jump directly from the authorization page to the friendly prompt page, providing APP installation detection, APP startup, APP download, etc.
  2. The sessionid is directly placed in the unique code of the QR code, and encryption ensures security.
  3. The server does not maintain the relationship between the QR code and the current session, and cannot effectively verify the validity of the QR code (step 5, in the authorization request). Validation is also required)
  4. How to use temporary timestamps?
  5. Server-side user session saving mechanism
  6. Message push: browser polling, server polling, MQ, third-party push

  • Simple implementation
1. Two-dimensional code generation: QRcode, zixing relationship between the error rate, size and storage content size of the two-dimensional code that needs to be paid attention to, otherwise there may be a problem of failure of the two-dimensional code parsing
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.io.InputStream;  
import java.io.OutputStream;  

import javax.imageio.ImageIO;  

import jp.sourceforge.qrcode.QRCodeDecoder;  
import jp.sourceforge.qrcode.exception.DecodingFailedException;
import com.swetake.util.Qrcode;  
   
public class TwoDimensionCode {  
       
    /**
     * Generate QRCode image
     * @param content store the content
     * @param imgPath image path
     */
    public void encoderQRCode(String content, String imgPath) {  
        this.encoderQRCode(content, imgPath, "png", 7, null);  
    }  
       
    /**
     * Generate QRCode image
     * @param content store the content
     * @param output output stream
     */
    public void encoderQRCode(String content, OutputStream output) {  
        this.encoderQRCode(content, output, "png", 7, null);  
    }  
       
    /**
     * Generate QRCode image
     * @param content store the content
     * @param imgPath image path
     * @param imgType image type
     */
    public void encoderQRCode(String content, String imgPath, String imgType) {  
        this.encoderQRCode(content, imgPath, imgType, 7, null);  
    }  
       
    /**
     * Generate QRCode image
     * @param content store the content
     * @param output output stream
     * @param imgType image type
     */
    public void encoderQRCode(String content, OutputStream output, String imgType) {  
        this.encoderQRCode(content, output, imgType, 7, null);  
    }  
   
    /**
     * Generate QRCode image
     * @param content store the content
     * @param imgPath image path
     * @param imgType image type
     * @param size QR code size
     */
    public void encoderQRCode(String content, String imgPath, String imgType, int size,String logoPath) {  
        try {  
            BufferedImage bufImg = this.qRCodeCommon(content, imgType, size,logoPath);  
               
            File imgFile = new File(imgPath);
            if (!imgFile.exists()){
                imgFile.mkdirs();
            }
            // Generate QRCode image of QR code  
            ImageIO.write(bufImg, imgType, imgFile);  
        } catch (Exception e) {  
            e.printStackTrace ();  
        }  
    }  
   
    /**
     * Generate QRCode image
     * @param content store the content
     * @param output output stream
     * @param imgType image type
     * @param size QR code size
     */
    public void encoderQRCode(String content, OutputStream output, String imgType, int size,String logoPath) {  
        try {  
            BufferedImage bufImg = this.qRCodeCommon(content, imgType, size, logoPath);  
            // Generate QRCode image of QR code  
            ImageIO.write(bufImg, imgType, output);  
        } catch (Exception e) {  
            e.printStackTrace ();  
        }  
    }  
       
    /**
     * A public method for generating a QRCode image
     * @param content store the content
     * @param imgType image type
     * @param size QR code size
     * @return
     */
    private BufferedImage qRCodeCommon(String content, String imgType,int size,String logoPath) {  
        BufferedImage bufImg = null;  
        try {  
            Qrcode qrcodeHandler = new Qrcode();  
            // Set the error rate of QR code, optional L(7%), M(15%), Q(25%), H(30%), the higher the error rate, the less information can be stored, but The smaller the requirement for the clarity of the QR code  
            qrcodeHandler.setQrcodeErrorCorrect('M');  
            qrcodeHandler.setQrcodeEncodeMode('B');  
            // Set the size of the QR code, the value range is 1-40. The larger the value, the larger the size and the larger the information that can be stored.  
            qrcodeHandler.setQrcodeVersion(size);  
            // Get the byte array of the content, set the encoding format  
            byte[] contentBytes = content.getBytes("utf-8");  
            // size of the picture  
            int imgSize = 67 + 12 * (size - 1);  
            bufImg = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_RGB);  
            Graphics2D gs = bufImg.createGraphics();  
            // set background color  
            gs.setBackground(Color.WHITE);  
            gs.clearRect(0, 0, imgSize, imgSize);  
   
            // set image color > BLACK  
            gs.setColor(Color.BLACK);  
            // Set the offset, not setting it may cause parsing errors  
            int pixoff = 2;  
            // output content > QR code  
            if (contentBytes.length > 0 && contentBytes.length < 800) {  
                boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);  
                for (int i = 0; i < codeOut.length; i++) {  
                    for (int j = 0; j < codeOut.length; j++) {  
                        if (codeOut[j][i]) {  
                            gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3);  
                        }  
                    }  
                }  
            } else {  
                throw new Exception("QRCode content bytes length = " + contentBytes.length + " not in [0, 800].");  
            }  
            
            if(logoPath!=null){
            	File logoFile=new File(logoPath);
                if(logoFile.isFile()&&logoFile.exists()){
                	 Image logo = ImageIO.read(logoFile);//Instantiate an Image object.  
                     int widthLogo = logo.getWidth(null)>bufImg.getWidth()*2/10?(bufImg.getWidth()*2/10):logo.getWidth(null);  
                     int heightLogo = logo.getHeight(null)>bufImg.getHeight()*2/10?(bufImg.getHeight()*2/10):logo.getWidth(null);  
                     //The logo is placed in the center
                     int x = (bufImg.getWidth() - widthLogo) / 2;  
                     int y = (bufImg.getHeight() - heightLogo) / 2;  
                     gs.drawImage(logo, x, y, widthLogo, heightLogo, null);  
                }
            }
            gs.dispose();  
            bufImg.flush();  
        } catch (Exception e) {  
            e.printStackTrace ();  
        }  
        return bufImg;  
    }  
       
    /**
     * Parse QR Code (QRCode)
     * @param imgPath image path
     * @return
     */
    public String decoderQRCode(String imgPath) {  
        // QRCode QR code image file  
        File imageFile = new File(imgPath);  
        BufferedImage bufImg = null;  
        String content = null;  
        try {  
            bufImg = ImageIO.read(imageFile);  
            QRCodeDecoder decoder = new QRCodeDecoder();  
            content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");   
        } catch (IOException e) {  
            System.out.println("Error: " + e.getMessage());  
            e.printStackTrace ();  
        } catch (DecodingFailedException dfe) {  
            System.out.println("Error: " + dfe.getMessage());  
            dfe.printStackTrace();  
        }  
        return content;  
    }  
       
    /**
     * Parse QR Code (QRCode)
     * @param input input stream
     * @return
     */
    public String decoderQRCode(InputStream input) {  
        BufferedImage bufImg = null;  
        String content = null;  
        try {  
            bufImg = ImageIO.read(input);  
            QRCodeDecoder decoder = new QRCodeDecoder();  
            content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");   
        } catch (IOException e) {  
            System.out.println("Error: " + e.getMessage());  
            e.printStackTrace ();  
        } catch (DecodingFailedException dfe) {  
            System.out.println("Error: " + dfe.getMessage());  
            dfe.printStackTrace();  
        }  
        return content;  
    }    
}


2. Interface processing class: In the example, the browser is notified to use front-end polling. It is recommended to change to back-end polling and push (MQ, third-party push, etc.); in the example, the session is maintained by a custom session manager
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Controller;
import com.hec.interceptor.MySessionContext;
import com.hec.util.MethodException;
import com.hec.util.StringUtil;
import com.hec.util.qrcode.QrcodeContentUtils;
import com.hec.util.qrcode.TwoDimensionCode;
import com.opensymphony.xwork2.ActionSupport;

@Controller
public class QrcodeLoginAction extends ActionSupport{

	private static final long serialVersionUID = 1L;
	private Map<String, Object> resultMap;

	//@MethodException(remark = "", description = "Get the login QR code")
	public String show() {
		HttpServletRequest request = ServletActionContext.getRequest();
		HttpServletResponse response=ServletActionContext.getResponse();
		ServletOutputStream out = null;
        try {
        	out=response.getOutputStream();
        	MySessionContext.getSession(request.getSession().getId()).setAttribute("qrcodeStatus", 0);
            //generate QR code
            TwoDimensionCode handler = new TwoDimensionCode();
            handler.encoderQRCode(QrcodeContentUtils.uniqueEncrypUrl(request.getSession().getId()), out, "png", 14, QrcodeContentUtils.getWebPath()+"/website/skin/default/img/logo.png");
			out.flush();
		} catch (IOException e) {
			e.printStackTrace ();
		}finally{
			if(out!=null){
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace ();
				}
			}
		}
        return null;
	}

	@MethodException(remark = "", description = "QR code scan confirmation")
	public String confirm(){
		HttpServletRequest request = ServletActionContext.getRequest();
		String key=request.getParameter("key");
		String token=request.getParameter("token");
		if(StringUtil.isNotNullOrEmpty(key)){//QR code unique code
			//make sure it is logged in
			HttpSession appSession=MySessionContext.getSession(token);
			if(appSession!=null&&appSession.getAttribute("userinfo")!=null){
				resultMap=new HashMap<String, Object>();
				String mes_code=null;
				String[] array=QrcodeContentUtils.parseEncrypUniqueCode(key);
				if(array!=null){
					mes_code=QrcodeContentUtils.checkEncrypUniqueCode(array);
					if(mes_code.equals("suc")){
						//Update the QR code scan login status to scanned
						MySessionContext.getSession(array[1]).setAttribute("qrcodeStatus", 1);
					}
				}else{
					mes_code= "err_001";//key format error
				}
				resultMap.put("status", mes_code);
				return "success";
			}else{
				return "error";//Jump to the friendly prompt page
			}
		}else{
			return "error";//Jump to the friendly prompt page
		}
		
	}

	//@MethodException(remark = "", description = "QR code scanning result polling")
	public String polling(){
		HttpSession session = ServletActionContext.getRequest().getSession();
		Integer qrcodeStatus=(Integer) session.getAttribute("qrcodeStatus");
		resultMap=new HashMap<String, Object>();
		resultMap.put("qrcodeStatus", qrcodeStatus);
		return "success";
	}
	
	@MethodException(remark = "", description = "Authorize QR code login")
	public String grantAuth(){
		resultMap=new HashMap<String, Object>();
		String mes_code=null;
		HttpServletRequest request = ServletActionContext.getRequest();
		String key=request.getParameter("key");
		String token=request.getParameter("token");
		if(StringUtil.isNotNullOrEmpty(key)){
			//make sure it is logged in
			HttpSession appSession=MySessionContext.getSession(token);
			if(appSession!=null&&appSession.getAttribute("userinfo")!=null){
				String[] array=QrcodeContentUtils.parseEncrypUniqueCode(key);
				if(array!=null){
					mes_code=QrcodeContentUtils.checkEncrypUniqueCode(array);
					if(mes_code.equals("suc")){
						HttpSession pcSession=MySessionContext.getSession(array[1]);
						//Update the QR code scan login status to authorized
						pcSession.setAttribute("qrcodeStatus", 2);
						//Copy the user information in the session in the APP to the PC
						pcSession.setAttribute("userinfo", appSession.getAttribute("userinfo"));
					}
					
				}else{
					mes_code= "err_001";//key format error
				}
			}else{
				mes_code= "err_004";//The client is not logged in
			}
		}else{
			mes_code= "err_005";//The parameter key is missing
		}
		resultMap=new HashMap<String, Object>();
		resultMap.put("status", mes_code);
		return "success";
	}
	
	@MethodException(remark = "", description = "Refused to authorize QR code login")
	public String refuseAuth(){
		resultMap=new HashMap<String, Object>();
		String mes_code=null;
		HttpServletRequest request = ServletActionContext.getRequest();
		String key=request.getParameter("key");
		String token=request.getParameter("token");
		if(StringUtil.isNotNullOrEmpty(key)){
			//make sure it is logged in
			HttpSession appSession=MySessionContext.getSession(token);
			if(appSession!=null&&appSession.getAttribute("userinfo")!=null){
				String[] array=QrcodeContentUtils.parseEncrypUniqueCode(key);
				if(array!=null){
					mes_code=QrcodeContentUtils.checkEncrypUniqueCode(array);
					if(mes_code.equals("suc")){
						HttpSession pcSession=MySessionContext.getSession(array[1]);
						//Update the QR code scan login status to deny
						pcSession.setAttribute("qrcodeStatus", 3);
					}
				}else{
					mes_code= "err_001";//key format error
				}
			}else{
				mes_code= "err_004";//The client is not logged in
			}
		}else{
			mes_code= "err_005";//The parameter key is missing
		}
		resultMap=new HashMap<String, Object>();
		resultMap.put("status", mes_code);
		return "success";
	}
	
	public Map<String, Object> getResultMap() {
		return resultMap;
	}

	public void setResultMap(Map<String, Object> resultMap) {
		this.resultMap = resultMap;
	}
}


3. Custom session manager: There are session sharing problems and poor performance in the cluster environment, and a third-party cache can be introduced for management
import java.util.HashSet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionControl implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent event) {
		MySessionContext.addSession(event.getSession());
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent event) {
		HttpSession session = event.getSession();
		MySessionContext.delSession(session);
	}

}

import java.util.HashMap;

import javax.servlet.http.HttpSession;

public class MySessionContext {

	private static HashMap<String, HttpSession> sessionMap = new HashMap<String, HttpSession>();

    public static synchronized void addSession(HttpSession session) {
        if (session != null) {
        	sessionMap.put(session.getId(), session);
        }
    }

    public static synchronized void delSession(HttpSession session) {
        if (session != null) {
        	sessionMap.remove(session.getId());
        }
    }

    public static synchronized HttpSession getSession(String session_id) {
        if (session_id == null)
        	return null;
        return sessionMap.get(session_id);
    }
}


Attached is the complete code snippet

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326893302&siteId=291194637