I come again, today we talk about the function of a two-step verification ♥

Brief introduction:

Two-step authentication, in which a user is required to enter a dynamic password in addition to a username and password when logging in to an account, adds an extra layer of protection for the account. This dynamic password is either specialized hardware or provided by the user’s mobile APP. Even if an intruder steals a user’s password, they cannot log in to the account because they cannot use the user’s phone. Many game clients and online banks use this approach. Take the bank as an example, when the user transfers money, the first step is to enter a six-digit withdrawal password, and the second step is to enter the number on the dynamic password machine, which is the hardware provided by the bank when opening an account.

Dynamic password principle:

The client and server negotiate a key K in advance for one-time password generation, which is not known to any third party. In addition, the client and server each have a counter, C, and synchronize the count values beforehand. During Authentication, the client uses the Hash-based Message Authentication Code (HMAC) algorithm to calculate the one-time password for the combination of key and counter (K,C), as follows: HOTP(K,C) = Truncate(HMAC-SHA-1(K,C)) HMAC-SHA-1 is adopted. Hmac-md5 can also be used. The HMAC algorithm has a large number of digits, which is inconvenient for users to enter. Therefore, Truncate the value into a group of decimal digits (for example, 6 digits). After the calculation is complete, the client counter C counts and increments by 1. After the user inputs and submits this set of decimal numbers, the server performs the same calculation and compares it with the value submitted by the user. If they are the same, the verification passes, and the server increases the count value C by 1. If they are different, the authentication fails.

Principle:

Step 1: Enter the password of the common account. After the authentication succeeds, the second authentication page is displayed. Step 2: The second verification page requires the user to enter the dynamic password. The user’s mobile phone must bind the account through the APP to obtain the dynamic password. The APP is recommended www.pc6.com/az/675682.h… Step 3: Generate two-dimensional code page

OK, that’s all for the introduction, let’s start our operation…

Step 1: Let’s start with our Pom file:

<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.12.</version> </dependency> <! <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.33.</version>
    		</dependency>
    		<dependency>
    			<groupId>org.projectlombok</groupId>
    			<artifactId>lombok</artifactId>
    			<version>1.1620.</version>
    			<scope>provided</scope>
    		</dependency>
  <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	</dependencies>
Copy the code

Does it feel easy, yes, not much dependence…

Step 2: Also application. Yml file: These are not more to say, ha ha ha, let’s copy it.

server:
  port: 8080
    
spring:
  application:
    name: two-step-verify-demo
Copy the code

Very short configuration file

Step 3: Start our operation, cuihua serving…

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;

    /** * Second verification, generate a QR code */
    @RequestMapping("/qrcode")
    public void qrcode(String username, HttpServletResponse response) {
        try (ServletOutputStream stream = response.getOutputStream()) {
            googleAuthenticatorService.genQRImage(username, stream);
        } catch (IOException e) {
            log.error("Error occurred", e); }}/** * Second validation, enter the 6-digit */ on eagle2FA APP
    @RequestMapping("/verify")
    public String verify(String username, int code) {

        boolean validCode = googleAuthenticatorService.validCode(username, code);

        if (validCode){

            return "index";
        }

        return "error"; }}Copy the code

Copy may have an error message, do not care about it, the following disk he.

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
 * @createThe 2018-09-06 * / calm
@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;

    /** * Since this is just a demo, the DAO does not actually operate on the database, so the real scenario would have to persist */
    @Autowired
    private UserDao userDao;

    @PostConstruct
    public void init(a) {
        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 is stored in the databaseuserDao.saveUserCredentials(userName, secretKey); }}); log.info("GoogleAuthenticator initialized successfully");
    }

    /** * Generate a qr code link */
    private String getQrUrl(String username) {
        // A new secretKey is generated every time a createCredentials call is made
        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);
    }
 /** * Generate a QR code file */
    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(); }}/** * Generate a QR code */
    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(); }}}Copy the code

UserDao(Simulating database operations)

package com.demo.twostep.dao;
public interface UserDao {

    String getSecretKey(String userName);

    boolean saveUserCredentials(String userName, String secretKey);
}
Copy the code

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; }}Copy the code

OK, the background code is done, it’s time for us to test, boss, where are the chopsticks, I’m going to taste the food…

Start by creating three pages:

login.html

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Landing page</title>
</head>
<body>

<form action="/verify" method="post">User name:<input type="text" name="username" id="username">
    <br>The secret code:<input type="password" name="password" id="password">
    <br>Verification code:<input type="text" name="code"><input type="button" value="Get" id="button">
    <br>
    <input type="submit" value="Login">
</form>

<script type="application/javascript">

    var button  =document.getElementById("button");

    button.onclick = function () {

        // Get the information entered by the user
        var username = document.getElementById("username").value;

        var password = document.getElementById("password").value;

        if (username == ' ' || password == ' '){

            alert("Please fill in the information");

        }else {

            window.location.href = '/qrcode? username=fc'; }}</script>
</body>
</html>
Copy the code

index.html:

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Success page</title>
</head>
<body>Successful landing...</body>
</html>
Copy the code

error.html:

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Failure of the page</title>
</head>
<body>Login failed...</body>
</html>
Copy the code

Start our project running, hahaha (this is our startup class)

 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); }}Copy the code

Landing Page:

The obtained QR code:

We use App scan, we will get a verification code (you can set the expiration time of the verification code)

Getting this means success.

Enter the corresponding verification code, login can access this page.

OKOK, this tutorial is complete, thanks for browsing… Do you have any questions to comment or leave a comment in the comments section

Monday ♥& Hearts