最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java Spring Boot實現(xiàn)簡易掃碼登錄詳解

 更新時間:2021年09月26日 08:41:31   作者:莊周de蝴蝶  
這篇文章主要為大家詳細(xì)介紹了java Spring Boot實現(xiàn)app掃碼登錄功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

前言

本文將介紹基于SpringBoot + Vue + Android實現(xiàn)的掃碼登錄demo的總體思路,完整代碼已上傳到GitHub。Web端體驗地址:http://47.116.72.33/(只剩一個月有效期),apk下載地址:https://github.com/Zjvngvn/qrscan/releases/tag/0.0.1。用戶名:非空即可,密碼:123456,效果見文末,整體實現(xiàn)如有不妥之處,歡迎交流討論,實現(xiàn)部分參考二維碼掃碼登錄是什么原理。

項目簡介

后端:SpringBoot,Redis。

前端:Vue,Vue Router、VueX、Axios、vue-qr、ElemntUI。

安卓:ZXing、XUI、YHttp。

實現(xiàn)思路

總體的掃碼登錄和OAuth2.0的驗證邏輯相似,如下所示:

image-20210921205657426

用戶選擇掃碼登錄可以看作是A:前端發(fā)授權(quán)請求,等待app掃碼。

用戶使用app進行掃碼可以看作是B:掃碼進行授權(quán),返回一個臨時Token供二次認(rèn)證。

用戶在app進行確認(rèn)登錄可以看作是C:進行登錄確認(rèn),授權(quán)用戶在Web端登錄。

后端在用戶確認(rèn)登錄后返回一個正式Token即可看作是步驟D。

后續(xù)前端根據(jù)正式Token訪問后臺接口,正式在Web端進行操作即可看作是E和F。

二次認(rèn)證的原因

之所以在用戶掃碼之后還需要進行再一次的確認(rèn)登錄,而不是直接就登錄的原因,則是為了用戶安全考慮,避免用戶掃了其他人需要登錄的二維碼,在未經(jīng)確認(rèn)就直接登錄了,導(dǎo)致他人可能會在我們不知道的情況下訪問我們的信息。

實現(xiàn)步驟

1.用戶訪問網(wǎng)頁端,選擇掃碼登錄

用戶在選擇掃碼登錄時,會向后端發(fā)送一個二維碼的生成請求,后端生成UUID,并保存到Redis(固定有效時間),狀態(tài)設(shè)置為UNUSED(未使用)狀態(tài),如果Redis緩存過期,則為EXPIRE(過期)狀態(tài),前端根據(jù)后端返回的內(nèi)容生成二維碼,并設(shè)置一個定時器,每隔一段時間根據(jù)二維碼的內(nèi)容中的UUID,向后端發(fā)送請求,獲取二維碼的狀態(tài),更新界面展示的內(nèi)容。

生成二維碼后端接口:

/**
 * 生成二維碼內(nèi)容
 *
 * @return 結(jié)果
 */
@GetMapping("/generate")
public BaseResult generate() {
    String code = IdUtil.simpleUUID();
    redisCache.setCacheObject(code, CodeUtils.getUnusedCodeInfo(), 
                              DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS);
    return BaseResult.success(GENERATE_SUCCESS, code);
}

前端獲取內(nèi)容,生成二維碼:

getToken() {
    this.codeStatus = 'EMPTY'
    this.tip = '正在獲取登錄碼,請稍等'
    // 有效時間 60 秒
    this.effectiveSeconds = 60
    clearInterval(this.timer)
    request({
        method: 'get',
        url: '/code/generate'
    }).then((response) => {
        // 請求成功, 設(shè)置二維碼內(nèi)容, 并更新相關(guān)信息
        this.code = `${HOST}/code/scan?code=${response.data}`
        this.codeStatus = 'UNUSED'
        this.tip = '請使用手機掃碼登錄'
        this.timer = setInterval(this.getTokenInfo, 2000)
    }).catch(() => {
        this.getToken()
    })
}

后端返回二維碼狀態(tài)信息的接口:

/**
 * 獲取二維碼狀態(tài)信息
 *
 * @param code 二維碼
 * @return 結(jié)果
 */
@GetMapping("/info")
public BaseResult info(String code) {
    CodeVO codeVO = redisCache.getCacheObject(code);
    if (codeVO == null) {
        return BaseResult.success(INVALID_CODE, StringUtils.EMPTY);
    }
    return BaseResult.success(GET_SUCCESS, codeVO);
}

前端輪詢獲取二維碼狀態(tài):

getTokenInfo() {
    this.effectiveSeconds--
    // 二維碼過期
    if (this.effectiveSeconds <= 0) {
        this.codeStatus = 'EXPIRE'
        this.tip = '二維碼已過期,請刷新'
        return
    }
    // 輪詢查詢二維碼狀態(tài)
    request({
        method: 'get',
        url: '/code/info',
        params: {
            code: this.code.substr(this.code.indexOf('=') + 1)
        }
    }).then(response => {
        const codeVO = response.data
        // 二維碼過期
        if (!codeVO || !codeVO.codeStatus) {
            this.codeStatus = 'EXPIRE'
            this.tip = '二維碼已過期,請刷新'
            return
        }
        // 二維碼狀態(tài)為為正在登錄
        if (codeVO.codeStatus === 'CONFIRMING') {
            this.username = codeVO.username
            this.avatar = codeVO.avatar
            this.codeStatus = 'CONFIRMING'
            this.tip = '掃碼成功,請在手機上確認(rèn)'
            return
        }
        // 二維碼狀態(tài)為確認(rèn)登錄
        if (codeVO.codeStatus === 'CONFIRMED') {
            clearInterval(this.timer)
            const token = codeVO.token
            store.commit('setToken', token)
            this.$router.push('/home')
            Message.success('登錄成功')
            return
        }
    })
}

2.使用手機掃碼,二維碼狀態(tài)改變

當(dāng)用戶使用手機掃碼時(已登錄并且為正確的app,否則掃碼會跳轉(zhuǎn)到自定義的宣傳頁),會更新二維碼的狀態(tài)為CONFIRMING(待確認(rèn))狀態(tài),并在Redis緩存中新增用戶名及頭像信息的保存供前端使用展示,此外還會返回用戶的登錄信息(登錄地址、瀏覽器、操作系統(tǒng))給app展示,同時生成一個臨時Token給app(固定有效時間)。

用戶掃碼時的后臺處理:

/**
 * 處理未使用狀態(tài)的二維碼
 *
 * @param code 二維碼
 * @param token token
 * @return 結(jié)果
 */
private BaseResult handleUnusedQr(String code, String token) {
    // 校驗 app 端訪問傳遞的 token
    boolean isLegal = JwtUtils.verify(token);
    if (!isLegal) {
        return BaseResult.error(AUTHENTICATION_FAILED);
    }
    // 保存用戶名、頭像信息, 供前端展示
    String username = JwtUtils.getUsername(token);
    CodeVO codeVO = CodeUtils.getConfirmingCodeInfo(username, DEFAULT_AVATAR_URL);
    redisCache.setCacheObject(code, codeVO, DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS);
    // 返回登錄地址、瀏覽器、操作系統(tǒng)以及一個臨時 token 給 app
    String address = HttpUtils.getRealAddressByIp();
    String browser = HttpUtils.getBrowserName();
    String os = HttpUtils.getOsName();
    String tmpToken = JwtUtils.sign(username);
    // 將臨時 token 作為鍵, 用戶名為內(nèi)容存儲在 redis 中
    redisCache.setCacheObject(tmpToken, username, DEFAULT_TEMP_TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES);
    LoginInfoVO loginInfoVO = new LoginInfoVO(address, browser, os, tmpToken);
    return BaseResult.success(SCAN_SUCCESS, loginInfoVO);
}

3.手機確認(rèn)登錄

當(dāng)用戶在app中點擊確認(rèn)登錄時,就會攜帶生成的臨時Token發(fā)送更新狀態(tài)的請求,二維碼的狀態(tài)會被更新為CONFIRMED(已確認(rèn)登錄)狀態(tài),同時后端會生成一個正式Token保存在Redis中,前端在輪詢更新狀態(tài)時獲取這個Token,然后使用這個Token進行登錄。

后端處理確認(rèn)登錄的代碼:

/**
 * 處理未待確認(rèn)狀態(tài)的二維碼
 *
 * @param code 二維碼
 * @param token token
 * @return 結(jié)果
 */
private BaseResult handleConfirmingQr(String code, String token) {
    // 使用臨時 token 獲取用戶名, 并從 redis 中刪除臨時 token
    String username = redisCache.getCacheObject(token);
    if (StringUtils.isBlank(username)) {
        return BaseResult.error(AUTHENTICATION_FAILED);
    }
    redisCache.deleteObject(token);
    // 根據(jù)用戶名生成正式 token并保存在 redis 中供前端使用
    String formalToken = JwtUtils.sign(username);
    CodeVO codeVO = CodeUtils.getConfirmedCodeInfo(username, DEFAULT_AVATAR_URL, formalToken);
    redisCache.setCacheObject(code, codeVO, DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS);
    return BaseResult.success(CONFIRM_SUCCESS);
}

效果演示

在這里插入圖片描述

在這里插入圖片描述

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • 深入淺出MappedByteBuffer(推薦)

    深入淺出MappedByteBuffer(推薦)

    MappedByteBuffer使用虛擬內(nèi)存,因此分配(map)的內(nèi)存大小不受JVM的-Xmx參數(shù)限制,但是也是有大小限制的,這篇文章主要介紹了MappedByteBuffer的基本知識,需要的朋友可以參考下
    2022-12-12
  • java實現(xiàn)Runnable接口適合資源的共享

    java實現(xiàn)Runnable接口適合資源的共享

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)Runnable接口適合資源的共享,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Java Class 加密工具 ClassFinal詳解

    Java Class 加密工具 ClassFinal詳解

    ClassFinal 是一款 java class 文件安全加密工具,支持直接加密jar包或war包,無需修改任何項目代碼,兼容spring-framework;可避免源碼泄漏或字節(jié)碼被反編譯,這篇文章主要介紹了Java Class 加密工具 ClassFinal,需要的朋友可以參考下
    2023-03-03
  • 快速入門HarmonyOS的Java UI框架的教程

    快速入門HarmonyOS的Java UI框架的教程

    這篇文章主要介紹了快速入門HarmonyOS的Java UI框架,本文給大家介紹的非常詳細(xì)對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • SpringBoot整合Shiro兩種方式(總結(jié))

    SpringBoot整合Shiro兩種方式(總結(jié))

    這篇文章主要介紹了SpringBoot整合Shiro兩種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Java泛型中的通配符舉例詳解

    Java泛型中的通配符舉例詳解

    Java泛型中的通配符是指使用"?"來表示未知類型,可以用于定義泛型類、泛型方法和泛型接口,下面這篇文章主要給大家介紹了關(guān)于Java泛型中通配符的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • Spring?boot集成easy?excel實現(xiàn)導(dǎo)入導(dǎo)出功能

    Spring?boot集成easy?excel實現(xiàn)導(dǎo)入導(dǎo)出功能

    這篇文章主要介紹了Spring?boot集成easy?excel實現(xiàn)導(dǎo)入導(dǎo)出操作,使用easyexcel,首先要引入easyexcel的maven依賴,具體的版本根據(jù)你的需求去設(shè)置,本文結(jié)合實例代碼講解的非常詳細(xì),需要的朋友可以參考下
    2024-05-05
  • java中冪指數(shù)值的運算代碼解析

    java中冪指數(shù)值的運算代碼解析

    這篇文章主要介紹了java中冪指數(shù)值的運算代碼解析,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • 基于google zxing的Java二維碼生成與解碼

    基于google zxing的Java二維碼生成與解碼

    這篇文章主要為大家詳細(xì)介紹了基于google zxing的Java二維碼生成與解碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • 詳解Spring事件發(fā)布與監(jiān)聽機制

    詳解Spring事件發(fā)布與監(jiān)聽機制

    Spring提供了ApplicationContext事件機制,可以發(fā)布和監(jiān)聽事件,這個特性非常有用。Spring內(nèi)置了一些事件和監(jiān)聽器,例如在Spring容器啟動前,Spring容器啟動后,應(yīng)用啟動失敗后等事件發(fā)生后,監(jiān)聽在這些事件上的監(jiān)聽器會做出相應(yīng)的響應(yīng)處理
    2021-06-06

最新評論

张家川| 铜陵市| 黄冈市| 铜山县| 奉节县| 漳州市| 尖扎县| 大安市| 阿荣旗| 顺平县| 崇明县| 东乌珠穆沁旗| 隆安县| 顺义区| 克什克腾旗| 江都市| 高青县| 东辽县| 濮阳县| 遵义县| 都匀市| 东阿县| 沁阳市| 珲春市| 阳新县| 宣汉县| 和田县| 苏尼特右旗| 洛南县| 岑溪市| 呼伦贝尔市| 横山县| 怀柔区| 蓬安县| 阿尔山市| 开封市| 莱芜市| 威信县| 滦平县| 万源市| 武鸣县|