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

springboot掃碼登錄的簡(jiǎn)單實(shí)現(xiàn)

 更新時(shí)間:2021年09月23日 09:40:44   作者:莊周de蝴蝶  
本文主要介紹基于SpringBoot + Vue + Android實(shí)現(xiàn)的掃碼登錄,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

前言

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

項(xiàng)目簡(jiǎn)介

后端:SpringBootRedis。
前端:VueVue Router、VueXAxios、vue-qr、ElemntUI
安卓:ZXing、XUIYHttp。

實(shí)現(xiàn)思路

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

image-20210921205657426

用戶(hù)選擇掃碼登錄可以看作是A:前端發(fā)授權(quán)請(qǐng)求,等待app掃碼。
用戶(hù)使用app進(jìn)行掃碼可以看作是B:掃碼進(jìn)行授權(quán),返回一個(gè)臨時(shí)Token供二次認(rèn)證。
用戶(hù)在app進(jìn)行確認(rèn)登錄可以看作是C:進(jìn)行登錄確認(rèn),授權(quán)用戶(hù)在Web端登錄。
后端在用戶(hù)確認(rèn)登錄后返回一個(gè)正式Token即可看作是步驟D。
后續(xù)前端根據(jù)正式Token訪(fǎng)問(wèn)后臺(tái)接口,正式在Web端進(jìn)行操作即可看作是EF。

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

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

實(shí)現(xiàn)步驟

用戶(hù)訪(fǎng)問(wèn)網(wǎng)頁(yè)端,選擇掃碼登錄

用戶(hù)在選擇掃碼登錄時(shí),會(huì)向后端發(fā)送一個(gè)二維碼的生成請(qǐng)求,后端生成UUID,并保存到Redis(固定有效時(shí)間),狀態(tài)設(shè)置為UNUSED(未使用)狀態(tài),如果Redis緩存過(guò)期,則為EXPIRE(過(guò)期)狀態(tài),前端根據(jù)后端返回的內(nèi)容生成二維碼,并設(shè)置一個(gè)定時(shí)器,每隔一段時(shí)間根據(jù)二維碼的內(nèi)容中的UUID,向后端發(fā)送請(qǐng)求,獲取二維碼的狀態(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 = '正在獲取登錄碼,請(qǐng)稍等'
    // 有效時(shí)間 60 秒
    this.effectiveSeconds = 60
    clearInterval(this.timer)
    request({
        method: 'get',
        url: '/code/generate'
    }).then((response) => {
        // 請(qǐng)求成功, 設(shè)置二維碼內(nèi)容, 并更新相關(guān)信息
        this.code = `${HOST}/code/scan?code=${response.data}`
        this.codeStatus = 'UNUSED'
        this.tip = '請(qǐng)使用手機(jī)掃碼登錄'
        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);
}

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

getTokenInfo() {
    this.effectiveSeconds--
    // 二維碼過(guò)期
    if (this.effectiveSeconds <= 0) {
        this.codeStatus = 'EXPIRE'
        this.tip = '二維碼已過(guò)期,請(qǐng)刷新'
        return
    }
    // 輪詢(xún)查詢(xún)二維碼狀態(tài)
    request({
        method: 'get',
        url: '/code/info',
        params: {
            code: this.code.substr(this.code.indexOf('=') + 1)
        }
    }).then(response => {
        const codeVO = response.data
        // 二維碼過(guò)期
        if (!codeVO || !codeVO.codeStatus) {
            this.codeStatus = 'EXPIRE'
            this.tip = '二維碼已過(guò)期,請(qǐng)刷新'
            return
        }
        // 二維碼狀態(tài)為為正在登錄
        if (codeVO.codeStatus === 'CONFIRMING') {
            this.username = codeVO.username
            this.avatar = codeVO.avatar
            this.codeStatus = 'CONFIRMING'
            this.tip = '掃碼成功,請(qǐng)?jiān)谑謾C(jī)上確認(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
        }
    })
}

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

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

用戶(hù)掃碼時(shí)的后臺(tái)處理:

/**
 * 處理未使用狀態(tài)的二維碼
 *
 * @param code 二維碼
 * @param token token
 * @return 結(jié)果
 */
private BaseResult handleUnusedQr(String code, String token) {
    // 校驗(yàn) app 端訪(fǎng)問(wèn)傳遞的 token
    boolean isLegal = JwtUtils.verify(token);
    if (!isLegal) {
        return BaseResult.error(AUTHENTICATION_FAILED);
    }
    // 保存用戶(hù)名、頭像信息, 供前端展示
    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)以及一個(gè)臨時(shí) token 給 app
    String address = HttpUtils.getRealAddressByIp();
    String browser = HttpUtils.getBrowserName();
    String os = HttpUtils.getOsName();
    String tmpToken = JwtUtils.sign(username);
    // 將臨時(shí) token 作為鍵, 用戶(hù)名為內(nèi)容存儲(chǔ)在 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);
}

手機(jī)確認(rèn)登錄

當(dāng)用戶(hù)在app中點(diǎn)擊確認(rèn)登錄時(shí),就會(huì)攜帶生成的臨時(shí)Token發(fā)送更新?tīng)顟B(tài)的請(qǐng)求,二維碼的狀態(tài)會(huì)被更新為CONFIRMED(已確認(rèn)登錄)狀態(tài),同時(shí)后端會(huì)生成一個(gè)正式Token保存在Redis中,前端在輪詢(xún)更新?tīng)顟B(tài)時(shí)獲取這個(gè)Token,然后使用這個(gè)Token進(jìn)行登錄。

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

/**
 * 處理未待確認(rèn)狀態(tài)的二維碼
 *
 * @param code 二維碼
 * @param token token
 * @return 結(jié)果
 */
private BaseResult handleConfirmingQr(String code, String token) {
    // 使用臨時(shí) token 獲取用戶(hù)名, 并從 redis 中刪除臨時(shí) token
    String username = redisCache.getCacheObject(token);
    if (StringUtils.isBlank(username)) {
        return BaseResult.error(AUTHENTICATION_FAILED);
    }
    redisCache.deleteObject(token);
    // 根據(jù)用戶(hù)名生成正式 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);
}

效果演示

在這里插入圖片描述

在這里插入圖片描述

到此這篇關(guān)于springboot掃碼登錄的簡(jiǎn)單實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)springboot掃碼登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決SpringBoot項(xiàng)目啟動(dòng)后網(wǎng)頁(yè)顯示Please sign in的問(wèn)題

    解決SpringBoot項(xiàng)目啟動(dòng)后網(wǎng)頁(yè)顯示Please sign in的問(wèn)題

    這篇文章主要介紹了解決SpringBoot項(xiàng)目啟動(dòng)后網(wǎng)頁(yè)顯示Please sign in的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • java開(kāi)發(fā)web前端cookie session及token會(huì)話(huà)機(jī)制詳解

    java開(kāi)發(fā)web前端cookie session及token會(huì)話(huà)機(jī)制詳解

    如果把人體比作一個(gè)web系統(tǒng)的話(huà),cookie、session和token就好像人體的經(jīng)絡(luò)和血管一樣,而web系統(tǒng)中的數(shù)據(jù),就好像人體的血液一樣。血液依靠著血管在人體內(nèi)流動(dòng),就如數(shù)據(jù)根據(jù)cookie和session機(jī)制在web系統(tǒng)中流動(dòng)一樣
    2021-10-10
  • SpringBoot+MyBatis-Plus實(shí)現(xiàn)分頁(yè)示例

    SpringBoot+MyBatis-Plus實(shí)現(xiàn)分頁(yè)示例

    本文介紹了SpringBoot+MyBatis-Plus實(shí)現(xiàn)分頁(yè)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Spring Boot 中使用 Mybatis Plus的操作方法

    Spring Boot 中使用 Mybatis Plus的操作方法

    本文介紹了如何在 Spring Boot 項(xiàng)目中集成 Mybatis Plus,Spring Boot 與 MyBatis Plus 的集成非常簡(jiǎn)單,通過(guò)自動(dòng)配置和簡(jiǎn)潔的 API,可以大大減少開(kāi)發(fā)中常見(jiàn)的數(shù)據(jù)庫(kù)操作代碼,需要的朋友參考下吧
    2024-12-12
  • Java中靜態(tài)類(lèi)型檢查是如何進(jìn)行的實(shí)例思路詳解

    Java中靜態(tài)類(lèi)型檢查是如何進(jìn)行的實(shí)例思路詳解

    這篇文章主要介紹了Java中靜態(tài)類(lèi)型檢查是如何進(jìn)行的實(shí)例思路詳解的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • Spring集成Druid連接池及監(jiān)控配置的全過(guò)程

    Spring集成Druid連接池及監(jiān)控配置的全過(guò)程

    java程序很大一部分要操作數(shù)據(jù)庫(kù),為了提高性能操作數(shù)據(jù)庫(kù)的時(shí)候,有不得不使用數(shù)據(jù)庫(kù)連接池,下面這篇文章主要給大家介紹了關(guān)于Spring集成Druid連接池及監(jiān)控配置的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • Spring Security OAuth2 授權(quán)碼模式的實(shí)現(xiàn)

    Spring Security OAuth2 授權(quán)碼模式的實(shí)現(xiàn)

    這篇文章主要介紹了Spring Security OAuth2 授權(quán)碼模式的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • SpringBoot處理JSON數(shù)據(jù)方法詳解

    SpringBoot處理JSON數(shù)據(jù)方法詳解

    這篇文章主要介紹了SpringBoot整合Web開(kāi)發(fā)中Json數(shù)據(jù)處理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-10-10
  • java集合遍歷的幾種方式總結(jié)及詳細(xì)比較

    java集合遍歷的幾種方式總結(jié)及詳細(xì)比較

    下面小編就為大家?guī)?lái)一篇java集合遍歷的幾種方式總結(jié)及詳細(xì)比較。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-04-04
  • IDEA消除指定警告的兩種方法小結(jié)

    IDEA消除指定警告的兩種方法小結(jié)

    有時(shí)候IDEA會(huì)代碼中給出一些我們不需要的警告,看起來(lái)就很不美觀(guān),本文主要介紹了IDEA消除指定警告的兩種方法,感興趣的可以了解一下
    2023-08-08

最新評(píng)論

东安县| 宁乡县| 新绛县| 福安市| 金川县| 类乌齐县| 阆中市| 房山区| 昭苏县| 松原市| 莒南县| 合川市| 孟村| 澄迈县| 永寿县| 新余市| 桃源县| 宁乡县| 安庆市| 北安市| 宾川县| 英山县| 德兴市| 防城港市| 法库县| 珲春市| 南通市| 伊金霍洛旗| 贡觉县| 民乐县| 赫章县| 伊川县| 高邑县| 浪卡子县| 泸定县| 修武县| 山东| 黔江区| 沧州市| 陆良县| 定兴县|