This article outline

[TOC]

preface

In order to prevent the destruction of the world, in order to protect the peace of the world… If you say wrong, try again

In order to prevent the authentication system have been violent attacks, many systems have increased captcha efficacy, common picture is qr code, the industry safer is a message authentication code, and of course some puzzles captcha, join the qr code of artificial intelligence and so on, today our topic is the separation of front and back side pictures qr code login.

Login scheme with unseparated verification codes at the front and back ends

Traditional projects are based on session interaction, and both the front and back end are in a project, such as traditional SSH projects or some JSP systems. When the front end page triggers the request to obtain the verification code, the information in the verification code can be stored in the context, so only the user name, password and verification code are needed for login.

The verification code generation process is as follows

The login verification process is as follows

As you can see, the entire login process is still dependent on the Session context and the backend adjusts the page.

Verification code login scheme separated from the front and back ends

With constantly upgrade system and business, client-side code together before and after the project is more and more bloated, already can’t rapid iteration, and responsibility to distinguish, rushed into the arms of the front and back end separation, found that code and after separation of duties, development efficiency is higher and higher, the functional iteration is faster and faster, but previous login authentication code scheme is about to change.

The verification code generation process is as follows

Compared with the original scheme, the redis middleware is added, which is no longer stored in the session. However, how to distinguish the verification code generated by this request? So we added a unique identifier to distinguish them

The login verification process is as follows

It can be found that compared with the original distributed project login scheme based on the separation of the front and back ends, a Redis middleware and token return are added, and the context session is no longer dependent, and page adjustment is also changed from the back end to the front end

Hand off the wheel

There are a lot of wheels based on verification code. This paper takes Kaptcha as an example to integrate Kaptcha through Springboot project to achieve verification code generation and login scheme.

Introduce Kaptcha

Kaptcha is an open source captcha project based on SimpleCaptcha

The wheel I’m looking for is based on SimpleCaptcha secondary encapsulation, and Maven relies on the following

<! > <dependency> <groupId>com.github. Penggle </groupId> < artifactId > kaptcha < / artifactId > < version > 2.3.2 < / version > < / dependency >Copy the code

Create a new project and add dependencies

The main dependencies are SpringBoot, Kaptcha and Redis

pom.xml

<? xml version="1.0" encoding="UTF-8"? > <project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> < modelVersion > 4.0.0 < / modelVersion > < groupId > com. LZP < / groupId > < artifactId > kaptcha < / artifactId > < version > 1.0 - the SNAPSHOT < / version > < the parent > < groupId > org. Springframework. Boot < / groupId > The < artifactId > spring - the boot - starter - parent < / artifactId > < version > 2.3.0. RELEASE < / version > < relativePath / > <! -- lookup parent from repository --> </parent> <dependencies> <! > <dependency> <groupId>com.github. Penggle </groupId> < artifactId > kaptcha < / artifactId > < version > 2.3.2 < / version > < / dependency > < the dependency > <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <! Mons </groupId> </groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> < artifactId > fastjson < / artifactId > < version > 1.2.3 < / version > < / dependency > < the dependency > <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId>  </plugin> </plugins> </build> </project>Copy the code

Redis configuration classRedisConfig

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        returnredisTemplate; }}Copy the code

Verification code configuration classKaptchaConfig

@Configuration
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha producer(a){

        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border"."no");
        properties.setProperty("kaptcha.border.color"."105179, living");
        properties.setProperty("kaptcha.textproducer.font.color"."black");
        properties.setProperty("kaptcha.image.width"."110");
        properties.setProperty("kaptcha.image.height"."40");
        properties.setProperty("kaptcha.textproducer.char.string"."23456789abcdefghkmnpqrstuvwxyzABCDEFGHKMNPRSTUVWXYZ");
        properties.setProperty("kaptcha.textproducer.font.size"."30");
        properties.setProperty("kaptcha.textproducer.char.space"."3");
        properties.setProperty("kaptcha.session.key"."code");
        properties.setProperty("kaptcha.textproducer.char.length"."4");
        properties.setProperty("kaptcha.textproducer.font.names"."Song Style, Regular script, Microsoft Yahei");
// properties.setProperty("kaptcha.obscurificator.impl","com.xxx"); You can override the implementation class
        properties.setProperty("kaptcha.noise.impl"."com.google.code.kaptcha.impl.NoNoise");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);

        return defaultKaptcha;
    }
Copy the code

Captcha control layerCaptchaController

In order to facilitate the code to write a piece, pay attention to look

package com.lzp.kaptcha.controller;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.lzp.kaptcha.service.CaptchaService;
import com.lzp.kaptcha.vo.CaptchaVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

@RestController
@RequestMapping("/captcha")
public class CaptchaController {

    @Autowired
    private DefaultKaptcha producer;

    @Autowired
    private CaptchaService captchaService;

    @ResponseBody
    @GetMapping("/get")
    public CaptchaVO getCaptcha(a) throws IOException {

        // Generate a literal captcha
        String content = producer.createText();
        // Generate image captcha
        ByteArrayOutputStream outputStream = null;
        BufferedImage image = producer.createImage(content);

        outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "jpg", outputStream);
        // Base64 encodes byte arrays
        BASE64Encoder encoder = new BASE64Encoder();

        String str = "data:image/jpeg; base64,";
        String base64Img = str + encoder.encode(outputStream.toByteArray()).replace("\n"."").replace("\r"."");

        CaptchaVO captchaVO  =captchaService.cacheCaptcha(content);
        captchaVO.setBase64Img(base64Img);

        returncaptchaVO; }}Copy the code

Verification code returns an objectCaptchaVO

package com.lzp.kaptcha.vo;

public class CaptchaVO {
    /** * Verification code identifier */
    private String captchaKey;
    /** * Verification code expiration time */
    private Long expire;
    /** * base64 The character string */
    private String base64Img;

    public String getCaptchaKey(a) {
        return captchaKey;
    }

    public void setCaptchaKey(String captchaKey) {
        this.captchaKey = captchaKey;
    }

    public Long getExpire(a) {
        return expire;
    }

    public void setExpire(Long expire) {
        this.expire = expire;
    }

    public String getBase64Img(a) {
        return base64Img;
    }

    public void setBase64Img(String base64Img) {
        this.base64Img = base64Img; }}Copy the code

Redis wrapper classRedisUtils

Online random find, class inside indicate the source, will use, more code is not posted, the end of the code to obtain

Captcha method layerCaptchaService

package com.lzp.kaptcha.service;

import com.lzp.kaptcha.utils.RedisUtils;
import com.lzp.kaptcha.vo.CaptchaVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class CaptchaService {

    @Value("${server.session.timeout:300}")
    private Long timeout;

    @Autowired
    private RedisUtils redisUtils;


    private final String CAPTCHA_KEY = "captcha:verification:";

    public CaptchaVO cacheCaptcha(String captcha){
        // Generate a random identifier
        String captchaKey = UUID.randomUUID().toString();

        // Cache the verification code and set the expiration time
        redisUtils.set(CAPTCHA_KEY.concat(captchaKey),captcha,timeout);

        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaKey(captchaKey);
        captchaVO.setExpire(timeout);

        returncaptchaVO; }}Copy the code

User login object encapsulationLoginDTO

package com.lzp.kaptcha.dto;

public class LoginDTO {

    private String userName;

    private String pwd;

    private String captchaKey;

    private String captcha;

    public String getUserName(a) {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPwd(a) {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getCaptchaKey(a) {
        return captchaKey;
    }

    public void setCaptchaKey(String captchaKey) {
        this.captchaKey = captchaKey;
    }

    public String getCaptcha(a) {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha; }}Copy the code

Login control layerUserController

I wrote the logical code, I believe you all understand

package com.lzp.kaptcha.controller;

import com.lzp.kaptcha.dto.LoginDTO;
import com.lzp.kaptcha.utils.RedisUtils;
import com.lzp.kaptcha.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private RedisUtils redisUtils;

    @PostMapping("/login")
    public UserVO login(@RequestBody LoginDTO loginDTO)  {
        Object captch = redisUtils.get(loginDTO.getCaptchaKey());
        if(captch == null) {// throw the verification code has expired
        }
        if(! loginDTO.getCaptcha().equals(captch)){// throw verification code error
        }
        // Query user information

        // Check whether the user does not throw a username or password error

        // Check whether the password is correct. If not, throw the username and password error

        // Construct the user object returned to the front end and encapsulate the information and generate tokens

        return newUserVO(); }}Copy the code

Obtain and view the verification code

The code for

Code has been uploaded to the address library, remember to add a star mark oh!

Github.com/pengziliu/G…

Check back for more highlights