精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

SpringBoot 掃碼登錄全流程:UUID 生成、狀態(tài)輪詢、授權(quán)回調(diào)詳解

開發(fā) 前端
用戶使用手機(jī)端應(yīng)用掃描二維碼,手機(jī)端應(yīng)用攜帶掃碼信息請求后端,后端驗(yàn)證掃碼信息并標(biāo)記二維碼為已掃描狀態(tài),同時返回授權(quán)頁面或直接進(jìn)行授權(quán)操作。

前言

在移動互聯(lián)網(wǎng)時代,掃碼登錄以其便捷性和安全性,成為眾多應(yīng)用首選的登錄方式。

技術(shù)原理

掃碼登錄的核心流程基于WebSocket、Redis等技術(shù),主要包含以下幾個關(guān)鍵步驟:

  • 生成二維碼:用戶點(diǎn)擊掃碼登錄后,后端生成一個唯一的UUID作為標(biāo)識,將該標(biāo)識與用戶設(shè)備信息等關(guān)聯(lián),存儲到Redis中,并生成包含此UUID的二維碼返回給前端展示。
  • 前端輪詢或WebSocket監(jiān)聽:前端通過輪詢接口或使用WebSocket長連接,不斷向后端查詢二維碼對應(yīng)的登錄狀態(tài)。
  • 掃碼與授權(quán):用戶使用手機(jī)端應(yīng)用掃描二維碼,手機(jī)端應(yīng)用攜帶掃碼信息請求后端,后端驗(yàn)證掃碼信息并標(biāo)記二維碼為已掃描狀態(tài),同時返回授權(quán)頁面或直接進(jìn)行授權(quán)操作。
  • 完成登錄:授權(quán)通過后,后端更新Redis中二維碼對應(yīng)的登錄狀態(tài)為已登錄,前端監(jiān)聽到登錄狀態(tài)變化后,完成登錄流程,跳轉(zhuǎn)至相應(yīng)頁面。

實(shí)現(xiàn)

演示效果

輪詢方式

圖片圖片

websocket

圖片圖片

引入依賴

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

配置

spring:
  redis:
    host: localhost
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
  session:
    store-type: redis

生成二維碼

/**
 * 二維碼生成工具類
 */
public class QRCodeUtil {
    
    private static final int WIDTH = 300;
    private static final int HEIGHT = 300;
    private static final String FORMAT = "png";
    
    /**
     * 生成二維碼字節(jié)數(shù)組
     * @param content 二維碼內(nèi)容
     * @return 二維碼圖片字節(jié)數(shù)組
     */
    public static byte[] generateQRCode(String content) throws WriterException, IOException {
        Map<EncodeHintType, Object> hints = new HashMap<>();
        // 設(shè)置字符編碼
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        // 設(shè)置容錯級別
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 設(shè)置邊距
        hints.put(EncodeHintType.MARGIN, 1);
        
        BitMatrix bitMatrix = new MultiFormatWriter().encode(
                content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);
        
        BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);
        
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, FORMAT, outputStream);
        
        return outputStream.toByteArray();
    }
    
    /**
     * 生成二維碼Base64字符串
     */
    public static String generateQRCodeBase64(String content) throws WriterException, IOException {
        byte[] bytes = generateQRCode(content);
        return "data:image/png;base64," + java.util.Base64.getEncoder().encodeToString(bytes);
    }
}

定義常量和實(shí)體類

/**
 * 常量類
 */
public class Constants {
    // Redis 中二維碼狀態(tài)的前綴
    public static final String QR_CODE_PREFIX = "qr:code:";
    // 二維碼過期時間(秒)
    public static final long QR_CODE_EXPIRE = 5 * 60;
}

/**
 * 二維碼狀態(tài)枚舉
 */
public enum QRCodeStatus {
    WAITING("waiting", "等待掃描"),
    SCANNED("scanned", "已掃描"),
    CONFIRMED("confirmed", "已確認(rèn)"),
    CANCELLED("cancelled", "已取消"),
    EXPIRED("expired", "已過期"),
    ERROR("error", "錯誤");
    
    private String code;
    private String message;
    
    QRCodeStatus(String code, String message) {
        this.code = code;
        this.message = message;
    }
    
    // getter 方法
    public String getCode() {
        return code;
    }
    
    public String getMessage() {
        return message;
    }
}

/**
 * WebSocket消息實(shí)體
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WebSocketMessage {
    private String uuid;
    private QRCodeStatus status;
    private String message;
    private Object data;
}

/**
 * 用戶信息實(shí)體
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
    private Long userId;
    private String username;
    private String nickname;
    private String avatar;
}

配置WebSocket

/**
 * WebSocket配置
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // 啟用簡單消息代理,前綴為/topic的消息會被代理轉(zhuǎn)發(fā)到訂閱了相應(yīng)主題的客戶端
        config.enableSimpleBroker("/topic");
        // 客戶端發(fā)送消息的前綴
        config.setApplicationDestinationPrefixes("/app");
    }
    
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注冊STOMP端點(diǎn),客戶端通過此端點(diǎn)連接到WebSocket服務(wù)器
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }
    
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxTextMessageBufferSize(8192);
        container.setMaxBinaryMessageBufferSize(8192);
        return container;
    }
}

二維碼服務(wù)實(shí)現(xiàn)

/**
 * 二維碼服務(wù)
 */
@Service
public class QRCodeService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 創(chuàng)建新的二維碼
     * @return 二維碼UUID
     */
    public String createQRCode() {
        String uuid = java.util.UUID.randomUUID().toString();
        String redisKey = Constants.QR_CODE_PREFIX + uuid;

        // 存儲二維碼狀態(tài)到Redis
        redisTemplate.opsForHash().put(redisKey, "status", QRCodeStatus.WAITING.getCode());
        redisTemplate.expire(redisKey, Constants.QR_CODE_EXPIRE, TimeUnit.SECONDS);
        return uuid;
    }

    /**
     * 更新二維碼狀態(tài)
     * @param uuid 二維碼UUID
     * @param status 新狀態(tài)
     */
    public void updateQRCodeStatus(String uuid, QRCodeStatus status) {
        String redisKey = Constants.QR_CODE_PREFIX + uuid;
        redisTemplate.opsForHash().put(redisKey, "status", status.getCode());
        redisTemplate.expire(redisKey, getRemainingTime(uuid), TimeUnit.SECONDS);
    }

    /**
     * 更新二維碼狀態(tài)并關(guān)聯(lián)用戶信息
     * @param uuid 二維碼UUID
     * @param status 新狀態(tài)
     * @param userInfo 用戶信息
     */
    public void updateQRCodeStatusWithUser(String uuid, QRCodeStatus status, UserInfo userInfo) {
        String redisKey = Constants.QR_CODE_PREFIX + uuid;
        // 使用Hash結(jié)構(gòu)存儲狀態(tài)和用戶信息
        redisTemplate.opsForHash().put(redisKey, "status", status.getCode());
        redisTemplate.opsForHash().put(redisKey, "userInfo", userInfo);
        redisTemplate.expire(redisKey, getRemainingTime(uuid), TimeUnit.SECONDS);
    }

    /**
     * 獲取二維碼狀態(tài)
     * @param uuid 二維碼UUID
     * @return 狀態(tài)
     */
    public QRCodeStatus getQRCodeStatus(String uuid) {
        String redisKey = Constants.QR_CODE_PREFIX + uuid;
        Object status = redisTemplate.opsForHash().get(redisKey, "status");

        if (status == null) {
            return QRCodeStatus.EXPIRED;
        }

        for (QRCodeStatus qrCodeStatus : QRCodeStatus.values()) {
            if (qrCodeStatus.getCode().equals(status.toString())) {
                return qrCodeStatus;
            }
        }

        return QRCodeStatus.ERROR;
    }

    /**
     * 獲取二維碼關(guān)聯(lián)的用戶信息
     * @param uuid 二維碼UUID
     * @return 用戶信息
     */
    public UserInfo getUserInfo(String uuid) {
        String redisKey = Constants.QR_CODE_PREFIX + uuid;
        return (UserInfo) redisTemplate.opsForHash().get(redisKey, "userInfo");
    }

    /**
     * 獲取Redis鍵的剩余時間
     * @param uuid 二維碼UUID
     * @return 剩余時間(秒)
     */
    private Long getRemainingTime(String uuid) {
        String redisKey = Constants.QR_CODE_PREFIX + uuid;
        return redisTemplate.getExpire(redisKey, TimeUnit.SECONDS);
    }

    /**
     * 使二維碼過期
     * @param uuid 二維碼UUID
     */
    public void expireQRCode(String uuid) {
        String redisKey = Constants.QR_CODE_PREFIX + uuid;
        redisTemplate.delete(redisKey);
    }
}

后端控制器實(shí)現(xiàn)

/**
 * 登錄控制器
 */
@RestController
@RequestMapping("/api/login")
public class LoginController {

    @Autowired
    private QRCodeService qrCodeService;

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    /**
     * 生成二維碼
     */
    @GetMapping("/qrCode")
    public Map<String, Object> generateQRCode() throws Exception {
        String uuid = qrCodeService.createQRCode();
        String qrCodeBase64 = QRCodeUtil.generateQRCodeBase64(uuid);

        Map<String, Object> result = new HashMap<>();
        result.put("uuid", uuid);
        result.put("qrCode", qrCodeBase64);
        result.put("expireTime", Constants.QR_CODE_EXPIRE);

        return result;
    }

    /**
     * 處理掃碼請求
     */
    @PostMapping("/scan")
    public Map<String, Object> scanQRCode(@RequestBody Map<String, String> request) {

        String uuid = request.get("uuid");
        Long userId = Long.parseLong(request.get("userId"));
        Map<String, Object> result = new HashMap<>();

        // 檢查二維碼是否存在且有效
        QRCodeStatus status = qrCodeService.getQRCodeStatus(uuid);
        if (status != QRCodeStatus.WAITING) {
            result.put("success", false);
            result.put("message", "二維碼無效或已過期");
            return result;
        }

        // 獲取用戶信息(這里應(yīng)該從數(shù)據(jù)庫查詢,簡化示例)
        UserInfo userInfo = new UserInfo(userId, "一安", "一安未來", "https://picsum.photos/200/200");

        // 更新二維碼狀態(tài)為已掃描
        qrCodeService.updateQRCodeStatusWithUser(uuid, QRCodeStatus.SCANNED, userInfo);

        // 通過WebSocket通知前端二維碼已被掃描
        WebSocketMessage message = new WebSocketMessage(
                uuid,
                QRCodeStatus.SCANNED,
                "二維碼已被掃描,請確認(rèn)登錄",
                userInfo
        );
        messagingTemplate.convertAndSend("/topic/qr/" + uuid, message);

        result.put("success", true);
        result.put("message", "掃碼成功,請在PC端確認(rèn)登錄");
        return result;
    }

    /**
     * 處理授權(quán)請求
     */
    @PostMapping("/authorize")
    public Map<String, Object> authorize(@RequestBody Map<String, String> request) {


        String uuid = request.get("uuid");
        Boolean confirm = Boolean.parseBoolean(request.get("confirm"));
        Map<String, Object> result = new HashMap<>();

        // 檢查二維碼是否存在且已掃描
        QRCodeStatus status = qrCodeService.getQRCodeStatus(uuid);
        if (status != QRCodeStatus.SCANNED) {
            result.put("success", false);
            result.put("message", "二維碼狀態(tài)無效");
            return result;
        }

        if (confirm) {
            // 獲取用戶信息
            UserInfo userInfo = qrCodeService.getUserInfo(uuid);

            // 更新二維碼狀態(tài)為已確認(rèn)
            qrCodeService.updateQRCodeStatusWithUser(uuid, QRCodeStatus.CONFIRMED, userInfo);

            // 通過WebSocket通知前端登錄成功
            WebSocketMessage message = new WebSocketMessage(
                    uuid,
                    QRCodeStatus.CONFIRMED,
                    "登錄成功",
                    userInfo
            );
            messagingTemplate.convertAndSend("/topic/qr/" + uuid, message);

            result.put("success", true);
            result.put("message", "授權(quán)成功");
            result.put("userInfo", userInfo);
        } else {
            // 更新二維碼狀態(tài)為已取消
            qrCodeService.updateQRCodeStatus(uuid, QRCodeStatus.ERROR);

            // 通過WebSocket通知前端登錄已取消
            WebSocketMessage message = new WebSocketMessage(
                    uuid,
                    QRCodeStatus.ERROR,
                    "用戶取消登錄",
                    null
            );
            messagingTemplate.convertAndSend("/topic/qr/" + uuid, message);

            result.put("success", false);
            result.put("message", "用戶取消登錄");
        }

        return result;
    }

    /**
     * 檢查二維碼狀態(tài)(輪詢方式)
     */
    @GetMapping("/checkStatus")
    public Map<String, Object> checkStatus(@RequestParam String uuid) {
        Map<String, Object> result = new HashMap<>();

        QRCodeStatus status = qrCodeService.getQRCodeStatus(uuid);
        result.put("status", status.getCode());
        result.put("message", status.getMessage());

        if (status == QRCodeStatus.SCANNED) {
            // 獲取用戶信息
            UserInfo userInfo = qrCodeService.getUserInfo(uuid);
            result.put("userInfo", userInfo);
        }

        return result;
    }
}

WebSocket消息處理器

/**
 * WebSocket消息處理器
 */
@Controller
public class WebSocketController {
    
    /**
     * 處理客戶端訂閱二維碼狀態(tài)的消息
     */
    @MessageMapping("/subscribeQr")
    @SendTo("/topic/qr/{uuid}")
    public WebSocketMessage subscribeQr(@PathVariable String uuid) {
        // 這里可以根據(jù)需要返回初始狀態(tài)
        return new WebSocketMessage(
                uuid, 
                QRCodeStatus.WAITING, 
                "等待掃描", 
                null
        );
    }
}

總結(jié)

二維碼生成階段

  1. 用戶打開Web登錄頁面
  2. 前端請求后端生成唯一的二維碼ID
  3. 后端生成二維碼ID,初始狀態(tài)為等待掃描
  4. 后端將二維碼ID及狀態(tài)存儲到Redis
  5. 后端生成包含二維碼ID的二維碼圖片并返回給前端
  6. 前端建立WebSocket連接,準(zhǔn)備接收狀態(tài)更新(輪詢方式不需要)

掃描確認(rèn)階段

  1. 用戶通過移動端App掃描二維碼,獲取二維碼ID
  2. 移動端發(fā)送掃描請求到服務(wù)端
  3. 服務(wù)端更新二維碼狀態(tài)為已掃描
  4. 服務(wù)端通過WebSocket推送狀態(tài)變更到Web端(輪詢方式不需要)
  5. Web端更新UI顯示已掃描狀態(tài)
  6. 移動端顯示用戶選擇界面
  7. 用戶在移動端選擇要登錄的賬號并確認(rèn)

登錄完成階段

  1. 移動端發(fā)送確認(rèn)登錄請求到服務(wù)端
  2. 服務(wù)端驗(yàn)證二維碼狀態(tài),生成用戶令牌
  3. 服務(wù)端更新二維碼狀態(tài)為已確認(rèn),并附帶用戶信息
  4. 服務(wù)端通過WebSocket推送登錄成功信息到Web端(輪詢方式不需要)
  5. Web端接收到登錄成功消息,獲取用戶信息
  6. Web端完成登錄流程,顯示用戶信息
  7. 移動端顯示登錄成功界面

功能優(yōu)化與擴(kuò)展

安全增強(qiáng)

  • 數(shù)據(jù)加密:對二維碼內(nèi)容和傳輸?shù)臄?shù)據(jù)進(jìn)行加密處理,防止信息泄露。
  • 防重放攻擊:為每個請求添加時間戳和簽名,防止請求被截獲后重放。
  • 訪問控制:限制對Redis數(shù)據(jù)的訪問權(quán)限,只允許授權(quán)的請求操作相關(guān)數(shù)據(jù)。
  • IP限制:對頻繁請求的IP進(jìn)行限制,防止惡意攻擊。

多設(shè)備支持

  • 設(shè)備管理:記錄和管理用戶登錄的設(shè)備信息,支持查看和管理已登錄設(shè)備。
  • 異地登錄提醒:當(dāng)檢測到用戶在異地登錄時,發(fā)送提醒通知。
  • 單點(diǎn)登錄:實(shí)現(xiàn)同一賬號在不同設(shè)備上的單點(diǎn)登錄功能。
責(zé)任編輯:武曉燕 來源: 一安未來
相關(guān)推薦

2025-06-04 04:00:00

Spring掃碼登錄免密認(rèn)證

2023-11-20 08:29:33

Vue微信掃碼授權(quán)登錄

2017-08-24 11:54:43

Linux日志定時輪循機(jī)制

2022-01-14 14:50:14

SpringBootymlJava

2022-01-13 17:24:04

SpringBootYml監(jiān)聽器

2020-03-08 15:39:41

微信掃碼登陸二維碼

2021-10-26 10:29:45

掃碼登錄功能

2009-08-19 16:40:35

C#回調(diào)

2014-06-18 09:25:07

HTTP

2022-06-10 06:55:21

JustAuthSpring

2020-04-15 16:30:24

掃碼登錄微信前端

2022-07-01 08:02:30

QQ掃碼登錄

2021-11-06 07:42:04

驗(yàn)證開發(fā)流程

2021-01-21 09:31:21

QQ微信移動應(yīng)用

2024-11-06 13:03:06

2024-03-07 07:59:37

2021-09-08 10:02:56

面試二維碼前端

2017-12-28 10:10:15

2019-05-21 15:15:22

掃碼乘車公交卡支付方式

2025-04-25 08:30:00

前端后端用戶登錄
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

免费在线一区二区| 亚洲一卡二卡三卡四卡五卡| 国产成人高潮免费观看精品| 中文字幕 自拍| 成人在线观看免费播放| 亚洲欧美自拍偷拍| 国产精品二区二区三区| 蜜臀精品一区二区三区| 国产精品福利在线观看播放| 亚洲第一区第一页| 久久久精品高清| 国产亚洲成av人片在线观看 | 成人欧美亚洲| 国产99久久久国产精品潘金| 国产精品国产亚洲伊人久久| 九九精品在线观看视频| 久久99国产精品视频| 欧美日韩二区三区| 欧美三级在线观看视频| 美女羞羞视频在线观看| 91免费国产视频网站| 国产一区二区视频在线观看| 成人免费看片98欧美| 影音先锋日韩精品| 亚洲网址你懂得| 天天躁日日躁狠狠躁免费麻豆| 欧美亚洲人成在线| 欧美视频中文在线看| 毛片在线视频观看| 亚洲乱亚洲乱妇| 久久一区二区三区四区| 不卡视频一区| 国产精品视频无码| 蜜臀va亚洲va欧美va天堂| 91精品国产乱码久久久久久久久| 国精品无码一区二区三区| jiujiure精品视频播放| 亚洲免费电影一区| 日本免费福利视频| eeuss鲁片一区二区三区| 欧美日韩一区二区在线观看| 日韩网址在线观看| 高清毛片在线观看| 亚洲国产视频在线| 久久久成人精品一区二区三区 | 99久热在线精品996热是什么| 亚洲澳门在线| 色天天综合狠狠色| 丰满的亚洲女人毛茸茸| 亚洲色图丝袜| 亚洲欧美国产精品专区久久| 男人的天堂影院| 日韩免费成人| 日韩欧美一区二区不卡| www.久久com| 精品入口麻豆88视频| 欧美日韩成人在线| 五月婷婷丁香综合网| 亚洲电影有码| 欧美三级中文字幕| 激情五月俺来也| 精品成人av| 欧美日韩小视频| 99九九99九九九99九他书对| 一区二区三区无毛| 欧美大片一区二区| chinese麻豆新拍video| 西野翔中文久久精品字幕| 亚洲欧美激情一区| 黄色激情小视频| 五月婷婷亚洲| 欧美激情网友自拍| 日韩中文字幕在线观看视频| 久热精品在线| 国产日韩综合一区二区性色av| 国产精品一区二区三区在线免费观看| 国内外成人在线| 国产99午夜精品一区二区三区 | 亚洲人成电影在线| 国产精品情侣呻吟对白视频| 我不卡影院28| 午夜精品久久久久久久白皮肤| 日韩av女优在线观看| 免费在线成人| 国产日韩中文字幕在线| 狠狠综合久久av一区二区| 久久综合999| 日韩亚洲视频| 伊人影院在线视频| 五月激情丁香一区二区三区| 国产精品99久久免费黑人人妻| 日韩欧美一区二区三区免费观看| 欧美日本一区二区三区| 激情综合激情五月| 欧美伦理在线视频| 久久99久久久久久久噜噜| 日韩一区二区视频在线| 美国三级日本三级久久99| 都市激情久久久久久久久久久| 国产精品一区二区免费在线观看| 黑人操日本美女| 精品福利电影| 国产精品日日摸夜夜添夜夜av| 国产伦理一区二区| 99视频超级精品| 一区二区视频国产| xxx性欧美| 欧美精品三级在线观看| 香港三级日本三级| 日韩精品免费| 97精品国产97久久久久久免费| 日本一区二区三区久久| 国产成人精品三级| 日本一区二区三区免费观看| v片在线观看| 日本精品视频一区二区三区| 国产老头和老头xxxx×| 成人在线国产| 欧美一级电影在线| 亚洲AV无码乱码国产精品牛牛| 久久久久久久久伊人| 国产一区 在线播放| 欧美综合影院| 亚洲欧美国产日韩中文字幕| 1024手机在线视频| 麻豆精品在线看| 欧美区高清在线| 国产精品一区二区日韩| 日韩视频123| 亚洲 欧美 国产 另类| 日韩在线一二三区| 另类视频在线观看+1080p| 男人天堂亚洲| 日韩天堂在线观看| 中日韩一级黄色片| 奇米色一区二区| 色视频一区二区三区| 一级毛片久久久| 亚洲精品国产精品久久清纯直播| 久久网中文字幕| 国产精品一级黄| 日本丰满少妇黄大片在线观看| 狂野欧美性猛交xxxx| 亚洲视频视频在线| 波多野结衣在线观看一区| 91麻豆国产香蕉久久精品| 日本韩国欧美在线观看| 卡一精品卡二卡三网站乱码| 欧美福利视频网站| 成 人 免费 黄 色| 亚洲国产精品一区二区www在线| 久久久精品视频国产| 在线成人直播| 91久色国产| av剧情在线观看| 日韩av网站电影| 午夜精品久久久久久久久久久久久蜜桃| 成人精品视频.| 黄色国产一级视频| 亚洲专区视频| 国产精品av电影| 五月天婷婷在线视频| 欧美精品一卡两卡| 中文字幕在线有码| 成人免费视频网站在线观看| 欧美成人高潮一二区在线看| 少妇久久久久| 国产精品美女久久久久av超清| 1024视频在线| 日韩视频一区在线观看| 天天操天天干视频| 国产日韩精品一区二区三区在线| 校园春色 亚洲色图| 99欧美视频| 国产伦精品一区二区三区高清版 | 国内精品久久久久影院优| 人人妻人人澡人人爽人人欧美一区 | 9999热视频| 99国产欧美久久久精品| 992kp快乐看片永久免费网址| 欧美wwwww| www 成人av com| 伊人久久在线| 久久精品欧美视频| 五月婷婷丁香花| 欧美三电影在线| 国产在线免费视频| 久久久电影一区二区三区| www.国产视频.com| 亚洲东热激情| 亚洲高清精品中出| 国产精品xxxav免费视频| 国产精品第一第二| 三级网站视频在在线播放| 亚洲免费电影一区| 亚洲国产福利视频| 欧美性大战久久久久久久蜜臀| 清纯粉嫩极品夜夜嗨av| 97精品国产露脸对白| 国产成年人视频网站| 99香蕉国产精品偷在线观看| 自拍亚洲欧美老师丝袜| 校花撩起jk露出白色内裤国产精品 | 亚洲二区av| 欧美一区二区.| av在线免费观看网址| 亚洲天堂男人天堂| 人妻无码一区二区三区久久99 | 国产精品igao视频| 国内老司机av在线| 日韩在线观看免费全集电视剧网站| 少妇高潮久久久| 欧美一区二区福利视频| 艳妇乳肉豪妇荡乳av无码福利 | 男人日女人bb视频| 欧美日韩精选| 亚洲一区在线直播| 精品九九在线| 久久精品二区| www.久久东京| 97操在线视频| 成人影院网站ww555久久精品| 日本中文字幕成人| 女厕盗摄一区二区三区| 欧美贵妇videos办公室| 老司机在线视频二区| 中文字幕日韩精品在线| 免费黄色在线视频网站| 亚洲精品成人av| 蜜桃av鲁一鲁一鲁一鲁俄罗斯的| 欧美一区二区精美| 国产精品久久久午夜夜伦鲁鲁| 在线精品视频小说1| 亚洲欧美综合另类| 韩曰欧美视频免费观看| 日韩视频免费观看高清| 亚洲高清久久久| 久久久全国免费视频| 亚洲精品免费在线观看| 性爱在线免费视频| 中国色在线观看另类| 欧美成人国产精品一区二区| 久久人人爽爽爽人久久久| 第四色在线视频| 久久免费偷拍视频| 国产精品无码一区二区三区| 91丨porny丨国产入口| 欧美精品欧美极品欧美激情| 97久久精品人人做人人爽50路 | 国产精品久久久久一区二区三区| 美女洗澡无遮挡| 久久久噜噜噜久噜久久综合| 扒开jk护士狂揉免费| 国产亚洲自拍一区| 九九热免费在线| 亚洲欧洲国产日韩| 懂色av懂色av粉嫩av| 亚洲自拍另类综合| 久久国产精品系列| 在线亚洲人成电影网站色www| 中国a一片一级一片| 欧美日本韩国一区| 国产wwwxxx| 亚洲第一网站男人都懂| 香蕉久久国产av一区二区| 亚洲丝袜一区在线| 麻豆视频在线观看免费| 欧美日韩高清在线观看| av影视在线看| 国产999在线观看| 2019中文亚洲字幕| 国产91免费视频| 国产一区二区三区探花 | 日本高清视频在线播放| 久久高清视频免费| 精精国产xxxx视频在线播放| 国产精品久久999| 精品91福利视频| 久久99精品久久久久久秒播放器| 精品国产乱码久久久久久蜜坠欲下 | 99c视频在线| 美女一区二区在线观看| 日本最新一区二区三区视频观看| 香蕉久久网站| 亚洲人成无码网站久久99热国产 | 亚洲精品午夜视频| 亚洲视频免费在线| 国内免费精品视频| 欧美片网站yy| 天天干天天做天天操| 日韩视频永久免费观看| 2021中文字幕在线| 国产精品扒开腿做| 成人另类视频| 亚洲免费视频一区| 亚洲乱亚洲高清| 91国内在线播放| 久久网站热最新地址| 国产av无码专区亚洲av毛网站| 色综合天天性综合| 性一交一乱一色一视频麻豆| 亚洲天堂视频在线观看| 国产白丝在线观看| 国产日韩欧美成人| 九一精品国产| 成人在线国产视频| 韩国av一区二区三区四区| 亚洲性猛交xxxx乱大交| 婷婷综合五月天| 国产精品人人妻人人爽| 亚洲午夜久久久影院| 波多野结衣中文字幕久久| 国产在线精品一区免费香蕉| 亚洲国产欧美日韩在线观看第一区 | 国产一区二区视频在线观看| 你懂的视频欧美| 欧美日韩不卡在线视频| 国产在线视视频有精品| 亚洲精品视频网址| 欧美视频13p| 日韩有码第一页| 欧美丰满少妇xxxx| 欧美不卡在线观看| 伊人久久大香线蕉精品| 日日夜夜免费精品视频| 亚洲欧美日本一区| 亚洲成人av电影| 亚洲国产精品suv| 日韩最新在线视频| 欧美91在线|欧美| 日本一区二区三区四区高清视频| 亚洲资源av| 中出视频在线观看| 午夜一区二区三区在线观看| 亚洲精品网站在线| 欧美激情va永久在线播放| 麻豆精品在线| 大地资源网在线观看免费官网| 国产一区二区伦理| www色aa色aawww| 欧美一区二区三区在线看| 黄av在线免费观看| 亚洲一区二区三区sesese| 911精品美国片911久久久| 中文字幕一区久久| 亚洲欧美视频在线观看| 国产精品一区二区人人爽 | 一二三四视频在线中文| 久久99精品久久久久久水蜜桃| 亚洲日本欧美| 欧美性xxxx图片| 一本色道久久综合亚洲精品按摩| 四虎影院在线域名免费观看| 5252色成人免费视频| 亚洲人成精品久久久 | 精品日韩在线播放| 国产精品综合二区| 精品亚洲永久免费| 亚洲国产日韩精品在线| 人狥杂交一区欧美二区| 欧美国产一区二区在线| 日本不卡一二三区黄网| 三级影片在线观看| 日韩手机在线导航| 极品在线视频| 污视频在线免费观看一区二区三区| 美女脱光内衣内裤视频久久影院| 久久高清内射无套| 亚洲成人av资源网| 老司机2019福利精品视频导航 | 亚洲精品1区| 真人bbbbbbbbb毛片| 色偷偷88欧美精品久久久| a√资源在线| 产国精品偷在线| 亚洲男人影院| 国产中文字幕久久| 欧美成人欧美edvon| 在线观看v片| 中文精品视频一区二区在线观看| 国产成人高清在线| 老熟妇一区二区三区| 久热99视频在线观看| 秋霞在线一区| 91插插插插插插插插| 亚洲综合精品自拍| 国产精品秘入口| av蓝导航精品导航| 久久久久免费| 国产这里有精品| 国产亚洲一区精品| 超碰97久久| 中文字幕永久有效| 欧美日韩国产精品一区二区不卡中文| av资源网在线观看| 粉嫩av四季av绯色av第一区| 热久久久久久久| a v视频在线观看| 久久国产精品电影| 精品国产一区探花在线观看|