我又来了,,,今天我们来讲一讲两步验证的功能♥♥
简介:
两步验证,是指用户登录账户的时候,除了要输入用户名和密码,还要求用户输入一个动态密码,为帐户添加了一层额外保护。这个动态密码要么是专门的硬件,要么由用户手机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。如果不相同,则验证失败。
原理:
第一步:输入常规帐号密码,验证成功后进入二次验证页面。
第二步:二次验证页面要求用户输入动态密码,用户手机必须先通过APP绑定帐号后才能获取动态密码,APP推荐http://www.pc6.com/az/675682.html
第三步:页面生成二维码
OK,介绍就到这里了,开始我们的操作了。。。。
第一步:还是先来我们的Pom文件吧:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!--二次验证-->
<dependency>
<groupId>com.warrenstrange</groupId>
<artifactId>googleauth</artifactId>
<version>1.1.2</version>
</dependency>
<!--二维码-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
有没有感觉很简单呢,是的,没多的依赖。。。。
第二步:也是application.yml文件:这些都不多说了吧,,哈哈哈,还是copy一下吧。
server:
port: 8080
spring:
application:
name: two-step-verify-demo
很简短的配置文件
第三步:开始我们的操作,翠花上菜。。。
ActionController:
package com.demo.twostep.controller;
import com.demo.twostep.service.GoogleAuthenticatorService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@RestController
public class ActionController {
@Autowired
private GoogleAuthenticatorService googleAuthenticatorService;
/**
* 二次验证,生成二维码
*/
@RequestMapping("/qrcode")
public void qrcode(String username, HttpServletResponse response) {
try (ServletOutputStream stream = response.getOutputStream()) {
googleAuthenticatorService.genQRImage(username, stream);
} catch (IOException e) {
log.error("发生错误", e);
}
}
/**
* 二次验证,输入eagle2fa APP上的6位数字
*/
@RequestMapping("/verify")
public String verify(String username, int code) {
boolean validCode = googleAuthenticatorService.validCode(username, code);
if (validCode){
return "index";
}
return "error";
}
}
copy可能会有错误提示,不用管它,下面就开始盘他。。
GoogleAuthenticatorService
package com.demo.twostep.service;
import com.demo.twostep.dao.UserDao;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import com.warrenstrange.googleauth.ICredentialRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
/**
* @author yangjiajia
* @create 2018-09-06 16:42
*/
@Slf4j
@Service
public class GoogleAuthenticatorService {
private static final GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
private static final String KEY_FORMAT = "otpauth://totp/%s?secret=%s";
private static final String IMAGE_EXT = "png";
private static final int WIDTH = 300;
private static final int HEIGHT = 300;
/**
* 由于只是演示,这里的dao没有真的操作数据库,真实的场景必然要持久化的
*/
@Autowired
private UserDao userDao;
@PostConstruct
public void init() {
googleAuthenticator.setCredentialRepository(new ICredentialRepository() {
@Override
public String getSecretKey(String userName) {
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初始化成功");
}
/**
* 生成二维码链接
*/
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());
}
public boolean validCode(String username, int code) {
return googleAuthenticator.authorizeUser(username, code);
}
/**
* 生成二维码文件
*/
public static void genQRImage(String content, String filePath) {
try {
BitMatrix bm = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT);
Path file = new File(filePath).toPath();
MatrixToImageWriter.writeToPath(bm, IMAGE_EXT, file);
} catch (WriterException | IOException e) {
e.printStackTrace();
}
}
/**
* 生成二维码
*/
public void genQRImage(String username, ServletOutputStream stream) {
try {
String content = getQrUrl(username);
BitMatrix bm = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT);
MatrixToImageWriter.writeToStream(bm, IMAGE_EXT, stream);
} catch (WriterException | IOException e) {
e.printStackTrace();
}
}
}
UserDao(模拟数据库操作)
package com.demo.twostep.dao;
public interface UserDao {
String getSecretKey(String userName);
boolean saveUserCredentials(String userName, String secretKey);
}
UserDaoImpl
package com.demo.twostep.dao;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class UserDaoImpl implements UserDao {
private final static Map<String, String> keyMap = new HashMap<>();
@Override
public String getSecretKey(String userName) {
return keyMap.get(userName);
}
@Override
public boolean saveUserCredentials(String userName, String secretKey) {
keyMap.put(userName, secretKey);
return true;
}
}
OK,后台代码就完成了哦,接下来就是我们来测试的时候了,
老板,筷子呢,我要尝菜了。。。
首先创建三个页面:
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆页面</title>
</head>
<body>
<form action="/verify" method="post">
用户名:<input type="text" name="username" id="username">
<br>
密 码:<input type="password" name="password" id="password">
<br>
验证码:<input type="text" name="code"><input type="button" value="获取" id="button">
<br>
<input type="submit" value="登陆">
</form>
<script type="application/javascript">
var button =document.getElementById("button");
button.onclick = function () {
// 获取用户输入的信息
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
if (username == '' || password == ''){
alert("请填入信息");
}else {
window.location.href = '/qrcode?username=fc';
}
}
</script>
</body>
</html>
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>成功页面</title>
</head>
<body>
登陆成功。。。。
</body>
</html>
error.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>失败页面</title>
</head>
<body>
登陆失败。。。。
</body>
</html>
开始启动我们的项目跑起来,哈哈哈哈(这是我们的启动类)
package com.demo.twostep;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableAutoConfiguration
public class TwoStepVerifyDemoApplicationMain {
public static void main(String[] args) {
SpringApplication.run(TwoStepVerifyDemoApplicationMain.class, args);
}
}
登陆页面:
得到的二维码:
我们使用App扫描,我们会得到一个验证码(可以设置验证码的过期时间)
得到这个就说明成功了。。
输入对应的验证码,登陆就可以访问这个页面了。。
OKOK,本教程全部完成了,感谢的你的浏览。。。。
有什么问题可以在评论区评论,或者留言
2020-03-16 21:37:30 星期一♥♥