如何開發兩步驗證功能


什么是兩步驗證

  兩步驗證,是指用戶登錄賬戶的時候,除了要輸入用戶名和密碼,還要求用戶輸入一個動態密碼,為帳戶添加了一層額外保護。這個動態密碼要么是專門的硬件,要么由用戶手機APP提供。即使入侵者竊取了用戶密碼,也會因不能使用用戶手機而無法登錄帳戶。許多游戲客戶端和網銀采用這種方式。以銀行為例,當用戶進行轉賬操作時,第一步輸入6位取款密碼,第二步輸入動態密碼器上數字,這個密碼器是開戶時銀行提供的硬件。

動態密碼原理

  客戶端和服務器事先協商好一個密鑰K,用於一次性密碼的生成過程,此密鑰不被任何第三方所知道。此外,客戶端和服務器各有一個計數器C,並且事先將計數值同步。進行驗證時,客戶端對密鑰和計數器的組合(K,C)使用HMAC(Hash-based Message Authentication Code)算法計算一次性密碼,公式如下:HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))上面采用了HMAC-SHA-1,當然也可以使用HMAC-MD5等。HMAC算法得出的值位數比較多,不方便用戶輸入,因此需要截斷(Truncate)成為一組不太長十進制數(例如6位)。計算完成之后客戶端計數器C計數值加1。用戶將這一組十進制數輸入並且提交之后,服務器端同樣的計算,並且與用戶提交的數值比較,如果相同,則驗證通過,服務器端將計數值C增加1。如果不相同,則驗證失敗。

業務流程

  如何開發這個功能呢?我們先理清它的流程:

  1. 第一步:輸入常規帳號密碼,驗證成功后進入二次驗證頁面。
  2. 第二步:二次驗證頁面要求用戶輸入動態密碼,用戶手機必須先通過APP綁定帳號后才能獲取動態密碼,APP推薦eagle2fa
  3. 第三步:頁面生成二維碼,內容是URI地址otpauth://totp/賬號?secret=密鑰,用eagle2fa掃碼后綁定帳號,把密鑰保存在客戶端,如下圖所示:
  4. 第四步:輸入APP上的6位數字,驗證通過進入用戶中心頁面。
    最重要的功能是生成二維碼驗證動態密碼

組件選型

googleauth是Google Authenticator的開源實現

    <dependency>
        <groupId>com.warrenstrange</groupId>
        <artifactId>googleauth</artifactId>
        <version>1.1.2</version>
   </dependency>

zxing用於生成二維碼圖片

    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>javase</artifactId>
        <version>3.3.3</version>
    </dependency>

也可以使用其他網站提供的的WEB API,譬如:

http://qr.liantu.com/api.php?text=x
x必須用UTF8編碼格式,x內容出現&符號時用%26代替,換行符使用%0A

關鍵代碼

以下代碼演示怎么使用GoogleAuthenticator包:

    private static final GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();

    /**
     * 由於只是演示,dao沒有操作數據庫,真實的場景必然要持久化
     */
    @Autowired
    private UserDao userDao;

    @PostConstruct
    public void init() {
        googleAuthenticator.setCredentialRepository(new ICredentialRepository() {
            @Override
            public String getSecretKey(String userName) {
                //根據帳號查詢secretKey
                return userDao.getSecretKey(userName);
            }

            @Override
            public void saveUserCredentials(String userName, String secretKey, int validationCode, List<Integer> scratchCodes) {
                //secretKey要保存在數據庫中
                userDao.saveUserCredentials(userName, secretKey);
            }
        });
        log.info("GoogleAuthenticator初始化成功");
    }

以下代碼是生成二維碼,uri的格式不能寫錯。

    // 必須按照這個格式,APP才能正常綁定
    private static final String KEY_FORMAT = "otpauth://totp/%s?secret=%s";

    /**
     * 生成二維碼鏈接
     */
    private String getQrUrl(String username) {
        //每次調用createCredentials都會生成新的secretKey
        GoogleAuthenticatorKey key = googleAuthenticator.createCredentials(username);
        log.info("username={},secretKey={}", username, key.getKey());
        return String.format(KEY_FORMAT, username, key.getKey());
    }

以下是二次驗證方法:

    // 驗證動態密碼  username 帳號, code  app上的6位數字
    public boolean validCode(String username, int code) {
        return googleAuthenticator.authorizeUser(username, code);
    }

點擊獲取完整代碼

參考(部分摘抄的文字版權屬於原作者)

https://blog.seetee.me/post/2011/google-two-step-verification/
https://www.zhihu.com/question/20462696/answer/19670601


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM