什么是兩步驗證
??兩步驗證,是指用戶登錄賬戶的時候,除了要輸入用戶名和密碼,還要求用戶輸入一個動態(tài)密碼,為帳戶添加了一層額外保護。這個動態(tài)密碼要么是專門的硬件,要么由用戶手機APP提供。即使入侵者竊取了用戶密碼,也會因不能使用用戶手機而無法登錄帳戶。許多游戲客戶端和網(wǎng)銀采用這種方式。以銀行為例,當用戶進行轉賬操作時,第一步輸入6位取款密碼,第二步輸入動態(tài)密碼器上數(shù)字,這個密碼器是開戶時銀行提供的硬件。
動態(tài)密碼原理
??客戶端和服務器事先協(xié)商好一個密鑰K,用于一次性密碼的生成過程,此密鑰不被任何第三方所知道。此外,客戶端和服務器各有一個計數(shù)器C,并且事先將計數(shù)值同步。進行驗證時,客戶端對密鑰和計數(shù)器的組合(K,C)使用HMAC(Hash-based
Message Authentication Code)算法計算一次性密碼,公式如下:HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
上面采用了HMAC-SHA-1,當然也可以使用HMAC-MD5等。HMAC算法得出的值位數(shù)比較多,不方便用戶輸入,因此需要截斷(Truncate)成為一組不太長十進制數(shù)(例如6位)。計算完成之后客戶端計數(shù)器C計數(shù)值加1。用戶將這一組十進制數(shù)輸入并且提交之后,服務器端同樣的計算,并且與用戶提交的數(shù)值比較,如果相同,則驗證通過,服務器端將計數(shù)值C增加1。如果不相同,則驗證失敗。
業(yè)務流程
??如何開發(fā)這個功能呢?我們先理清它的流程:
* 第一步:輸入常規(guī)帳號密碼,驗證成功后進入二次驗證頁面。
* 第二步:二次驗證頁面要求用戶輸入動態(tài)密碼,用戶手機必須先通過APP綁定帳號后才能獲取動態(tài)密碼,APP推薦eagle2fa
<http://www.eagle2fa.com/>。
* 第三步:頁面生成二維碼,內(nèi)容是URI地址otpauth://totp/賬號?secret=密鑰
,用eagle2fa掃碼后綁定帳號,把密鑰保存在客戶端,如下圖所示:
* 第四步:輸入APP上的6位數(shù)字,驗證通過進入用戶中心頁面。
最重要的功能是生成二維碼和驗證動態(tài)密碼。
組件選型
googleauth是Google Authenticator的開源實現(xiàn)
<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>
也可以使用其他網(wǎng)站提供的的WEB API,譬如:
http://qr.liantu.com/api.php?text=x x必須用UTF8編碼格式,x內(nèi)容出現(xiàn)&符號時用%26代替,換行符使用%0A
關鍵代碼
以下代碼演示怎么使用GoogleAuthenticator包:
private static final GoogleAuthenticator googleAuthenticator = new
GoogleAuthenticator(); /** * 由于只是演示,dao沒有操作數(shù)據(jù)庫,真實的場景必然要持久化 */ @Autowired
private UserDao userDao; @PostConstruct public void init() {
googleAuthenticator.setCredentialRepository(new ICredentialRepository() {
@Override public String getSecretKey(String userName) { //根據(jù)帳號查詢secretKey
return userDao.getSecretKey(userName); } @Override public void
saveUserCredentials(String userName, String secretKey, int validationCode,
List<Integer> scratchCodes) { //secretKey要保存在數(shù)據(jù)庫中
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()); }
以下是二次驗證方法:
// 驗證動態(tài)密碼 username 帳號, code app上的6位數(shù)字 public boolean validCode(String
username, int code) { return googleAuthenticator.authorizeUser(username, code);
}
點擊獲取完整代碼
<https://gitee.com/cabbage/java-study-demo/tree/master/two-step-verify-demo>
參考(部分摘抄的文字版權屬于原作者)
https://blog.seetee.me/post/2011/google-two-step-verification/
https://www.zhihu.com/question/20462696/answer/19670601
熱門工具 換一換