圖解用戶登錄驗證流程,寫得太好了!

原文:juejin.cn/post/7025768845075808286

前言

本文通過圖示及代碼的方式介紹用戶登錄流程及技術實現,內容包括用戶登錄,用戶驗證,如何獲取操作用戶的信息以及一些黑名單及匿名接口如何免驗證相關的實現。

業務圖解

對於用戶登錄來說、涉及到了用戶註冊、登錄驗證幾個方面,通過流程圖演示如何處理(新用戶 / 老用戶)登錄

流程解讀

客戶端 - 登錄界面(通常手機驗證碼登錄)

  1. 填寫手機號

  2. 發送驗證碼

  3. 填寫驗證碼

  4. 勾選新用戶自動註冊

服務端 - 用戶驗證

  1. 驗證賬號驗證碼是否正確

  2. 驗證用戶是否存在(不存在出初始化用戶信息)

  3. 完成驗證生成 token

  4. 將 token 返回給客戶端

用戶信息設計

驗證流程圖解

登錄驗證流程涉及到了兩個接口,兩個緩存。

  1. 獲取驗證碼接口,給手機號發送驗證碼並設置驗證碼緩存,設置過期時間; 最新面試題整理好了,點擊 Java 面試庫小程序在線刷題。

  2. 登錄接口,提交手機號及驗證碼,讀取緩存進行匹配驗證,成功則生成 token 返回給客戶端,客戶端登錄成功,登錄後請求頭攜帶 token 進行業務請求即可。

關於 token 過期時間

通常我們 token 的過期時間是根據客戶端的類型來定義的,app 的過期時間會更長一些(通常一個星期),web 端過期時間以小時爲單位,如果控制過期時間可以將 web 登錄和 app 登錄拆分爲兩個接口(能夠分流,接口壓力更小),或者是根據請求頭信息進行判斷即可,是移動端就設置 7 天,是 web 端就設置兩小時。

關於業務請求 token 驗證

登錄成功後,客戶端每次請求都會攜帶 token, 通常我們會有一個網關來進行 token 驗證,網關用於登錄驗證的核心就是登錄成功後寫入的 token 作爲 key,值爲用戶基礎信息的緩存,圖解如下:

驗證成功後,重寫內部請求頭,將用戶的的 id, 賬號,暱稱信息放入請求頭中,這樣可以方便業務系統獲取當前操作用戶信息以及權限控制等等。

關於登出操作

用戶攜帶 token 請求登出接口,登出接口對 token 對應的緩存進行刪除操作,返回 401 即可,客戶端獲取到 401 就會跳轉到登錄頁面

關於匿名請求(免登錄)

通常匿名請求放行有兩種方案,

  1. 授權 token, 爲 token 設置單位時間內請求次數;

  2. 配置路徑放行規則,對請求接口路徑進行正則匹配,符合正則規則的進行放行

方案 1:授權 token,限制單位時間請求次數

優點就是雖然是免登錄接口,但是接口的操作對象可以追溯,請求次數可控,避免被非法利用;缺點就是需要更多的編碼及配置工作。

技術實現

  1. 提供一個授權 token 管理頁面,主要管理 token 使用者,token 的值,單位時間訪問次數(如每分鐘 60 次)

  2. 增刪改查,將授權 token 存放到緩存中,使用 map 進行存儲,key 爲 token,值爲每分鐘訪問次數

  3. 單位時間計數緩存, 過期時間爲 1 分鐘

這時候我們需要在上面的驗證流程圖基礎上進行升級

請求次數檢查代碼實現

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 授權token請求限制緩存
 */
@Component
public class AuthTokenRequestLimitCache {

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    private static final String AUTH_TOKEN_LIMIT_KEY_PREFIX = "auth_token_limit";

    /**
     * 請求次數+1並檢查是否超限
     *
     * @param token
     * @return 是否放行
     */
    public boolean incrementWithCheck(String token) {
        // 1.獲取token請求次數限制,獲取爲null代表授權配置已被修改,此token已經不具備權限
        Integer limit = getLimit(token);
        if (limit == null) {
            return false;
        }
        // 2.組裝緩存key,讀取緩存
        String key = String.join(":", AUTH_TOKEN_LIMIT_KEY_PREFIX, token);
        Integer count = redisTemplate.opsForValue().get(key);
        // 3.沒有值代表一分鐘內沒有請求產生了
        if (count == null) {
            // 初始化值
            redisTemplate.opsForValue().increment(key);
            // 設置過期時間
            redisTemplate.expire(key, 1L, TimeUnit.MINUTES);
            return true;
        }
        // 自增並獲取當前值 大於限制的話 返回false 網關過濾器返回提示信息(如請求過於頻繁)
        Long inc = redisTemplate.opsForValue().increment(key);
        return inc <= limit;
    }

    /**
     * 獲取限值
     *
     * @param token
     * @return
     */
    public Integer getLimit(String token) {
        Object limit = redisTemplate.opsForHash().get("auth_token_limit", token);
        return limit == null ? null : (Integer) limit;
    }
}

對於授權接口,通常是隻允許 get 操作,對數據進行提交或者更新是不被允許的,當然這個是業務層面的,最終取決於系統設計。最新面試題整理好了,點擊 Java 面試庫小程序在線刷題。

方案 2:請求路徑正則校驗

我們在網關的配置文件中增加匿名接口規則,請求到網關時,檢查請求的路徑是否符合匿名接口規則,是則放行,不是則進行 token 校驗,方案比較簡單,只需要對網關進行處理即可。

關於黑名單

對於一個系統來說,黑名單是最後一道關卡,所以爲了安全我們需要對問題用戶進行黑名單操作,具體實現也比較簡單

  1. 用戶管理頁面提供一個拉黑的按鈕,拉黑後,這些用戶的 id 會存儲到一個 set 集合中去

  2. 登錄時候檢查用戶是否在黑名單中,是則拒絕登錄並提示

  3. 如果用戶已經登錄後進行拉黑操作,網關會在鑑權通過後檢查用戶是否在黑名單中,是則刪除 token 對應緩存,返回 401,401 就會跳到登錄頁,步驟 2 就會進行攔截。

總結

用戶系統是非常基礎的系統,但是很多程序員工作中可能並沒有真正的參與到用戶系統的開發,通過此文可以對用戶登錄流程及配套功能有一個全面的瞭解。

盼盼編程 資深後臺開發,本號分享後臺開發技術,包括算法與數據結構,筆試 / 面試,網絡編程,數據庫理論,操作系統,IT 雜文

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/2nUe1sJLTKa3DpXNzE6_Aw