如何開發兩步驗證功能
阿新 • • 發佈:2019-09-16
什麼是兩步驗證
兩步驗證,是指使用者登入賬戶的時候,除了要輸入使用者名稱和密碼,還要求使用者輸入一個動態密碼,為帳戶添加了一層額外保護。這個動態密碼要麼是專門的硬體,要麼由使用者手機APP提供。即使入侵者竊取了使用者密碼,也會因不能使用使用者手機而無法登入帳戶。許多遊戲客戶端和網銀採用這種方式。以銀行為例,當用戶進行轉賬操作時,第一步輸入6位取款密碼,第二步輸入動態密碼器上數字,這個密碼器是開戶時銀行提供的硬體。
動態密碼原理
客戶端和伺服器事先協商好一個金鑰K,用於一次性密碼的生成過程,此金鑰不被任何第三方所知道。此外,客戶端和伺服器各有一個計數器C,並且事先將計數值同步。進行驗證時,客戶端對金鑰和計數器的組合(K,C)使用HMAC(Hash-based Message Authentication Code)演算法計算一次性密碼,公式如下:HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
業務流程
如何開發這個功能呢?我們先理清它的流程:
- 第一步:輸入常規帳號密碼,驗證成功後進入二次驗證頁面。
- 第二步:二次驗證頁面要求使用者輸入動態密碼,使用者手機必須先通過APP繫結帳號後才能獲取動態密碼,APP推薦eagle2fa。
- 第三步:頁面生成二維碼,內容是URI地址
otpauth://totp/賬號?secret=金鑰
,用eagle2fa掃碼後繫結帳號,把金鑰儲存在客戶端,如下圖所示:
- 第四步:輸入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/19670