シンプルなスキャンコードログイン

起因

教師はログインするために QR コードをスキャンする必要があるためです。ルームメイトのリクエストで、ハイドログラフを作成します。

一連の考え

コンピュータがログインすると、グローバルに一意のものが生成されますidそしてid情報をQRコードに保存します。サーバーは合計もそこにid保存します携帯電話がコードをスキャンすると、特別な Web ページにジャンプします。ウェブページはユーザー情報を一緒に自動的にサーバーに送信します。サーバーはに従って見つかりますユーザー情報をコンピュータ上のブラウザに送信し、公開することによって。javax.websocket.Sessionmap
ididjavax.websocket.Sessionjavax.websocket.Session

もちろんここでは関係ありませんwebsocketwebsocket利点は二重通信です。サーバーはブラウザにリクエストを送信できます。jsそれ以外の場合は、ポーリング サーバーのみを使用できます。

コード

後部

UUIDWebSocketインターフェースを自分で抽象化したので、いつでも置き換えや実装ができて便利です。
@OnMessageメッセージの送信時に呼び出されるメソッドです
@OnClose接続が閉じられたときに呼び出されるメソッドです


興味がない場合は、直接スキップしてください。

ここでは回転を利用してmapQRコードの無効化を実現しています。なぜ日付フィールドを追加しないのでしょうか? 日付フィールドを追加するには、それぞれにタイマーを追加する必要があります。パフォーマンスが悪いように聞こえます。コードをスキャンするときに有効期限が切れているかどうかを判断していると言えるでしょうか。これも機能しません。コードをスキャンせずにログインするだけだと、メモリが無駄に消費されます。

ここでの回転にはmap、パフォーマンスとサイズの両方を備えたタイマーのみが必要です。唯一の欠点は、有効期間が一定ではなく一定期間であることです。

回転map内には 2 つありますmap挿入された場合のみ挿入されますnewMapoldMap削除は を削除し、次にoldMapと を交換するだけですnewMap

@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);
        }
    }
}

また、bean

@Configuration
public class WebSocketConfig {
    
    

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

}

スキャンコードインターフェイス

@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);
        }
    }

}

携帯電話でコードをスキャンするとviewQRCode機能に入ります。
まず、session.getAttribute(USR);携帯電話でユーザー情報を取得します。
そうでない場合は、モバイル端末に電話してログインします。そして、id否定的で無効なものをコンピュータに返しますuser;
存在する場合は、QRWebSocket.callbackユーザー情報を返しますuser

フロントエンドコード

ここでの[[${QR_SOCKET}]]待機はthymeleafテンプレートにあるので、そこには入らないでください。
layerライブラリにあるので心配する必要はありません
chain。またjson2form、自分で作成したツール関数ですので、心配する必要はありません。
次のコードの意味は、WebSocket通信を確立することです。
戻るときに、ユーザーはエラーを報告しidます ログインに成功したら、ログインインターフェースにアクセスしてユーザー情報を送信し、ログインに成功するとユーザーのホームページにジャンプします。-1

// 向后端发送一个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();
}

ツールキット

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));
    });
}

おすすめ

転載: blog.csdn.net/qq_45256489/article/details/121106487