How to Build a Scalable Front‑Back‑Separated Captcha Login with Spring Boot and Redis

This article walks through the problem of traditional session‑based captcha login, compares it with a modern front‑back‑separated architecture, and provides a step‑by‑step implementation using Spring Boot, Kaptcha, and Redis, including full code snippets, configuration classes, and flow diagrams.

Architect
Architect
Architect
How to Build a Scalable Front‑Back‑Separated Captcha Login with Spring Boot and Redis

Problem Overview

Traditional monolithic web applications store the generated captcha text in the HTTP session. The login request only needs to submit username, password and captcha, but the flow is tightly coupled to the session and the back‑end renders the page.

Monolithic Captcha Flow

Separated Architecture

In a front‑back‑separated microservice architecture the HTTP session is unavailable. The solution moves captcha storage to Redis and adds a UUID token to bind the captcha to a specific request, making the login stateless.

Separated Captcha Generation

Separated Login Verification

Implementation

1. Maven Dependencies

<dependency>
  <groupId>com.github.penggle</groupId>
  <artifactId>kaptcha</artifactId>
  <version>2.3.2</version>
</dependency>

<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>

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
</dependency>

2. Redis Configuration

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(factory);
        return template;
    }
}

3. Kaptcha Configuration

@Configuration
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha producer() {
        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Properties p = new Properties();
        p.setProperty("kaptcha.border", "no");
        p.setProperty("kaptcha.border.color", "105,179,90");
        p.setProperty("kaptcha.textproducer.font.color", "black");
        p.setProperty("kaptcha.image.width", "110");
        p.setProperty("kaptcha.image.height", "40");
        p.setProperty("kaptcha.textproducer.char.string",
                "23456789abcdefghkmnpqrstuvwxyzABCDEFGHKMNPRSTUVWXYZ");
        p.setProperty("kaptcha.textproducer.font.size", "30");
        p.setProperty("kaptcha.textproducer.char.space", "3");
        p.setProperty("kaptcha.session.key", "code");
        p.setProperty("kaptcha.textproducer.char.length", "4");
        p.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        p.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        kaptcha.setConfig(new Config(p));
        return kaptcha;
    }
}

4. Captcha Generation Endpoint

@RestController
@RequestMapping("/captcha")
public class CaptchaController {
    @Autowired private DefaultKaptcha producer;
    @Autowired private CaptchaService captchaService;

    @GetMapping("/get")
    public CaptchaVO getCaptcha() throws IOException {
        String text = producer.createText();
        BufferedImage image = producer.createImage(text);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(image, "jpg", out);
        String base64 = "data:image/jpeg;base64," +
                new BASE64Encoder().encode(out.toByteArray())
                .replace("
", "").replace("\r", "");
        CaptchaVO vo = captchaService.cacheCaptcha(text);
        vo.setBase64Img(base64);
        return vo;
    }
}

5. Captcha Value Object

public class CaptchaVO {
    private String captchaKey; // UUID
    private Long expire;       // seconds
    private String base64Img;
    // getters and setters omitted for brevity
}

6. Captcha Service (Redis Cache)

@Service
public class CaptchaService {
    @Value("${server.session.timeout:300}")
    private Long timeout;
    @Autowired private RedisUtils redisUtils;
    private static final String PREFIX = "captcha:verification:";

    public CaptchaVO cacheCaptcha(String text) {
        String uuid = UUID.randomUUID().toString();
        redisUtils.set(PREFIX + uuid, text, timeout);
        CaptchaVO vo = new CaptchaVO();
        vo.setCaptchaKey(uuid);
        vo.setExpire(timeout);
        return vo;
    }
}

7. Login DTO

public class LoginDTO {
    private String userName;
    private String pwd;
    private String captchaKey;
    private String captcha;
    // getters and setters omitted
}

8. Login Endpoint

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired private RedisUtils redisUtils;

    @PostMapping("/login")
    public UserVO login(@RequestBody LoginDTO dto) {
        Object stored = redisUtils.get(dto.getCaptchaKey());
        if (stored == null) {
            throw new RuntimeException("Captcha expired");
        }
        if (!dto.getCaptcha().equals(stored)) {
            throw new RuntimeException("Captcha mismatch");
        }
        // Validate user credentials (omitted)
        // Generate token and build UserVO (omitted)
        return new UserVO();
    }
}

Result

Moving captcha storage from the HTTP session to Redis and introducing a UUID token removes session dependence, yielding a stateless login flow that fits front‑back‑separated microservice architectures.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaredisCaptchaSpringBootKaptchaFrontBackSeparationLoginSecurity
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.