Inicio de sesión de código de escaneo simple

origen

Porque el profesor es obligatorio escanear el código QR para iniciar sesión. A pedido de mi compañero de cuarto, por la presente hidrograma.

tren de pensamiento

Cuando la computadora inicie sesión, se generará uno globalmente único id. Y almacena idla información en el código QR. El servidor también idalmacenará javax.websocket.Sessionla suma en mapél.
Cuando el teléfono móvil escanee el código, saltará a una página web especial. La página web idenvía automáticamente la información del usuario al servidor juntos. El servidor idse encuentra según javax.websocket.Session. Al javax.websocket.Sessionenviar información del usuario al navegador de la computadora y liberarla.

Por supuesto que no importa aquí websocket. websocketEl beneficio es la comunicación dúplex. El servidor puede enviar una solicitud al navegador. De lo contrario, solo puede utilizar jsel servidor de sondeo.

el código

extremo posterior

UUIDWebSocketAbstraí una interfaz por mí mismo, lo cual es conveniente para el reemplazo y la implementación en cualquier momento.
@OnMessageEs el método llamado cuando se envía el mensaje.
@OnCloseEs el método llamado cuando se cierra la conexión.


Si no está interesado, puede omitirlo directamente.

Aquí se utiliza una rotación mappara realizar la invalidación del código QR. ¿Por qué no simplemente agregar un campo de fecha? Para agregar un campo de fecha, debe agregar un temporizador para cada uno. Suena a bajo rendimiento. ¿Podría decir que se juzga si ha expirado al escanear el código? Esto tampoco funciona. Si alguien simplemente inicia sesión sin escanear el código, desperdiciará memoria.

La rotación aquí mapsolo necesita un temporizador, que tiene tanto rendimiento como tamaño. La única desventaja es que el período de validez es un intervalo, no fijo.

mapHay dos dentro de la rotación map. Solo se insertará cuando se inserte newMap. Eliminar solo eliminará oldMap, luego oldMape newMapintercambiará.

@Component
@ServerEndpoint(QR_SOCKET)
public class QRCodeWebSocket implements UUIDWebSocket {
    
    
    public QRCodeWebSocket() {
    
    
        new Timer().scheduleAtFixedRate(new TimerTask() {
    
    
            @Override
            public void run() {
    
    
                uuid2session.clear();
                session2uuid.clear();
            }
            // 二维码有效期5到10分钟
        }, 0, 300_000);
    }

    // 轮换map
    private static class RotationMap<T, R> {
    
    
        private Map<T, R> newMap = new ConcurrentHashMap<>(), oldMap = new ConcurrentHashMap<>();

        R get(T t) {
    
    
            return newMap.containsKey(t) ? newMap.get(t) : oldMap.get(t);
        }

        void put(T t, R r) {
    
    
            newMap.put(t, r);
        }

        void clear() {
    
    
            oldMap.clear();
            Map map = oldMap;
            oldMap = newMap;
            newMap = map;
        }

        void remove(T t) {
    
    
            if (newMap.containsKey(t)) {
    
    
                newMap.remove(t);
            } else
                oldMap.remove(t);
        }

    }

    final static RotationMap<String, Session> uuid2session = new RotationMap<>();
    final static RotationMap<Session, String> session2uuid = new RotationMap<>();

    @OnMessage
    @Override
    public void onMessage(String uuid, Session session) {
    
    
        uuid2session.put(uuid, session);
        session2uuid.put(session, uuid);
    }

    @Override
    public boolean callback(String uuid, String data) {
    
    
        Session session = uuid2session.get(uuid);
        if (session != null) {
    
    
            try {
    
    
                session.getBasicRemote().sendText(data);
                session.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    @OnClose
    @Override
    public void onClose(Session session) {
    
    
        String uuid = session2uuid.get(session);
        if (uuid != null) {
    
    
            uuid2session.remove(uuid);
            session2uuid.remove(session);
        }
    }
}

Configure también unbean

@Configuration
public class WebSocketConfig {
    
    

    /**
     * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
     * 会导致测试不通过。应该是测试环境没配全的原因
     * 打包时直接跳过测试
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
    
    
        return new ServerEndpointExporter();
    }

}

Interfaz de escaneo de código

@Controller
public class QRCodeController {
    
    
    ThemeConfig themeConfig;
    UUIDWebSocket QRWebSocket;

    public QRCodeController(ThemeConfig themeConfig, UUIDWebSocket QRWebSocket) {
    
    
        this.themeConfig = themeConfig;
        this.QRWebSocket = QRWebSocket;
    }

    // 回调
    @GetMapping(QR_LOGIN + "/{uuid}")
    String viewQRCode(@PathVariable("uuid") String uuid, HttpSession session, Model model) {
    
    
        User user = (User) session.getAttribute(USR);
        if (user != null) {
    
    
            QRWebSocket.callback(uuid, objectToString(user));
            return themeConfig.render(USER_INDEX);
        } else {
    
    
            user=new User();
            user.setId(-1);
            QRWebSocket.callback(uuid, objectToString(user));
            model.addAttribute(MSG, "请先登录");
            return themeConfig.render(LOGIN);
        }
    }

}

Al escanear el código en el teléfono móvil, entrará en viewQRCodela función.
Primero, session.getAttribute(USR);obtenga la información del usuario en el teléfono móvil.
De lo contrario, llame al terminal móvil para iniciar sesión. Y devolver uno idnegativo e inválido a la computadora user;
si lo hay, QRWebSocket.callbackdevolver la información del usuariouser

código de front-end

[[${QR_SOCKET}]]La espera aquí está thymeleafen la plantilla, así que no entres en ella.
layerEstá en la biblioteca, no se preocupe por eso
chain, y json2formno se preocupe por la función de la herramienta que escribió usted mismo.
El significado del siguiente código es establecer una WebSocketcomunicación.
Al regresar el usuario idestá-1 reportando un error.
Cuando tenga éxito, visite la interfaz de inicio de sesión y envíele la información del usuario. Si
el inicio de sesión es exitoso, saltará a la página de inicio del usuario.

// 向后端发送一个websocket连接请求
let ws = new WebSocket('ws://' + root_path + '[[${QR_SOCKET}]]');
ws.onmessage = function (event) {
    
    
    let data = JSON.parse(event.data);
    console.dir(data)
    if (data.id != -1) {
    
    
        chain({
    
    
            url: '[[${LOGIN}]]',
            method: 'post',
            data: json2form(data)
        }).then(
            () => {
    
    
                window.location.href = "[[${USER_INDEX}]]"
            }
        );
    } else {
    
    
        alert('扫码失败');
    }
}

function connect() {
    
    
    ws.send('[[${uuid}]]');
}

function qr_login() {
    
    
    layer.open({
    
    
        type: 1
        , area: ['300px', '300px']
        , title: '扫码登录'
        , anim: 1
        , content: '<center><img style="width: 100%" src="//api.pwmqr.com/qrcode/create/?url=http://' + root_path +
            '[[@{|${QR_LOGIN}/${uuid}|}]]"></center>'
    });
    connect();
}

Caja de herramientas

const root_path = "[[${ #httpServletRequest.getServerName() + ':' + #request.getServerPort()  + #request.getContextPath() } ]]"

function json2form(json) {
    
    
    let ks = Object.keys(json), i = 0;
    ks.forEach((ele, i, arr) => arr[i] += "=" + json[arr[i]]);
    return ks.join("&");
}

function chain(option) {
    
    
    return new Promise((resolve, reject) => {
    
    
        axios(option).then(r => resolve(r.data)).catch(e => reject(e));
    });
}

Supongo que te gusta

Origin blog.csdn.net/qq_45256489/article/details/121106487
Recomendado
Clasificación