springboot扫码实现登录_springboot怎么扫描mapper

前言:

本文基于SpringBoot框架实现了一个完整的扫码登录系统。

扫码登录流程:

  1. Web端向服务器请求生成唯一二维码
  2. 服务器生成二维码图片并返回
  3. 用户通过手机App扫描该二维码
  4. 手机App发送确认请求到服务器
  5. 服务器通知Web端登录成功
  6. Web端完成登录流程

后端实现

Maven依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>qrcode-login</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>qrcode-login</name>
    <description>SpringBoot QR Code Login Demo</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <!-- Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- WebSocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        
        <!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <!-- JSON -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        
        <!-- ZXing for QR Code generation -->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.5.1</version>
        </dependency>
        
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

配置文件

在application.yaml中添加配置:

spring:
  redis:
    host: localhost
    port: 6379
    password:
    database: 0
    timeout: 5000ms
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 5

# 二维码配置
qrcode:
  # 二维码有效期(秒)
  expire:
    seconds: 300
  width: 100
  height: 100

代码:

package com.example.qrcodelogin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class QrcodeLoginApplication {
    public static void main(String[] args) {
        SpringApplication.run(QrcodeLoginApplication.class, args);
    }
}

Redis配置


import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        // 配置ObjectMapper,添加类型信息
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.activateDefaultTyping(
                objectMapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY
        );
        // 使用Jackson2JsonRedisSerializer作为值的序列化器
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // 设置key和value的序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

websocket配置

package com.example.qrcodelogin.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(qrCodeWebSocketHandler(), "/ws/qrcode")
                .setAllowedOrigins("*");
    }
    
    @Bean
    public QrCodeWebSocketHandler qrCodeWebSocketHandler() {
        return new QrCodeWebSocketHandler();
    }
    
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

WebSocket处理器

package com.example.qrcodelogin.config;

import com.example.qrcodelogin.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class QrCodeWebSocketHandler extends TextWebSocketHandler {
    
    // 存储所有WebSocket连接,key为二维码ID
    private static final Map<String, WebSocketSession> SESSIONS = new ConcurrentHashMap<>();
    
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        log.info("WebSocket connection established: {}", session.getId());
    }
    
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        log.info("Received message: {}", payload);
        
        Map<String, String> msgMap = JsonUtil.fromJson(payload, Map.class);
        if (msgMap != null && msgMap.containsKey("qrCodeId")) {
            String qrCodeId = msgMap.get("qrCodeId");
            log.info("Client subscribed to QR code: {}", qrCodeId);
            
            // 将会话与二维码ID关联
            SESSIONS.put(qrCodeId, session);
            
            // 发送确认消息
            session.sendMessage(new TextMessage("{"type":"CONNECTED","message":"Connected to QR code: " + qrCodeId + ""}"));
        }
    }
    
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        log.info("WebSocket connection closed: {}, status: {}", session.getId(), status);
        
        // 移除会话
        SESSIONS.entrySet().removeIf(entry -> entry.getValue().getId().equals(session.getId()));
    }
    
    // 向指定二维码ID的客户端发送消息
    public void sendMessage(String qrCodeId, Object message) {
        WebSocketSession session = SESSIONS.get(qrCodeId);
        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(JsonUtil.toJson(message)));
            } catch (IOException e) {
                log.error("Failed to send message to WebSocket client", e);
            }
        }
    }
}

QRCodeStatus.java - 二维码状态类

package com.example.qrcodelogin.model;

import lombok.Data;

@Data
public class QRCodeStatus {
    public static final String WAITING = "WAITING";   // 等待扫描
    public static final String SCANNED = "SCANNED";   // 已扫描
    public static final String CONFIRMED = "CONFIRMED"; // 已确认
    public static final String CANCELLED = "CANCELLED"; // 已取消
    public static final String EXPIRED = "EXPIRED";   // 已过期
    
    private String qrCodeId;     // 二维码ID
    private String status;       // 状态
    private UserInfo userInfo;   // 用户信息
    private long createTime;     // 创建时间
    
    public QRCodeStatus() {
        this.createTime = System.currentTimeMillis();
    }
    
    public QRCodeStatus(String qrCodeId, String status) {
        this.qrCodeId = qrCodeId;
        this.status = status;
        this.createTime = System.currentTimeMillis();
    }
}

UserInfo.java - 用户信息类

package com.example.qrcodelogin.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
    private String userId;
    private String username;
    private String avatar;
    private String email;
    private String token;
}

JsonUtil.java - JSON工具类

package com.example.qrcodelogin.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class JsonUtil {
    private static final ObjectMapper objectMapper = new ObjectMapper();
    
    public static String toJson(Object object) {
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.error("Convert object to json failed", e);
            return null;
        }
    }
    
    public static <T> T fromJson(String json, Class<T> clazz) {
        try {
            return objectMapper.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            log.error("Convert json to object failed", e);
            return null;
        }
    }
}

QR码生成工具类


package com.example.qrcodelogin.util;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class QRCodeUtil {
    
    /**
     * 生成二维码图片的字节数组
     */
    public static byte[] generateQRCodeImage(String text, int width, int height) throws WriterException, IOException {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        hints.put(EncodeHintType.MARGIN, 2);
        
        BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
        
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
        
        return outputStream.toByteArray();
    }
}

服务类

package com.example.qrcodelogin.service;

import com.example.qrcodelogin.config.QrCodeWebSocketHandler;
import com.example.qrcodelogin.model.QRCodeStatus;
import com.example.qrcodelogin.model.UserInfo;
import com.example.qrcodelogin.util.QRCodeUtil;
import com.google.zxing.WriterException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class QRCodeService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private QrCodeWebSocketHandler webSocketHandler;
    
    @Value("${qrcode.expire.seconds}")
    private long qrCodeExpireSeconds;
    
    @Value("${qrcode.width}")
    private int qrCodeWidth;
    
    @Value("${qrcode.height}")
    private int qrCodeHeight;
    
    private static final String QR_CODE_PREFIX = "qrcode:";
    
    /**
     * 生成二维码
     */
    public QRCodeStatus generateQRCode() {
        String qrCodeId = UUID.randomUUID().toString();
        QRCodeStatus qrCodeStatus = new QRCodeStatus(qrCodeId, QRCodeStatus.WAITING);
        
        // 存储到Redis并设置过期时间
        redisTemplate.opsForValue().set(QR_CODE_PREFIX + qrCodeId, qrCodeStatus, qrCodeExpireSeconds, TimeUnit.SECONDS);
        
        return qrCodeStatus;
    }
    
    /**
     * 生成二维码图片
     */
    public byte[] generateQRCodeImage(String qrCodeId, String baseUrl) {
        try {
            // 构建二维码内容
            String qrCodeContent = baseUrl + "/mobile.html?qrCodeId=" + qrCodeId;
            
            // 生成二维码图片
            return QRCodeUtil.generateQRCodeImage(qrCodeContent, qrCodeWidth, qrCodeHeight);
        } catch (WriterException | IOException e) {
            log.error("Failed to generate QR code image", e);
            return null;
        }
    }
    
    /**
     * 获取二维码状态
     */
    public QRCodeStatus getQRCodeStatus(String qrCodeId) {
        Object obj = redisTemplate.opsForValue().get(QR_CODE_PREFIX + qrCodeId);
        if (obj instanceof QRCodeStatus) {
            return (QRCodeStatus) obj;
        }
        return null;
    }
    
    /**
     * 更新二维码状态
     */
    public boolean updateQRCodeStatus(String qrCodeId, String status) {
        QRCodeStatus qrCodeStatus = getQRCodeStatus(qrCodeId);
        if (qrCodeStatus == null) {
            return false;
        }
        
        qrCodeStatus.setStatus(status);
        redisTemplate.opsForValue().set(QR_CODE_PREFIX + qrCodeId, qrCodeStatus, qrCodeExpireSeconds, TimeUnit.SECONDS);
        
        // 通过WebSocket发送状态更新
        Map<String, Object> message = new HashMap<>();
        message.put("type", "STATUS_CHANGE");
        message.put("status", status);
        webSocketHandler.sendMessage(qrCodeId, message);
        
        return true;
    }
    
    /**
     * 确认登录
     */
    public boolean confirmLogin(String qrCodeId, UserInfo userInfo) {
        QRCodeStatus qrCodeStatus = getQRCodeStatus(qrCodeId);
        if (qrCodeStatus == null || !QRCodeStatus.SCANNED.equals(qrCodeStatus.getStatus())) {
            return false;
        }
        
        qrCodeStatus.setStatus(QRCodeStatus.CONFIRMED);
        qrCodeStatus.setUserInfo(userInfo);
        redisTemplate.opsForValue().set(QR_CODE_PREFIX + qrCodeId, qrCodeStatus, qrCodeExpireSeconds, TimeUnit.SECONDS);
        
        // 通过WebSocket发送状态更新
        Map<String, Object> message = new HashMap<>();
        message.put("type", "STATUS_CHANGE");
        message.put("status", QRCodeStatus.CONFIRMED);
        message.put("userInfo", userInfo);
        webSocketHandler.sendMessage(qrCodeId, message);
        
        return true;
    }
    
    /**
     * 取消登录
     */
    public boolean cancelLogin(String qrCodeId) {
        QRCodeStatus qrCodeStatus = getQRCodeStatus(qrCodeId);
        if (qrCodeStatus == null) {
            return false;
        }
        
        qrCodeStatus.setStatus(QRCodeStatus.CANCELLED);
        redisTemplate.opsForValue().set(QR_CODE_PREFIX + qrCodeId, qrCodeStatus, qrCodeExpireSeconds, TimeUnit.SECONDS);
        
        // 通过WebSocket发送状态更新
        Map<String, Object> message = new HashMap<>();
        message.put("type", "STATUS_CHANGE");
        message.put("status", QRCodeStatus.CANCELLED);
        webSocketHandler.sendMessage(qrCodeId, message);
        
        return true;
    }
    
    /**
     * 定时检查并清理过期的二维码
     */
    @Scheduled(fixedRate = 60000) // 每分钟执行一次
    public void cleanExpiredQRCodes() {
        long currentTime = System.currentTimeMillis();
        long expireTime = currentTime - qrCodeExpireSeconds * 1000;
        
        // 查找所有二维码记录
        Set<String> keys = redisTemplate.keys(QR_CODE_PREFIX + "*");
        if (keys == null || keys.isEmpty()) {
            return;
        }
        
        for (String key : keys) {
            Object obj = redisTemplate.opsForValue().get(key);
            if (obj instanceof QRCodeStatus) {
                QRCodeStatus status = (QRCodeStatus) obj;
                
                // 检查创建时间是否超过过期时间
                if (status.getCreateTime() < expireTime && !QRCodeStatus.EXPIRED.equals(status.getStatus())) {
                    status.setStatus(QRCodeStatus.EXPIRED);
                    redisTemplate.opsForValue().set(key, status, 60, TimeUnit.SECONDS); // 设置一个短的过期时间,让客户端有机会收到过期通知
                    
                    // 发送过期通知
                    Map<String, Object> message = new HashMap<>();
                    message.put("type", "STATUS_CHANGE");
                    message.put("status", QRCodeStatus.EXPIRED);
                    webSocketHandler.sendMessage(status.getQrCodeId(), message);
                    
                    log.info("QR code expired: {}", status.getQrCodeId());
                }
            }
        }
    }
}

UserService.java - 用户服务类

package com.example.qrcodelogin.service;

import com.example.qrcodelogin.model.UserInfo;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
public class UserService {
    
    // 模拟用户数据库
    private static final Map<String, UserInfo> USER_DB = new HashMap<>();
    
    static {
        // 添加一些测试用户
        USER_DB.put("user1", new UserInfo(
                "user1",
                "张三",
                "https://api.dicebear.com/7.x/avataaars/svg?seed=user1",
                "zhangsan@example.com",
                null
        ));
        
        USER_DB.put("user2", new UserInfo(
                "user2",
                "李四",
                "https://api.dicebear.com/7.x/avataaars/svg?seed=user2",
                "lisi@example.com",
                null
        ));
    }
    
    /**
     * 获取所有用户
     */
    public Map<String, UserInfo> getAllUsers() {
        return USER_DB;
    }
    
    /**
     * 模拟登录
     */
    public UserInfo login(String userId) {
        UserInfo userInfo = USER_DB.get(userId);
        if (userInfo != null) {
            // 生成一个新的token
            String token = UUID.randomUUID().toString();
            userInfo.setToken(token);
            return userInfo;
        }
        return null;
    }
    
    /**
     * 验证token
     */
    public UserInfo validateToken(String token) {
        // 简单模拟,实际应用中应该有更复杂的token验证逻辑
        for (UserInfo user : USER_DB.values()) {
            if (token != null && token.equals(user.getToken())) {
                return user;
            }
        }
        return null;
    }
}

QRCodeController.java - 二维码相关API

package com.example.qrcodelogin.controller;

import com.example.qrcodelogin.model.QRCodeStatus;
import com.example.qrcodelogin.model.UserInfo;
import com.example.qrcodelogin.service.QRCodeService;
import com.example.qrcodelogin.service.UserService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/qrcode")
public class QRCodeController {
    
    @Autowired
    private QRCodeService qrCodeService;
    
    @Autowired
    private UserService userService;
    
    /**
     * 生成二维码
     */
    @GetMapping("/generate")
    public ResponseEntity<QRCodeStatus> generateQRCode() {
        QRCodeStatus qrCodeStatus = qrCodeService.generateQRCode();
        log.info("Generated QR code: {}", qrCodeStatus.getQrCodeId());
        return ResponseEntity.ok(qrCodeStatus);
    }
    
    /**
     * 获取二维码图片
     */
    @GetMapping(value = "/image/{qrCodeId}", produces = MediaType.IMAGE_PNG_VALUE)
    public ResponseEntity<byte[]> getQRCodeImage(@PathVariable String qrCodeId, HttpServletRequest request) {
        // 获取基础URL
        String baseUrl = request.getScheme() + "://" + request.getServerName();
        if (request.getServerPort() != 80 && request.getServerPort() != 443) {
            baseUrl += ":" + request.getServerPort();
        }
        
        byte[] qrCodeImage = qrCodeService.generateQRCodeImage(qrCodeId, baseUrl);
        if (qrCodeImage != null) {
            return ResponseEntity.ok()
                    .contentType(MediaType.IMAGE_PNG)
                    .body(qrCodeImage);
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
    }
    
    /**
     * 扫描二维码
     */
    @PostMapping("/scan")
    public ResponseEntity<String> scanQRCode(@RequestBody Map<String, String> request) {
        String qrCodeId = request.get("qrCodeId");
        if (qrCodeId == null) {
            return ResponseEntity.badRequest().body("QR code ID is required");
        }
        
        boolean updated = qrCodeService.updateQRCodeStatus(qrCodeId, QRCodeStatus.SCANNED);
        if (!updated) {
            return ResponseEntity.badRequest().body("Invalid QR code");
        }
        
        log.info("QR code scanned: {}", qrCodeId);
        return ResponseEntity.ok("Scanned successfully");
    }
    
    /**
     * 确认登录
     */
    @PostMapping("/confirm")
    public ResponseEntity<String> confirmLogin(@RequestBody ConfirmLoginRequest request) {
        if (request.getQrCodeId() == null || request.getUserId() == null) {
            return ResponseEntity.badRequest().body("QR code ID and user ID are required");
        }
        
        // 模拟用户登录
        UserInfo userInfo = userService.login(request.getUserId());
        if (userInfo == null) {
            return ResponseEntity.badRequest().body("User not found");
        }
        
        boolean confirmed = qrCodeService.confirmLogin(request.getQrCodeId(), userInfo);
        if (!confirmed) {
            return ResponseEntity.badRequest().body("Invalid QR code or status");
        }
        
        log.info("Login confirmed: {}, user: {}", request.getQrCodeId(), request.getUserId());
        return ResponseEntity.ok("Login confirmed successfully");
    }
    
    /**
     * 取消登录
     */
    @PostMapping("/cancel")
    public ResponseEntity<String> cancelLogin(@RequestBody Map<String, String> request) {
        String qrCodeId = request.get("qrCodeId");
        if (qrCodeId == null) {
            return ResponseEntity.badRequest().body("QR code ID is required");
        }
        
        boolean cancelled = qrCodeService.cancelLogin(qrCodeId);
        if (!cancelled) {
            return ResponseEntity.badRequest().body("Invalid QR code");
        }
        
        log.info("Login cancelled: {}", qrCodeId);
        return ResponseEntity.ok("Login cancelled successfully");
    }
    
    /**
     * 获取二维码状态
     */
    @GetMapping("/status/{qrCodeId}")
    public ResponseEntity<QRCodeStatus> getQRCodeStatus(@PathVariable String qrCodeId) {
        QRCodeStatus qrCodeStatus = qrCodeService.getQRCodeStatus(qrCodeId);
        if (qrCodeStatus == null) {
            return ResponseEntity.badRequest().body(null);
        }
        
        return ResponseEntity.ok(qrCodeStatus);
    }
    
    @Data
    public static class ConfirmLoginRequest {
        private String qrCodeId;
        private String userId;
    }
}

LoginController.java - 登录相关API

package com.example.qrcodelogin.controller;

import com.example.qrcodelogin.model.UserInfo;
import com.example.qrcodelogin.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/auth")
public class LoginController {
    
    @Autowired
    private UserService userService;
    
    /**
     * 验证token并获取用户信息
     */
    @PostMapping("/validate")
    public ResponseEntity<UserInfo> validateToken(@RequestBody Map<String, String> request) {
        String token = request.get("token");
        if (token == null) {
            return ResponseEntity.badRequest().body(null);
        }
        
        UserInfo userInfo = userService.validateToken(token);
        if (userInfo == null) {
            return ResponseEntity.badRequest().body(null);
        }
        
        log.info("Token validated for user: {}", userInfo.getUsername());
        return ResponseEntity.ok(userInfo);
    }
    
    /**
     * 获取可用的测试用户列表 (仅用于演示)
     */
    @GetMapping("/users")
    public ResponseEntity<Map<String, UserInfo>> getTestUsers() {
        return ResponseEntity.ok(userService.getAllUsers());
    }
}

总结

上述是后台实现springboot扫码实现登录的全业务流程代码。通过调用接口或前端页面触发登录操作流程,后台执行登录请求,实现扫码登录功能。

原文链接:,转发请注明来源!