Redis-2 Redis基础数据类型与基本使用

news2024/12/24 21:13:51

高级Redis应用进阶 一站式Redis解决方案-Redis-2 Redis基础数据类型与基本使用

 源代码在GitHub - 629y/food-social-contact-parent: redis项目-美食社交APP

1. Redis基本数据类型

1.字符串(strings)

set username zhangsan

get username

mset age 18 address bj

设置多个

mget username age

获取多个

incr num

incr-递增+1 num-变量从0开始

decr num

decr-递减-1

incrby num 2

incrby-以每次(+2)递增

decrby num 3

decrby-以每次(-3)递减

del num

删除数据

2.散列(hashes)

hset userInfo username zhangsan age 18 address bj

hget userInfo username

hget userInfo age

hmget userInfo username age

获取多个

hgetall userInfo

获取全部

hlen userInfo

获取key的数量

hincrby userInfo age 2

hincrby-(age)以每次(+2)递增

hdel userInfo age

删除age

del userInfo

删除userInfo

3.列表(lists)

lpush student zhangsan lisi wangwu

lpush-左插入

rpush student tianqi

rpush-右插入

lpop student

lpop-左弹出

rpop student

rpop-右弹出

lrange student 0 1

student中从下标为(0)到(1)排序查看

4.集合(sets)-去重

sadd nums 1 2 3

sadd nums 1 1 2 2 3 3

不会添加重复的

smembers nums

查看nums中的所有

srem nums 2

移除2

spop nums

随机弹出

sadd nums1 1 2 3

sadd nums2 2 3 4

sinter nums1 nums2

sinter-交集-2,3

sdiff nums1 nums2

(nums1)相对于(nums2)的差集-1

sunion nums1 nums2

并集-1,2,3,4

5.有序集合(sorted sets)

zadd rank 66 zhangsan 88 lisi 77 wangwu 99 zhaoliu

zadd rank 66 zhangsan 88 lisi 77 wangwu 99 zhaoliu

去重添加rank

zrange rank 0 3

从下标(0)到(3)按权重输出

zrangebyscore rank 77 99

将分数是在(77)-(99)之间的按权重输出

zrem rank zhaoliu

移除zhaoliu

zcard rank

统计rank中的数量

zcount rank 77 88

统计在包含在(77)-(88)之间的rank中的数量

zrank rank wangwu

查看wangwu在rank中的下标

zrevrank rank zhangsan

查看zhangsan在rank中的翻转下标

2. Redis之Sorted Set底层算法分析

二分查找

3. 认证中心需求分析

用户登录

4. 公共项目环境搭建

创建数据库

db_imooc.sql

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80018
 Source Host           : localhost:3306
 Source Schema         : db_imooc

 Target Server Type    : MySQL
 Target Server Version : 80018
 File Encoding         : 65001

 Date: 14/11/2020 19:06:41
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_dictionary
-- ----------------------------
DROP TABLE IF EXISTS `t_dictionary`;
CREATE TABLE `t_dictionary`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `data` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `display_order` int(11) NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 415 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_dictionary
-- ----------------------------
INSERT INTO `t_dictionary` VALUES (1, 'TableType           ', 'Regular|大厅', 0);
INSERT INTO `t_dictionary` VALUES (2, 'TableType           ', 'Bar|吧台', 0);
INSERT INTO `t_dictionary` VALUES (3, 'TableType           ', 'Window|靠窗', 0);
INSERT INTO `t_dictionary` VALUES (4, 'TableType           ', 'Outdoor|户外', 0);
INSERT INTO `t_dictionary` VALUES (5, 'TableType           ', 'Private|包间', 0);
INSERT INTO `t_dictionary` VALUES (7, 'RestaurantTag       ', '24|hours|24小时营业', 0);
INSERT INTO `t_dictionary` VALUES (8, 'RestaurantTag       ', 'Afternoon|tea|下午茶', 0);
INSERT INTO `t_dictionary` VALUES (9, 'RestaurantTag       ', 'All|you|can|eat|自助餐', 0);
INSERT INTO `t_dictionary` VALUES (10, 'RestaurantTag       ', 'Bistros|酒馆', 0);
INSERT INTO `t_dictionary` VALUES (11, 'RestaurantTag       ', 'Breakfast|早餐', 0);
INSERT INTO `t_dictionary` VALUES (12, 'RestaurantTag       ', 'Bund|view|外滩风景', 0);
INSERT INTO `t_dictionary` VALUES (13, 'RestaurantTag       ', 'Classic|Shanghai|老上海', 0);
INSERT INTO `t_dictionary` VALUES (14, 'RestaurantTag       ', 'Cocktails|鸡尾酒', 0);
INSERT INTO `t_dictionary` VALUES (15, 'RestaurantTag       ', 'Credit|cards|accepted|可刷卡', 0);
INSERT INTO `t_dictionary` VALUES (16, 'RestaurantTag       ', 'Delivery|可送外卖', 0);
INSERT INTO `t_dictionary` VALUES (17, 'RestaurantTag       ', 'Pet|friendly|宠物友好', 0);
INSERT INTO `t_dictionary` VALUES (18, 'RestaurantTag       ', 'Kids|friendly||适合小孩', 0);
INSERT INTO `t_dictionary` VALUES (19, 'RestaurantTag       ', 'Fine|dining|顶级餐厅', 0);
INSERT INTO `t_dictionary` VALUES (20, 'RestaurantTag       ', 'Free|parking|免费停车', 0);
INSERT INTO `t_dictionary` VALUES (21, 'RestaurantTag       ', 'Lounge|酒廊', 0);
INSERT INTO `t_dictionary` VALUES (22, 'RestaurantTag       ', 'Lunch|set|午市套餐', 0);
INSERT INTO `t_dictionary` VALUES (23, 'RestaurantTag       ', 'Group|dining|团体', 0);
INSERT INTO `t_dictionary` VALUES (24, 'RestaurantTag       ', 'Healthy|健康', 0);
INSERT INTO `t_dictionary` VALUES (25, 'RestaurantTag       ', 'Historic|building|历史建筑', 0);
INSERT INTO `t_dictionary` VALUES (26, 'RestaurantTag       ', 'Hotel|restaurant||酒店餐厅', 0);
INSERT INTO `t_dictionary` VALUES (27, 'RestaurantTag       ', 'Ice|cream|冰激凌', 0);
INSERT INTO `t_dictionary` VALUES (28, 'RestaurantTag       ', 'Late|night|dining|夜宵', 0);
INSERT INTO `t_dictionary` VALUES (29, 'RestaurantTag       ', 'Non-smoking|有无烟区', 0);
INSERT INTO `t_dictionary` VALUES (30, 'RestaurantTag       ', 'Notable|wine|list|葡萄酒', 0);
INSERT INTO `t_dictionary` VALUES (32, 'RestaurantTag       ', 'Outdoor|seating|户外餐桌', 0);
INSERT INTO `t_dictionary` VALUES (33, 'RestaurantTag       ', 'Performance|现场表演', 0);
INSERT INTO `t_dictionary` VALUES (34, 'RestaurantTag       ', 'Romantic||浪漫', 0);
INSERT INTO `t_dictionary` VALUES (35, 'RestaurantTag       ', 'Ramen|日式拉面', 0);
INSERT INTO `t_dictionary` VALUES (36, 'RestaurantTag       ', 'Salads|沙拉', 0);
INSERT INTO `t_dictionary` VALUES (37, 'RestaurantTag       ', 'Sandwiches & Delis|三明治&熟食', 0);
INSERT INTO `t_dictionary` VALUES (38, 'RestaurantTag       ', 'Smoothies|冰沙', 0);
INSERT INTO `t_dictionary` VALUES (39, 'RestaurantTag       ', 'Tapas|西班牙小吃', 0);
INSERT INTO `t_dictionary` VALUES (40, 'RestaurantTag       ', 'Themed|restaurant|主题餐厅', 0);
INSERT INTO `t_dictionary` VALUES (41, 'RestaurantTag       ', 'Villa|别墅', 0);
INSERT INTO `t_dictionary` VALUES (43, 'Cuisine             ', 'American|北美菜', 0);
INSERT INTO `t_dictionary` VALUES (45, 'Cuisine             ', 'Australian|澳洲菜', 0);
INSERT INTO `t_dictionary` VALUES (48, 'Cuisine             ', 'Barbecue|烧烤', 0);
INSERT INTO `t_dictionary` VALUES (50, 'Cuisine             ', 'Beijing|京菜', 0);
INSERT INTO `t_dictionary` VALUES (63, 'Cuisine             ', 'Dongbei|东北菜', 0);
INSERT INTO `t_dictionary` VALUES (65, 'Cuisine             ', 'Hunan|湘菜', 0);
INSERT INTO `t_dictionary` VALUES (68, 'Cuisine             ', 'French|法国菜', 0);
INSERT INTO `t_dictionary` VALUES (70, 'Cuisine             ', 'Fusion|创意菜', 0);
INSERT INTO `t_dictionary` VALUES (71, 'Cuisine             ', 'German|德国菜', 0);
INSERT INTO `t_dictionary` VALUES (72, 'Cuisine             ', 'Grocery|杂货', 0);
INSERT INTO `t_dictionary` VALUES (73, 'Cuisine             ', 'Halal|清真', 0);
INSERT INTO `t_dictionary` VALUES (76, 'Cuisine             ', 'Hot Pot|火锅', 0);
INSERT INTO `t_dictionary` VALUES (79, 'Cuisine             ', 'Indian|印度菜', 0);
INSERT INTO `t_dictionary` VALUES (80, 'Cuisine             ', 'Indonesian|印尼菜', 0);
INSERT INTO `t_dictionary` VALUES (81, 'Cuisine             ', 'Italian|意大利菜', 0);
INSERT INTO `t_dictionary` VALUES (82, 'Cuisine             ', 'Japanese|日本料理', 0);
INSERT INTO `t_dictionary` VALUES (84, 'Cuisine             ', 'Jiangxi|赣菜', 0);
INSERT INTO `t_dictionary` VALUES (88, 'Cuisine             ', 'Malaysian|马来西亚菜', 0);
INSERT INTO `t_dictionary` VALUES (91, 'Cuisine             ', 'Mediterranean|地中海菜', 0);
INSERT INTO `t_dictionary` VALUES (92, 'Cuisine             ', 'Mexican / Tex-Mex|墨西哥菜', 0);
INSERT INTO `t_dictionary` VALUES (94, 'Cuisine             ', 'Other|其他', 0);
INSERT INTO `t_dictionary` VALUES (102, 'Cuisine             ', 'Portuguese|葡国菜', 0);
INSERT INTO `t_dictionary` VALUES (103, 'Cuisine             ', 'Russian|俄国菜', 0);
INSERT INTO `t_dictionary` VALUES (104, 'Cuisine             ', 'Sandwiches & Delis|三明治&简食', 0);
INSERT INTO `t_dictionary` VALUES (107, 'Cuisine             ', 'Shaoxing|绍兴菜', 0);
INSERT INTO `t_dictionary` VALUES (111, 'Cuisine             ', 'Shanghainese|上海菜', 0);
INSERT INTO `t_dictionary` VALUES (112, 'Cuisine             ', 'Singaporean|新加坡菜', 0);
INSERT INTO `t_dictionary` VALUES (113, 'Cuisine             ', 'South American|南美菜', 0);
INSERT INTO `t_dictionary` VALUES (114, 'Cuisine             ', 'Spanish|西班牙菜', 0);
INSERT INTO `t_dictionary` VALUES (115, 'Cuisine             ', 'Steakhouse|牛排店', 0);
INSERT INTO `t_dictionary` VALUES (117, 'Cuisine             ', 'Taiwanese|台湾菜', 0);
INSERT INTO `t_dictionary` VALUES (118, 'Cuisine             ', 'Thai|泰国菜', 0);
INSERT INTO `t_dictionary` VALUES (121, 'Cuisine             ', 'Turkish|土耳其菜', 0);
INSERT INTO `t_dictionary` VALUES (122, 'Cuisine             ', 'Vegetarian|素食', 0);
INSERT INTO `t_dictionary` VALUES (123, 'Cuisine             ', 'Vietnamese|越南菜', 0);
INSERT INTO `t_dictionary` VALUES (124, 'Cuisine             ', 'Wine Bar|红酒吧', 0);
INSERT INTO `t_dictionary` VALUES (126, 'Cuisine             ', 'Yunnan|云南菜', 0);
INSERT INTO `t_dictionary` VALUES (129, 'Cuisine             ', 'Zhejiang|浙菜', 0);
INSERT INTO `t_dictionary` VALUES (130, 'nations             ', 'Afghanistan | 阿富汗', 0);
INSERT INTO `t_dictionary` VALUES (131, 'nations             ', 'Albania | 阿尔巴尼亚', 0);
INSERT INTO `t_dictionary` VALUES (132, 'nations             ', 'Algeria | 阿尔及利亚', 0);
INSERT INTO `t_dictionary` VALUES (133, 'nations             ', 'Andorra | 安道尔', 0);
INSERT INTO `t_dictionary` VALUES (134, 'nations             ', 'Angola | 安哥拉', 0);
INSERT INTO `t_dictionary` VALUES (135, 'nations             ', 'Argentina | 阿根廷', 0);
INSERT INTO `t_dictionary` VALUES (136, 'nations             ', 'Armenia | 亚美尼亚', 0);
INSERT INTO `t_dictionary` VALUES (137, 'nations             ', 'Australia | 澳大利亚', 0);
INSERT INTO `t_dictionary` VALUES (138, 'nations             ', 'Austria | 奥地利', 0);
INSERT INTO `t_dictionary` VALUES (139, 'nations             ', 'Azerbaijan | 阿塞拜疆', 0);
INSERT INTO `t_dictionary` VALUES (140, 'nations             ', 'Bahamas | 巴哈马', 0);
INSERT INTO `t_dictionary` VALUES (141, 'nations             ', 'Bahrain | 巴林', 0);
INSERT INTO `t_dictionary` VALUES (142, 'nations             ', 'Bangladesh | 孟加拉国', 0);
INSERT INTO `t_dictionary` VALUES (143, 'nations             ', 'Barbados | 巴巴多斯', 0);
INSERT INTO `t_dictionary` VALUES (144, 'nations             ', 'Belarus | 白俄罗斯', 0);
INSERT INTO `t_dictionary` VALUES (145, 'nations             ', 'Belgium | 比利时', 0);
INSERT INTO `t_dictionary` VALUES (146, 'nations             ', 'Belize | 伯利兹', 0);
INSERT INTO `t_dictionary` VALUES (147, 'nations             ', 'Benin | 柏林', 0);
INSERT INTO `t_dictionary` VALUES (148, 'nations             ', 'Bhutan | 不丹', 0);
INSERT INTO `t_dictionary` VALUES (149, 'nations             ', 'Bolivia | 玻利维亚', 0);
INSERT INTO `t_dictionary` VALUES (150, 'nations             ', 'Bosnia-Herzegovina | 波斯尼亚和黑塞哥维那', 0);
INSERT INTO `t_dictionary` VALUES (151, 'nations             ', 'Botswana | 博茨瓦纳', 0);
INSERT INTO `t_dictionary` VALUES (152, 'nations             ', 'Brazil | 巴西', 0);
INSERT INTO `t_dictionary` VALUES (154, 'nations             ', 'Brunei | 文莱', 0);
INSERT INTO `t_dictionary` VALUES (155, 'nations             ', 'Bulgaria | 保加利亚', 0);
INSERT INTO `t_dictionary` VALUES (156, 'nations             ', 'Burkina | 布基纳法索', 0);
INSERT INTO `t_dictionary` VALUES (157, 'nations             ', 'Burma (Myanmar) | 缅甸', 0);
INSERT INTO `t_dictionary` VALUES (158, 'nations             ', 'Burundi | 布隆迪', 0);
INSERT INTO `t_dictionary` VALUES (159, 'nations             ', 'Cambodia | 柬埔寨', 0);
INSERT INTO `t_dictionary` VALUES (160, 'nations             ', 'Cameroon | 喀麦隆', 0);
INSERT INTO `t_dictionary` VALUES (161, 'nations             ', 'Canada | 加拿大', 0);
INSERT INTO `t_dictionary` VALUES (162, 'nations             ', 'Cape Verde Islands | 佛得角群岛', 0);
INSERT INTO `t_dictionary` VALUES (163, 'nations             ', 'Chad | 乍得', 0);
INSERT INTO `t_dictionary` VALUES (164, 'nations             ', 'Chile | 智利', 0);
INSERT INTO `t_dictionary` VALUES (166, 'nations             ', 'Colombia | 哥伦比亚', 0);
INSERT INTO `t_dictionary` VALUES (167, 'nations             ', 'Congo | 刚果', 0);
INSERT INTO `t_dictionary` VALUES (168, 'nations             ', 'Costa Rica | 哥斯达黎加', 0);
INSERT INTO `t_dictionary` VALUES (169, 'nations             ', 'Croatia | 克罗地亚', 0);
INSERT INTO `t_dictionary` VALUES (170, 'nations             ', 'Cuba | 古巴', 0);
INSERT INTO `t_dictionary` VALUES (171, 'nations             ', 'Cyprus | 塞浦路斯', 0);
INSERT INTO `t_dictionary` VALUES (172, 'nations             ', 'Czech Republic | 捷克共和国', 0);
INSERT INTO `t_dictionary` VALUES (173, 'nations             ', 'Denmark | 丹麦', 0);
INSERT INTO `t_dictionary` VALUES (174, 'nations             ', 'Djibouti | 吉布提', 0);
INSERT INTO `t_dictionary` VALUES (175, 'nations             ', 'Dominica | 多米尼加', 0);
INSERT INTO `t_dictionary` VALUES (176, 'nations             ', 'Dominican Republic | 多米尼加国共和国', 0);
INSERT INTO `t_dictionary` VALUES (177, 'nations             ', 'Ecuador | 厄瓜多尔', 0);
INSERT INTO `t_dictionary` VALUES (178, 'nations             ', 'Egypt | 埃及', 0);
INSERT INTO `t_dictionary` VALUES (179, 'nations             ', 'El Salvador | 萨尔瓦多', 0);
INSERT INTO `t_dictionary` VALUES (180, 'nations             ', 'England | 英格兰', 0);
INSERT INTO `t_dictionary` VALUES (181, 'nations             ', 'Eritrea | 厄立特里亚', 0);
INSERT INTO `t_dictionary` VALUES (182, 'nations             ', 'Estonia | 爱沙尼亚', 0);
INSERT INTO `t_dictionary` VALUES (183, 'nations             ', 'Ethiopia | 埃塞俄比亚', 0);
INSERT INTO `t_dictionary` VALUES (184, 'nations             ', 'Fiji | 斐济', 0);
INSERT INTO `t_dictionary` VALUES (185, 'nations             ', 'Finland | 芬兰', 0);
INSERT INTO `t_dictionary` VALUES (186, 'nations             ', 'France | 法国', 0);
INSERT INTO `t_dictionary` VALUES (187, 'nations             ', 'Gabon | 加蓬', 0);
INSERT INTO `t_dictionary` VALUES (188, 'nations             ', 'Gambia | 冈比亚', 0);
INSERT INTO `t_dictionary` VALUES (189, 'nations             ', 'Georgia | 格鲁吉亚', 0);
INSERT INTO `t_dictionary` VALUES (190, 'nations             ', 'Germany | 德国', 0);
INSERT INTO `t_dictionary` VALUES (191, 'nations             ', 'Ghana | 加纳', 0);
INSERT INTO `t_dictionary` VALUES (192, 'nations             ', 'Greece | 希腊', 0);
INSERT INTO `t_dictionary` VALUES (193, 'nations             ', 'Grenada | 格林纳达', 0);
INSERT INTO `t_dictionary` VALUES (194, 'nations             ', 'Guatemala | 危地马拉', 0);
INSERT INTO `t_dictionary` VALUES (195, 'nations             ', 'Guinea | 几内亚', 0);
INSERT INTO `t_dictionary` VALUES (196, 'nations             ', 'Guyana | 圭亚那', 0);
INSERT INTO `t_dictionary` VALUES (197, 'nations             ', 'Haiti | 海地', 0);
INSERT INTO `t_dictionary` VALUES (198, 'nations             ', 'Netherlands | 荷兰', 0);
INSERT INTO `t_dictionary` VALUES (199, 'nations             ', 'Honduras | 洪都拉斯', 0);
INSERT INTO `t_dictionary` VALUES (200, 'nations             ', 'Hungary | 匈牙利', 0);
INSERT INTO `t_dictionary` VALUES (201, 'nations             ', 'Iceland | 冰岛', 0);
INSERT INTO `t_dictionary` VALUES (202, 'nations             ', 'India | 印度', 0);
INSERT INTO `t_dictionary` VALUES (203, 'nations             ', 'Indonesia | 印度尼西亚', 0);
INSERT INTO `t_dictionary` VALUES (204, 'nations             ', 'Iran | 伊朗', 0);
INSERT INTO `t_dictionary` VALUES (205, 'nations             ', 'Iraq | 伊拉克', 0);
INSERT INTO `t_dictionary` VALUES (206, 'nations             ', 'Ireland | 爱尔兰', 0);
INSERT INTO `t_dictionary` VALUES (207, 'nations             ', 'Italy | 意大利', 0);
INSERT INTO `t_dictionary` VALUES (208, 'nations             ', 'Jamaica | 牙买加', 0);
INSERT INTO `t_dictionary` VALUES (209, 'nations             ', 'Japan | 日本', 0);
INSERT INTO `t_dictionary` VALUES (210, 'nations             ', 'Jordan | 约旦', 0);
INSERT INTO `t_dictionary` VALUES (211, 'nations             ', 'Kazakhstan | 哈萨克斯坦', 0);
INSERT INTO `t_dictionary` VALUES (212, 'nations             ', 'Kenya | 肯尼亚', 0);
INSERT INTO `t_dictionary` VALUES (213, 'nations             ', 'Kuwait | 科威特', 0);
INSERT INTO `t_dictionary` VALUES (214, 'nations             ', 'Laos | 老挝', 0);
INSERT INTO `t_dictionary` VALUES (215, 'nations             ', 'Latvia | 拉脱维亚', 0);
INSERT INTO `t_dictionary` VALUES (216, 'nations             ', 'Lebanon | 黎巴嫩', 0);
INSERT INTO `t_dictionary` VALUES (217, 'nations             ', 'Liberia | 利比里亚', 0);
INSERT INTO `t_dictionary` VALUES (218, 'nations             ', 'Libya | 利比亚', 0);
INSERT INTO `t_dictionary` VALUES (219, 'nations             ', 'Liechtenstein | 列支敦士登', 0);
INSERT INTO `t_dictionary` VALUES (220, 'nations             ', 'Lithuania | 立陶宛', 0);
INSERT INTO `t_dictionary` VALUES (221, 'nations             ', 'Luxembourg | 卢森堡', 0);
INSERT INTO `t_dictionary` VALUES (222, 'nations             ', 'Macedonia马其顿', 0);
INSERT INTO `t_dictionary` VALUES (223, 'nations             ', 'Madagascar | 马达加斯加', 0);
INSERT INTO `t_dictionary` VALUES (224, 'nations             ', 'Malawi | 马拉维', 0);
INSERT INTO `t_dictionary` VALUES (225, 'nations             ', 'Malaysia | 马来西亚', 0);
INSERT INTO `t_dictionary` VALUES (226, 'nations             ', 'Maldives | 马尔代夫', 0);
INSERT INTO `t_dictionary` VALUES (227, 'nations             ', 'Mali | 马里', 0);
INSERT INTO `t_dictionary` VALUES (228, 'nations             ', 'Malta | 马耳他', 0);
INSERT INTO `t_dictionary` VALUES (229, 'nations             ', 'Mauritania | 毛里塔尼亚', 0);
INSERT INTO `t_dictionary` VALUES (230, 'nations             ', 'Mauritius | 毛里求斯', 0);
INSERT INTO `t_dictionary` VALUES (231, 'nations             ', 'Mexico | 墨西哥', 0);
INSERT INTO `t_dictionary` VALUES (232, 'nations             ', 'Moldova | 摩尔多瓦', 0);
INSERT INTO `t_dictionary` VALUES (233, 'nations             ', 'Monaco | 摩纳哥', 0);
INSERT INTO `t_dictionary` VALUES (234, 'nations             ', 'Mongolia | 蒙古', 0);
INSERT INTO `t_dictionary` VALUES (235, 'nations             ', 'Montenegro | 黑山', 0);
INSERT INTO `t_dictionary` VALUES (236, 'nations             ', 'Morocco | 摩洛哥', 0);
INSERT INTO `t_dictionary` VALUES (237, 'nations             ', 'Mozambique | 莫桑比克', 0);
INSERT INTO `t_dictionary` VALUES (239, 'nations             ', 'Namibia | 纳米比亚', 0);
INSERT INTO `t_dictionary` VALUES (240, 'nations             ', 'Nepal | 尼泊尔', 0);
INSERT INTO `t_dictionary` VALUES (242, 'nations             ', 'New Zealand | 新西兰', 0);
INSERT INTO `t_dictionary` VALUES (243, 'nations             ', 'Nicaragua | 尼加拉瓜', 0);
INSERT INTO `t_dictionary` VALUES (244, 'nations             ', 'Niger | 尼日尔', 0);
INSERT INTO `t_dictionary` VALUES (245, 'nations             ', 'Nigeria | 尼日利亚', 0);
INSERT INTO `t_dictionary` VALUES (246, 'nations             ', 'North Korea | 朝鲜', 0);
INSERT INTO `t_dictionary` VALUES (247, 'nations             ', 'Norway | 挪威', 0);
INSERT INTO `t_dictionary` VALUES (248, 'nations             ', 'Oman | 阿曼', 0);
INSERT INTO `t_dictionary` VALUES (249, 'nations             ', 'Pakistan | 巴基斯坦', 0);
INSERT INTO `t_dictionary` VALUES (250, 'nations             ', 'Panama | 巴拿马', 0);
INSERT INTO `t_dictionary` VALUES (251, 'nations             ', 'Papua New Guinea | 巴布亚新几内亚', 0);
INSERT INTO `t_dictionary` VALUES (252, 'nations             ', 'Paraguay | 巴拉圭', 0);
INSERT INTO `t_dictionary` VALUES (253, 'nations             ', 'Peru | 秘鲁', 0);
INSERT INTO `t_dictionary` VALUES (254, 'nations             ', 'Philippines | 菲律宾', 0);
INSERT INTO `t_dictionary` VALUES (255, 'nations             ', 'Poland | 波兰', 0);
INSERT INTO `t_dictionary` VALUES (256, 'nations             ', 'Portugal | 葡萄牙', 0);
INSERT INTO `t_dictionary` VALUES (257, 'nations             ', 'Qatar | 卡塔尔', 0);
INSERT INTO `t_dictionary` VALUES (258, 'nations             ', 'Romania | 罗马尼亚', 0);
INSERT INTO `t_dictionary` VALUES (259, 'nations             ', 'Russia | 俄罗斯', 0);
INSERT INTO `t_dictionary` VALUES (260, 'nations             ', 'Rwanda | 卢旺达', 0);
INSERT INTO `t_dictionary` VALUES (261, 'nations             ', 'Saudi Arabia | 沙特阿拉伯', 0);
INSERT INTO `t_dictionary` VALUES (262, 'nations             ', 'Scotland | 苏格兰', 0);
INSERT INTO `t_dictionary` VALUES (263, 'nations             ', 'Senegal | 塞内加尔', 0);
INSERT INTO `t_dictionary` VALUES (264, 'nations             ', 'Serbia | 塞尔维亚', 0);
INSERT INTO `t_dictionary` VALUES (265, 'nations             ', 'Seychelles | 塞舌尔', 0);
INSERT INTO `t_dictionary` VALUES (266, 'nations             ', 'Sierra Leone | 塞拉里昂', 0);
INSERT INTO `t_dictionary` VALUES (267, 'nations             ', 'Singapore | 新加坡', 0);
INSERT INTO `t_dictionary` VALUES (268, 'nations             ', 'Slovakia | 斯洛伐克', 0);
INSERT INTO `t_dictionary` VALUES (269, 'nations             ', 'Slovenia | 斯洛伐尼亚', 0);
INSERT INTO `t_dictionary` VALUES (270, 'nations             ', 'Solomon Islands | 所罗门群岛', 0);
INSERT INTO `t_dictionary` VALUES (271, 'nations             ', 'Somalia | 索马里', 0);
INSERT INTO `t_dictionary` VALUES (272, 'nations             ', 'South Africa | 南非', 0);
INSERT INTO `t_dictionary` VALUES (273, 'nations             ', 'South Korea | 韩国', 0);
INSERT INTO `t_dictionary` VALUES (274, 'nations             ', 'Spain | 西班牙', 0);
INSERT INTO `t_dictionary` VALUES (275, 'nations             ', 'Sri Lanka | 斯里兰卡', 0);
INSERT INTO `t_dictionary` VALUES (276, 'nations             ', 'Sudan | 苏丹', 0);
INSERT INTO `t_dictionary` VALUES (277, 'nations             ', 'Suriname | 苏里南', 0);
INSERT INTO `t_dictionary` VALUES (278, 'nations             ', 'Swaziland | 斯维士兰', 0);
INSERT INTO `t_dictionary` VALUES (279, 'nations             ', 'Sweden | 瑞典', 0);
INSERT INTO `t_dictionary` VALUES (280, 'nations             ', 'Switzerland | 瑞士', 0);
INSERT INTO `t_dictionary` VALUES (281, 'nations             ', 'Syria | 叙利亚', 0);
INSERT INTO `t_dictionary` VALUES (282, 'nations             ', 'Taiwan | 台湾', 0);
INSERT INTO `t_dictionary` VALUES (283, 'nations             ', 'Tajikistan | 塔吉克斯坦', 0);
INSERT INTO `t_dictionary` VALUES (284, 'nations             ', 'Tanzania | 坦桑尼亚', 0);
INSERT INTO `t_dictionary` VALUES (285, 'nations             ', 'Thailand | 泰国', 0);
INSERT INTO `t_dictionary` VALUES (286, 'nations             ', 'Togo | 多哥', 0);
INSERT INTO `t_dictionary` VALUES (287, 'nations             ', 'Trinidad and Tobago | 特里尼达和多巴哥', 0);
INSERT INTO `t_dictionary` VALUES (289, 'nations             ', 'Tunisia | 突尼斯', 0);
INSERT INTO `t_dictionary` VALUES (290, 'nations             ', 'Turkey | 土耳其', 0);
INSERT INTO `t_dictionary` VALUES (291, 'nations             ', 'Turkmenistan | 土库曼斯坦', 0);
INSERT INTO `t_dictionary` VALUES (292, 'nations             ', 'Tuvalu | 图瓦卢', 0);
INSERT INTO `t_dictionary` VALUES (293, 'nations             ', 'Uganda | 乌干达', 0);
INSERT INTO `t_dictionary` VALUES (294, 'nations             ', 'Ukraine | 乌克兰', 0);
INSERT INTO `t_dictionary` VALUES (295, 'nations             ', 'United Arab Emirates | 阿拉伯联合大公国', 0);
INSERT INTO `t_dictionary` VALUES (296, 'nations             ', 'United Kingdom  | 联合王国', 0);
INSERT INTO `t_dictionary` VALUES (297, 'nations             ', 'U.S.A. | 美国', 0);
INSERT INTO `t_dictionary` VALUES (298, 'nations             ', 'Uruguay | 乌拉圭', 0);
INSERT INTO `t_dictionary` VALUES (299, 'nations             ', 'Uzbekistan | 乌兹别克斯坦', 0);
INSERT INTO `t_dictionary` VALUES (300, 'nations             ', 'Vanuatu | 瓦努阿图', 0);
INSERT INTO `t_dictionary` VALUES (301, 'nations             ', 'Vatican City | 梵蒂冈', 0);
INSERT INTO `t_dictionary` VALUES (302, 'nations             ', 'Venezuela | 委内瑞拉', 0);
INSERT INTO `t_dictionary` VALUES (303, 'nations             ', 'Vietnam | 越南', 0);
INSERT INTO `t_dictionary` VALUES (304, 'nations             ', 'Wales | 威尔士', 0);
INSERT INTO `t_dictionary` VALUES (305, 'nations             ', 'Western Samoa | 西萨摩亚', 0);
INSERT INTO `t_dictionary` VALUES (306, 'nations             ', 'Yemen | 也门', 0);
INSERT INTO `t_dictionary` VALUES (307, 'nations             ', 'Yugoslavia | 南斯拉夫', 0);
INSERT INTO `t_dictionary` VALUES (308, 'nations             ', 'Zaire | 扎伊尔', 0);
INSERT INTO `t_dictionary` VALUES (309, 'nations             ', 'Zambia | 赞比亚', 0);
INSERT INTO `t_dictionary` VALUES (310, 'nations             ', 'Zimbabwe | 津巴布韦', 0);
INSERT INTO `t_dictionary` VALUES (311, 'nations             ', 'Anhui | 安徽', 0);
INSERT INTO `t_dictionary` VALUES (312, 'nations             ', 'Fujian | 福建', 0);
INSERT INTO `t_dictionary` VALUES (313, 'nations             ', 'Gansu | 甘肃', 0);
INSERT INTO `t_dictionary` VALUES (314, 'nations             ', 'Guangdong | 广东', 0);
INSERT INTO `t_dictionary` VALUES (315, 'nations             ', 'Guizhou | 贵州', 0);
INSERT INTO `t_dictionary` VALUES (316, 'nations             ', 'Hainan | 海南', 0);
INSERT INTO `t_dictionary` VALUES (317, 'nations             ', 'Hebei | 河北', 0);
INSERT INTO `t_dictionary` VALUES (318, 'nations             ', 'Heilongjiang | 黑龙江', 0);
INSERT INTO `t_dictionary` VALUES (319, 'nations             ', 'Henan | 河南', 0);
INSERT INTO `t_dictionary` VALUES (320, 'nations             ', 'Hubei | 湖北', 0);
INSERT INTO `t_dictionary` VALUES (321, 'nations             ', 'Hunan | 湖南', 0);
INSERT INTO `t_dictionary` VALUES (322, 'nations             ', 'Jiangsu | 江苏', 0);
INSERT INTO `t_dictionary` VALUES (323, 'nations             ', 'Jiangxi | 江西', 0);
INSERT INTO `t_dictionary` VALUES (324, 'nations             ', 'Jilin | 吉林', 0);
INSERT INTO `t_dictionary` VALUES (325, 'nations             ', 'Liaoning | 辽宁', 0);
INSERT INTO `t_dictionary` VALUES (326, 'nations             ', 'Qinghai | 青海', 0);
INSERT INTO `t_dictionary` VALUES (327, 'nations             ', 'Shaanxi | 陕西', 0);
INSERT INTO `t_dictionary` VALUES (328, 'nations             ', 'Shandong | 山东', 0);
INSERT INTO `t_dictionary` VALUES (329, 'nations             ', 'Shanxi | 山西', 0);
INSERT INTO `t_dictionary` VALUES (330, 'nations             ', 'Sichuan | 四川', 0);
INSERT INTO `t_dictionary` VALUES (331, 'nations             ', 'Yunnan | 云南', 0);
INSERT INTO `t_dictionary` VALUES (332, 'nations             ', 'Zhejiang | 浙江', 0);
INSERT INTO `t_dictionary` VALUES (333, 'nations             ', 'Guangxi | 广西', 0);
INSERT INTO `t_dictionary` VALUES (334, 'nations             ', 'Inner Mongolia | 内蒙古', 0);
INSERT INTO `t_dictionary` VALUES (335, 'nations             ', 'Ningxia | 宁夏', 0);
INSERT INTO `t_dictionary` VALUES (336, 'nations             ', 'Xinjiang | 新疆', 0);
INSERT INTO `t_dictionary` VALUES (337, 'nations             ', 'Tibet | 西藏', 0);
INSERT INTO `t_dictionary` VALUES (338, 'nations             ', 'Beijing | 北京', 0);
INSERT INTO `t_dictionary` VALUES (339, 'nations             ', 'Chongqing | 重庆', 0);
INSERT INTO `t_dictionary` VALUES (340, 'nations             ', 'Shanghai | 上海', 0);
INSERT INTO `t_dictionary` VALUES (341, 'nations             ', 'Tianjin | 天津', 0);
INSERT INTO `t_dictionary` VALUES (342, 'nations             ', 'Hong Kong | 香港', 0);
INSERT INTO `t_dictionary` VALUES (343, 'nations             ', 'Macau | 澳门', 0);
INSERT INTO `t_dictionary` VALUES (346, 'RestaurantTag       ', 'Wifi|无线上网', 0);
INSERT INTO `t_dictionary` VALUES (349, 'RestaurantTag       ', 'Good|View|有景观位', 0);
INSERT INTO `t_dictionary` VALUES (351, 'RestaurantTag       ', 'Big|Party|大型宴会', 0);
INSERT INTO `t_dictionary` VALUES (352, 'RestaurantTag       ', 'Birthday|Party|生日宴会', 0);
INSERT INTO `t_dictionary` VALUES (353, 'RestaurantTag       ', 'BYOB|自带酒水', 0);
INSERT INTO `t_dictionary` VALUES (354, 'RestaurantStyle     ', '朋友聚餐', 0);
INSERT INTO `t_dictionary` VALUES (355, 'RestaurantStyle     ', '家庭聚会', 0);
INSERT INTO `t_dictionary` VALUES (356, 'RestaurantStyle     ', '随便吃吃', 0);
INSERT INTO `t_dictionary` VALUES (357, 'RestaurantStyle     ', '休闲小憩', 0);
INSERT INTO `t_dictionary` VALUES (358, 'RestaurantStyle     ', '情侣约会', 0);
INSERT INTO `t_dictionary` VALUES (359, 'RestaurantStyle     ', '商务宴请', 0);
INSERT INTO `t_dictionary` VALUES (360, 'InviteStatus        ', '即将接洽', 0);
INSERT INTO `t_dictionary` VALUES (361, 'InviteStatus        ', '等待老板决定', 0);
INSERT INTO `t_dictionary` VALUES (362, 'InviteStatus        ', '合同签署中', 0);
INSERT INTO `t_dictionary` VALUES (363, 'InviteStatus        ', '合同已经签订', 0);
INSERT INTO `t_dictionary` VALUES (370, 'Cuisine', 'All-You-Can-Eat|自助餐', 0);
INSERT INTO `t_dictionary` VALUES (371, 'Cuisine', 'Bar|酒吧', 0);
INSERT INTO `t_dictionary` VALUES (372, 'Cuisine', 'Cafe|咖啡厅', 0);
INSERT INTO `t_dictionary` VALUES (373, 'Cuisine', 'Cantonese|粤菜', 0);
INSERT INTO `t_dictionary` VALUES (374, 'Cuisine', 'Dessert|甜品', 0);
INSERT INTO `t_dictionary` VALUES (375, 'Cuisine', 'Global Cuisine|环球美食', 0);
INSERT INTO `t_dictionary` VALUES (376, 'Cuisine', 'Middle Eastern|中东菜', 0);
INSERT INTO `t_dictionary` VALUES (377, 'Cuisine', 'Southeast Asian|东南亚菜', 0);
INSERT INTO `t_dictionary` VALUES (378, 'Cuisine', 'Zhejiang|浙江菜', 0);
INSERT INTO `t_dictionary` VALUES (379, 'Cuisine', 'Fast Casual|小吃快餐', 0);
INSERT INTO `t_dictionary` VALUES (380, 'nations', 'Israel', 0);
INSERT INTO `t_dictionary` VALUES (381, 'nations', 'East Timor', 0);
INSERT INTO `t_dictionary` VALUES (382, 'nations', 'Central African Republic', 0);
INSERT INTO `t_dictionary` VALUES (383, 'nations', 'S?o Tomé and Principe', 0);
INSERT INTO `t_dictionary` VALUES (384, 'nations', 'Ivory Coast', 0);
INSERT INTO `t_dictionary` VALUES (385, 'nations', 'Lesotho', 0);
INSERT INTO `t_dictionary` VALUES (386, 'nations', 'Equatorial Guinea', 0);
INSERT INTO `t_dictionary` VALUES (387, 'nations', 'Guinea Bissau', 0);
INSERT INTO `t_dictionary` VALUES (400, 'Cuisine', 'Sushi|寿司', 0);
INSERT INTO `t_dictionary` VALUES (401, 'Cuisine', 'British|英国菜', 0);
INSERT INTO `t_dictionary` VALUES (402, 'Cuisine', 'Dim Sum|早茶点心', 0);
INSERT INTO `t_dictionary` VALUES (403, 'Cuisine', 'Xibei / Xinjiang|西北菜/新疆菜', 0);
INSERT INTO `t_dictionary` VALUES (405, 'Cuisine', 'Guizhou|黔菜', 0);
INSERT INTO `t_dictionary` VALUES (406, 'Cuisine', 'Pizza|披萨', 0);
INSERT INTO `t_dictionary` VALUES (408, 'Cuisine', 'Seafood|海鲜', 0);
INSERT INTO `t_dictionary` VALUES (409, 'Cuisine', 'Anhui|徽菜', 0);
INSERT INTO `t_dictionary` VALUES (411, 'Cuisine', 'Sichuan|川菜', 0);
INSERT INTO `t_dictionary` VALUES (412, 'Cuisine', 'Korean|韩国料理', 0);
INSERT INTO `t_dictionary` VALUES (413, 'Cuisine', 'Juice & Beverages|果汁饮料', 0);
INSERT INTO `t_dictionary` VALUES (414, 'Cuisine', 'Bakery & Pastries|面包烘焙', 0);

-- ----------------------------
-- Table structure for t_diners
-- ----------------------------
DROP TABLE IF EXISTS `t_diners`;
CREATE TABLE `t_diners`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称',
  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `avatar_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '头像',
  `roles` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '角色',
  `is_valid` tinyint(1) NULL DEFAULT NULL,
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_diners
-- ----------------------------
INSERT INTO `t_diners` VALUES (1, 'abc', '昵称st', '13888888888', 'abc@imooc.com', 'e10adc3949ba59abbe56e057f20f883e', '/abc', 'ROLE_USER', 1, '2020-11-06 16:17:52', '2020-11-06 16:17:55');
INSERT INTO `t_diners` VALUES (2, 'test', 'test', '13666666666', NULL, 'e10adc3949ba59abbe56e057f20f883e', '/test', 'ROLE_USER', 1, '2020-11-12 12:01:13', '2020-11-12 12:01:13');
INSERT INTO `t_diners` VALUES (3, 'test2', 'test2', '13666666667', NULL, 'e10adc3949ba59abbe56e057f20f883e', '/test2', 'ROLE_USER', 1, '2020-11-12 17:47:12', '2020-11-12 17:47:12');
INSERT INTO `t_diners` VALUES (5, 'aaa', 'aaa', '12311112222', NULL, 'e10adc3949ba59abbe56e057f20f883e', '/aaa', 'ROLE_USER', 1, '2020-11-13 12:29:49', '2020-11-13 12:29:49');

-- ----------------------------
-- Table structure for t_feed
-- ----------------------------
DROP TABLE IF EXISTS `t_feed`;
CREATE TABLE `t_feed`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '内容',
  `fk_diner_id` int(11) NULL DEFAULT NULL,
  `praise_amount` int(11) NULL DEFAULT NULL COMMENT '点赞数量',
  `comment_amount` int(11) NULL DEFAULT NULL COMMENT '评论数量',
  `fk_restaurant_id` int(11) NULL DEFAULT NULL,
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  `is_valid` tinyint(1) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_feed
-- ----------------------------

-- ----------------------------
-- Table structure for t_follow
-- ----------------------------
DROP TABLE IF EXISTS `t_follow`;
CREATE TABLE `t_follow`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `diner_id` int(11) NULL DEFAULT NULL,
  `follow_diner_id` int(11) NULL DEFAULT NULL,
  `is_valid` tinyint(1) NULL DEFAULT NULL,
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `index_followeddiner_valid`(`follow_diner_id`, `is_valid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_follow
-- ----------------------------

-- ----------------------------
-- Table structure for t_restaurant
-- ----------------------------
DROP TABLE IF EXISTS `t_restaurant`;
CREATE TABLE `t_restaurant`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `Name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'the En Name of the restaurant',
  `CnName` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `X` double NULL DEFAULT NULL,
  `Y` double NULL DEFAULT NULL,
  `Location` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'En location of the restaurant',
  `CnLocation` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `Area` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'city.district.neighbourhood\r\nExample: Shanghai.Xuhui.Xujiahui',
  `CnArea` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `Traffic` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'the information/descripton of the restaurant',
  `Telephone` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Phone of the restaurant',
  `Email` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `Website` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `Cuisine` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `AveragePrice` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `AvgLunchPrice` decimal(19, 0) NULL DEFAULT NULL COMMENT 'Average price of lunch',
  `Introduction` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Indtroduction of the restaurant',
  `Status` int(11) NULL DEFAULT 0 COMMENT '1=Opened 0=Closed',
  `CreateDT` datetime(0) NULL DEFAULT NULL,
  `IsValid` smallint(1) NULL DEFAULT 1 COMMENT '1=Valid 0=Invalid',
  `Thumbnail` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'pics at the list, value would be:\r\nbasepath/original/picname',
  `OpenHours` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `LikeVotes` int(10) NULL DEFAULT 0 COMMENT 'the percentage of people like it',
  `DislikeVotes` int(10) NULL DEFAULT 0 COMMENT 'How many people votes',
  `Amenities` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '设备',
  `Tags` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'tags of the restaurant',
  `OpenDate` datetime(0) NULL DEFAULT NULL,
  `closeDate` datetime(0) NULL DEFAULT NULL,
  `CityId` int(11) NULL DEFAULT 21 COMMENT '城市id',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `index_isvalid`(`IsValid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_restaurant
-- ----------------------------
INSERT INTO `t_restaurant` VALUES (14, '1931 Pub', '名古', 31.2158508275268, 121.461839852847, '112 Maoming Nan Lu, near Nanchang Lu', '茂名南路112号, 近南昌路', 'Xuhui.Fmr French Concession', '', '', '021 6472 5264', '', '', 'Shanghainese, Chinese', '¥¥', 0, '', NULL, '2014-05-04 19:26:28', 1, '', '', 1, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (15, '2001 Hong Kong Teahouse', '2001港式茶餐', 31.21385, 121.46051, '55 Shaanxi Nan Lu, near Changle Lu', '陕西南路55号, 近长乐路', 'Xuhui.Fmr French Concession', '', '', '021 5467 0205', '', '', 'Dim Sum, Chinese', '¥', 0, '', NULL, '2014-05-04 19:26:28', 4, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (16, '2nd floor', '2nd floor', 31.2162, 121.447998, '2/F, 810 Changle Lu,near Changshu Lu', '长乐路810号2楼, 近常熟路', 'Xuhui.Fmr French Concession', '', '', '13761133471', '', 'http://www.2ndfloor.asia', 'Cafe', '¥', 0, '', NULL, '2014-05-04 19:26:28', 3, '', '', 1, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (17, '400 Celsius', '400 Celsius', 31.19436, 121.43797, '1 Hongqiao Lu, 1/F, Grand Gateway, near Caoxi Bei Lu, Metro Line 1 Xujiahui Station', '虹桥路1号港汇广场1楼, 近漕溪北路, 地铁1号线徐家汇站', 'Xuhui.Xujiahui', '', '', '021 6447 0770', '', '', 'Steakhouse', '¥¥¥¥', 0, '', NULL, '2014-05-04 19:26:28', 3, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (18, '5 on the Bund', '5 on the Bund', 31.234482, 121.490753, 'Five on the Bund,20 Guangdong Lu, near Zhongshan Dong Yi Lu', '广东路20号, 近中山东一路', 'Huangpu.The Bund', '', '', '', '', '', 'Global Cuisine', '¥¥¥¥', 0, '', NULL, '2014-05-04 19:26:28', 3, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (19, '5 Tables Bistro', '5桌餐厅', 31.2174481541175, 121.47318647082, '210 Danshui Lu, near Zizhong Lu', '淡水路210号, 近自忠路', 'Luwan.Xintiandi', '', '', '021 3304 1205', '', 'www.weibo.com/5tables', 'European', '¥¥¥¥', 0, '', NULL, '2014-05-04 19:26:28', 4, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (20, '57 Du Xiang', '57度湘', 31.2250117063411, 121.47824432639, '138 Huaihai Zhong Lu, Infinity Plaza, 4/F, Room 401, near Longmen Lu', '淮海路138号无限度广场4楼401室, 近龙门路', 'Xuhui.Huaihai Zhong Lu', '', '', '021 3315 0057', '', '', 'Hunan, Chinese', '¥', 0, '', NULL, '2014-05-04 19:26:28', 1, 'restaurant/20/restaurant/T/160_160/1399622680327.JPG', 'Daily 11am-9pm', 17, 5, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (21, '609 Pho', '609 Pho', 31.237629, 121.438797, '609 Anyuan Lu, near Jiaozhou Lu', '安源路609号, 近胶州路', 'Jing\'an', '', '', '18201753996', '', '', 'Vietnamese', '¥', 0, '', NULL, '2014-05-04 19:26:28', 4, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (22, '70s Restaurant', '70后饭吧', 31.2398228737211, 121.438096413353, '1217 Changde Lu, near Changshou Lu', '常德路1217号, 近长寿路', 'Putuo', '', '', '021 6040 2808', '', '', 'Chinese', '¥¥', 0, '', NULL, '2014-05-04 19:26:28', 1, 'restaurant/22/restaurant/160_160/14075670693130533.JPG', '', 7, 2, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);

-- ----------------------------
-- Table structure for t_seckill_vouchers
-- ----------------------------
DROP TABLE IF EXISTS `t_seckill_vouchers`;
CREATE TABLE `t_seckill_vouchers`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `fk_voucher_id` int(11) NULL DEFAULT NULL,
  `amount` int(11) NULL DEFAULT NULL,
  `start_time` datetime(0) NULL DEFAULT NULL,
  `end_time` datetime(0) NULL DEFAULT NULL,
  `is_valid` int(11) NULL DEFAULT NULL,
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_seckill_vouchers
-- ----------------------------

-- ----------------------------
-- Table structure for t_voucher
-- ----------------------------
DROP TABLE IF EXISTS `t_voucher`;
CREATE TABLE `t_voucher`  (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '代金券标题',
  `thumbnail` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '缩略图',
  `amount` int(11) NULL DEFAULT NULL COMMENT '抵扣金额',
  `price` decimal(10, 2) NULL DEFAULT NULL COMMENT '售价',
  `status` int(10) NULL DEFAULT NULL COMMENT '-1=过期 0=下架 1=上架',
  `expire_time` datetime(0) NULL DEFAULT NULL COMMENT '过期时间',
  `redeem_restaurant_id` int(10) NULL DEFAULT NULL COMMENT '验证餐厅',
  `stock` int(11) NULL DEFAULT 0 COMMENT '库存',
  `stock_left` int(11) NULL DEFAULT 0 COMMENT '剩余数量',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述信息',
  `clause` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '使用条款',
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  `is_valid` tinyint(1) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_voucher
-- ----------------------------

-- ----------------------------
-- Table structure for t_voucher_order
-- ----------------------------
DROP TABLE IF EXISTS `t_voucher_order`;
CREATE TABLE `t_voucher_order`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` int(11) NULL DEFAULT NULL,
  `fk_voucher_id` int(11) NULL DEFAULT NULL,
  `fk_diner_id` int(11) NULL DEFAULT NULL,
  `qrcode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '图片地址',
  `payment` tinyint(4) NULL DEFAULT NULL COMMENT '0=微信支付 1=支付宝支付',
  `status` tinyint(1) NULL DEFAULT NULL COMMENT '订单状态:-1=已取消 0=未支付 1=已支付 2=已消费 3=已过期',
  `fk_seckill_id` int(11) NULL DEFAULT NULL COMMENT '如果是抢购订单时,抢购订单的id',
  `order_type` int(11) NULL DEFAULT NULL COMMENT '订单类型:0=正常订单 1=抢购订单',
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  `is_valid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of t_voucher_order
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

创建commons模块

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">
    <parent>
        <artifactId>food-social-contact-parent</artifactId>
        <groupId>com.imooc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>commons</artifactId>

    <dependencies>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <!-- guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>
        <!-- swagger -->
        <dependency>
            <groupId>com.battcn</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
        </dependency>
        <!-- security -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
        </dependency>
    </dependencies>

</project>

全局常量类

全局异常类

ParameterException.java

package com.imooc.commons.exception;

import com.imooc.commons.constant.ApiConstant;
import lombok.Getter;
import lombok.Setter;

/**
 * 全局异常类
 */
@Getter
@Setter
public class ParameterException extends RuntimeException {

    private Integer errorCode;

    public ParameterException() {
        super(ApiConstant.ERROR_MESSAGE);
        this.errorCode = ApiConstant.ERROR_CODE;
    }

    public ParameterException(Integer errorCode) {
        this.errorCode = errorCode;
    }

    public ParameterException(String message) {
        super(message);
        this.errorCode = ApiConstant.ERROR_CODE;
    }

    public ParameterException(Integer errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

}

断言工具类

AssertUtil.java

package com.imooc.commons.utils;

import cn.hutool.core.util.StrUtil;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.exception.ParameterException;

/**
 * 断言工具类
 */
public class AssertUtil {

    /**
     * 必须登录
     *
     * @param accessToken
     */
    public static void mustLogin(String accessToken) {
        if (StrUtil.isBlank(accessToken)) {
            throw new ParameterException(ApiConstant.NO_LOGIN_CODE, ApiConstant.NO_LOGIN_MESSAGE);
        }
    }

    /**
     * 判断字符串非空
     *
     * @param str
     * @param message
     */
    public static void isNotEmpty(String str, String... message) {
        if (StrUtil.isBlank(str)) {
            execute(message);
        }
    }

    /**
     * 判断对象非空
     *
     * @param obj
     * @param message
     */
    public static void isNotNull(Object obj, String... message) {
        if (obj == null) {
            execute(message);
        }
    }

    /**
     * 判断结果是否为真
     *
     * @param isTrue
     * @param message
     */
    public static void isTrue(boolean isTrue, String... message) {
        if (isTrue) {
            execute(message);
        }
    }

    /**
     * 最终执行方法
     *
     * @param message
     */
    private static void execute(String... message) {
        String msg = ApiConstant.ERROR_MESSAGE;
        if (message != null && message.length > 0) {
            msg = message[0];
        }
        throw new ParameterException(msg);
    }

}

公共返回对象

公共返回对象工具类

ResultInfoUtil.java

package com.imooc.commons.utils;


import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;

/**
 * 公共返回对象工具类
 */
public class ResultInfoUtil {

    /**
     * 请求出错返回
     *
     * @param path 请求路径
     * @param <T>
     * @return
     */
    public static <T> ResultInfo<T> buildError(String path) {
        ResultInfo<T> resultInfo = build(ApiConstant.ERROR_CODE,
                ApiConstant.ERROR_MESSAGE, path, null);
        return resultInfo;
    }

    /**
     * 请求出错返回
     *
     * @param errorCode 错误代码
     * @param message   错误提示信息
     * @param path      请求路径
     * @param <T>
     * @return
     */
    public static <T> ResultInfo<T> buildError(int errorCode, String message, String path) {
        ResultInfo<T> resultInfo = build(errorCode, message, path, null);
        return resultInfo;
    }

    /**
     * 请求成功返回
     *
     * @param path 请求路径
     * @param <T>
     * @return
     */
    public static <T> ResultInfo<T> buildSuccess(String path) {
        ResultInfo<T> resultInfo = build(ApiConstant.SUCCESS_CODE,
                ApiConstant.SUCCESS_MESSAGE, path, null);
        return resultInfo;
    }

    /**
     * 请求成功返回
     *
     * @param path 请求路径
     * @param data 返回数据对象
     * @param <T>
     * @return
     */
    public static <T> ResultInfo<T> buildSuccess(String path, T data) {
        ResultInfo<T> resultInfo = build(ApiConstant.SUCCESS_CODE,
                ApiConstant.SUCCESS_MESSAGE, path, data);
        return resultInfo;
    }

    /**
     * 构建返回对象方法
     *
     * @param code
     * @param message
     * @param path
     * @param data
     * @param <T>
     * @return
     */
    public static <T> ResultInfo<T> build(Integer code, String message, String path, T data) {
        if (code == null) {
            code = ApiConstant.SUCCESS_CODE;
        }
        if (message == null) {
            message = ApiConstant.SUCCESS_MESSAGE;
        }
        ResultInfo resultInfo = new ResultInfo();
        resultInfo.setCode(code);
        resultInfo.setMessage(message);
        resultInfo.setPath(path);
        resultInfo.setData(data);
        return resultInfo;
    }

}

5. Redis保存授权中心令牌

5.1. 创建ms-oauth2-server模块

5.2. 配置文件

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">
    <parent>
        <artifactId>food-social-contact-parent</artifactId>
        <groupId>com.imooc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ms-oauth2-server</artifactId>

    <dependencies>
        <!-- eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- spring web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring data redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- spring cloud security -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <!-- spring cloud oauth2 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <!-- commons 公共项目 -->
        <dependency>
            <groupId>com.imooc</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 自定义的元数据依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

5.3. Security 配置类

SecurityConfiguration.java

package com.imooc.oauth2.server.config;

import cn.hutool.crypto.digest.DigestUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.Resource;

/**
 * Security 配置类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    //注入 Redis 连接工厂
    @Resource
    private RedisConnectionFactory redisConnectionFactory;
    
    //初始化 RedisTokenStore 用于将 token 存储至 Redis
    @Bean
    public RedisTokenStore redisTokenStore(){
        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        redisTokenStore.setPrefix("TOKEN:");//设置key的层级前缀,方便查询
        return redisTokenStore;
    }
    
    //初始化密码编码器,用 MD5 加密密码
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new PasswordEncoder() {
            /**
             * 加密
             * @param rawPassword 原始密码
             * @return
             */
            @Override
            public String encode(CharSequence rawPassword) {
                return DigestUtil.md5Hex(rawPassword.toString());
            }

            /**
             * 校验密码
             * @param rawPassword 原始密码
             * @param encodedPassword 加密密码
             * @return
             */
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return DigestUtil.md5Hex(rawPassword.toString()).equals(encodedPassword);
            }
        };
    }
    
    //初始化认证管理对象

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    //放行和认证规则

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                //放行的请求
                .antMatchers("/oauth/**","/actuator/**").permitAll()
                .and()
                .authorizeRequests()
                //其他请求必须认证才能访问
                .anyRequest().authenticated();
    }
}

application.yml

server:
  port: 8082 # 端口

spring:
  application:
    name: ms-oauth2-server # 应用名
  # 数据库
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/db_imooc?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
  # Redis
  redis:
    port: 6379
    host: 127.0.0.1
    timeout: 3000
    database: 2
    password: 111111
  # swagger
  swagger:
    base-package: com.imooc.oauth2
    title: 慕课美食社交食客API接口文档

# Oauth2
client:
  oauth2:
    client-id: appId # 客户端标识 ID
    secret: 123456 # 客户端安全码
    # 授权类型
    grant_types:
      - password
      - refresh_token
    # token 有效时间,单位秒
    token-validity-time: 3600
    refresh-token-validity-time: 3600
    # 客户端访问范围
    scopes:
      - api
      - all

# 配置 Eureka Server 注册中心
eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/

# Mybatis
mybatis:
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰映射

# 指标监控健康检查
management:
  endpoints:
    web:
      exposure:
        include: "*" # 暴露的端点

5.5 客户端配置类

ClientOAuth2DataConfiguration.java

package com.imooc.oauth2.server.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 客户端配置类
 */
@Component
@ConfigurationProperties(prefix = "client.oauth2")
@Data
public class ClientOAuth2DataConfiguration {
    // 客户端标识 ID
    private String clientId;

    // 客户端安全码
    private String secret;

    // 授权类型
    private String[] grantTypes;

    // token有效期
    private int tokenValidityTime;

    // refresh-token有效期
    private int refreshTokenValidityTime;

    // 客户端访问范围
    private String[] scopes;
}

5.6. 登录逻辑

5.6.1 公共实体类

BaseModel.java

package com.imooc.commons.model.base;

import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.Date;

/**
 * 实体对象公共属性
 */
@Getter
@Setter
public class BaseModel implements Serializable {
    private Integer id;
    private Date createDate;
    private Date updateDate;
    private int isValid;
}

5.6.2 食客实体类

Diners.java

package com.imooc.commons.model.pojo;


import com.imooc.commons.model.base.BaseModel;
import lombok.Getter;
import lombok.Setter;

/**
 * 食客实体类
 */
@Getter
@Setter
public class Diners extends BaseModel {

    // 主键
    private Integer id;
    // 用户名
    private String username;
    // 昵称
    private String nickname;
    // 密码
    private String password;
    // 手机号
    private String phone;
    // 邮箱
    private String email;
    // 头像
    private String avatarUrl;
    // 角色
    private String roles;

}

5.6.4 登录校验

UserService.java

package com.imooc.oauth2.server.service;

import com.imooc.commons.model.pojo.Diners;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.oauth2.server.mapper.DinersMapper;
import io.swagger.annotations.Authorization;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;

/**
 * 登录校验
 */
@Service
public class UserService implements UserDetailsService {

    @Resource
    private DinersMapper dinersMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AssertUtil.isNotEmpty(username,"请输入用户名");
        Diners diners = dinersMapper.selectByAccountInfo(username);
        if (diners == null){
            throw new UsernameNotFoundException("用户名或密码错误,请重新输入");
        }
        return new User(username, diners.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(diners.getRoles()));
    }
}

5.4 授权服务

AuthorizationServerConfiguration.java

package com.imooc.oauth2.server.config;

import com.imooc.oauth2.server.service.UserService;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.Resource;

/**
 * 授权服务
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    // 客户端配置类
    @Resource
    private ClientOAuth2DataConfiguration clientOAuth2DataConfiguration;

    //密码编码器
    @Resource
    private PasswordEncoder passwordEncoder;

    // 认证管理对象
    @Resource
    private AuthenticationManager authenticationManager;

    // RedisTokenSore
    @Resource
    private RedisTokenStore redisTokenStore;

    // 登录校验
    @Resource
    private UserService userService;

    /**
     * 配置令牌端点安全约束
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 允许访问 token 的公钥,默认 /oauth/token_key 是受保护的
        security.tokenKeyAccess("permitAll()")
                // 允许检查 token 的状态,默认 /oauth/check_token 是受保护的
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端配置 - 授权模型
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient(clientOAuth2DataConfiguration.getClientId()) // 客户端标识 ID
                .secret(passwordEncoder.encode(clientOAuth2DataConfiguration.getSecret())) // 客户端安全码
                .authorizedGrantTypes(clientOAuth2DataConfiguration.getGrantTypes()) // 授权类型
                .accessTokenValiditySeconds(clientOAuth2DataConfiguration.getTokenValidityTime()) // token 有效期
                .refreshTokenValiditySeconds(clientOAuth2DataConfiguration.getRefreshTokenValidityTime()) // 刷新 token 的有效期
                .scopes(clientOAuth2DataConfiguration.getScopes()); // 客户端访问范围
    }

    /**
     * 配置授权以及令牌的访问端点和令牌服务
     * @param endpoints the endpoints configurer
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 认证器
        endpoints.authenticationManager(authenticationManager)
                // 具体登录的方法
                .userDetailsService(userService)
                // token 存储的方式:Redis
                .tokenStore(redisTokenStore);
    }

}

5.7 启动类

Oauth2ServerApplication.java

package com.imooc.oauth2.server;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.imooc.oauth2.server.mapper")
@SpringBootApplication
public class Oauth2ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(Oauth2ServerApplication.class,args);
    }
}

5.8 测试 

启动RegistryApplication、Oauth2ServerApplication

用postman测试

6. 重构认证授权中心增强令牌返回结果

6.1 重构认证端点返回结果

OAuthController.java

package com.imooc.oauth2.server.controller;

import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Oauth2 控制器
 */
@RestController
@RequestMapping("oauth")
public class OAuthController {

    @Resource
    private TokenEndpoint tokenEndpoint;

    @Resource
    private HttpServletRequest request;

    @PostMapping("token")
    public ResultInfo postAccessToken(Principal principal, @RequestParam Map<String, String> parameters)
            throws HttpRequestMethodNotSupportedException {
        return custom(tokenEndpoint.postAccessToken(principal, parameters).getBody());
    }

    /**
     * 自定义 Token 返回对象
     * @param accessToken
     * @return
     */
    private ResultInfo custom(OAuth2AccessToken accessToken) {
        DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
        Map<String, Object> data = new LinkedHashMap(token.getAdditionalInformation());
        data.put("accessToken", token.getValue());
        data.put("expireIn", token.getExpiresIn());
        data.put("scopes", token.getScope());
        if (token.getRefreshToken() != null) {
            data.put("refreshToken", token.getRefreshToken().getValue());
        }
        return ResultInfoUtil.buildSuccess(request.getServletPath(), data);
    }

}

6.2 重构登录逻辑,增加令牌返回结果

添加登录认证对象

SignInIdentity.java

package com.imooc.commons.model.domain;

import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 登录认证对象
 */
@Getter
@Setter
public class SignInIdentity implements UserDetails {

    // 主键
    private Integer id;
    // 用户名
    private String username;
    // 昵称
    private String nickname;
    // 密码
    private String password;
    // 手机号
    private String phone;
    // 邮箱
    private String email;
    // 头像
    private String avatarUrl;
    // 角色
    private String roles;
    // 是否有效 0=无效 1=有效
    private int isValid;
    // 角色集合, 不能为空
    private List<GrantedAuthority> authorities;

    // 获取角色信息
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if (StrUtil.isNotBlank(this.roles)){
            // 获取数据库中的角色信息
            Lists.newArrayList();
            this.authorities = Stream.of(this.roles.split(",")).map(role -> {
                return new SimpleGrantedAuthority(role);
            }).collect(Collectors.toList());
        }else{
            // 如果角色为空则设置为 ROLE_USER
            this.authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
        }
        return this.authorities;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return this.isValid != 0 ;
    }
}

修改登录认证对象

UserService.java

令牌增强对象,增强返回的结果

AuthorizationServerConfiguration.java

7. 食客服务登录业务完善

7.1 添加依赖

pom.xml(ms-diners)

<?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">
    <parent>
        <artifactId>food-social-contact-parent</artifactId>
        <groupId>com.imooc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ms-diners</artifactId>

    <dependencies>
        <!-- eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- spring web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- spring data redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!-- commons 公共项目 -->
        <dependency>
            <groupId>com.imooc</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 自定义的元数据依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

7.2 配置文件

application.yml(ms-diners)

server:
  port: 8081 # 端口

spring:
  application:
    name: ms-diners  # 应用名
  # 数据库
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/db_imooc?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
  # Redis
  redis:
    port: 6379
    host: 127.0.0.1
    timeout: 3000
    password: 111111
  # swagger
  swagger:
    base-package: com.imooc.diners
    title: 慕课美食社交食客API接口文档

# Oauth2 客户端信息
oauth2:
  client:
    client-id: appId
    secret: 123456
    grant_type: password
    scope: api

# oauth2 服务地址
service:
  name:
    ms-oauth-server: http://ms-oauth2-server/
    
# 配置 Eureka Server 注册中心
eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/

# Mybatis
mybatis:
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰映射

7.3 客户端配置类 

OAuth2ClientConfiguration.java

package com.imooc.diners.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 客户端配置类
 */
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "oauth2.client")
public class OAuth2ClientConfiguration {
    private String clientId;
    private String secret;
    private String grant_type;
    private String scope;
}

7.4 Rest配置类

RestTemplateConfiguration.java

package com.imooc.diners.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.client.RestTemplate;

/**
 * Rest 配置类
 */
@Configuration
public class RestTemplateConfiguration {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

7.6 实体类

OAuthDinerInfo.java

视图对象实体类

LoginDinerInfo.java

7.5 登录逻辑

最终调用的是授权认证中心,

食客服务业务逻辑层

DinersService.java

package com.imooc.diners.service;

import cn.hutool.core.bean.BeanUtil;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.commons.utils.ResultInfoUtil;
import com.imooc.diners.config.OAuth2ClientConfiguration;
import com.imooc.diners.domain.OAuthDinerInfo;
import com.imooc.diners.vo.LoginDinerInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.LinkedHashMap;

/**
 * 食客服务业务逻辑层
 */
@Service
public class DinersService {
    @Resource
    private RestTemplate restTemplate;

    @Value("${service.name.ms-oauth-server}")
    private String oauthServiceName;

    @Resource
    private OAuth2ClientConfiguration oAuth2ClientConfiguration;

    /**
     * 登录
     * @param account  帐号:用户名或手机或邮箱
     * @param password 密码
     * @param path     请求路径
     * @return
     */
    public ResultInfo signIn(String account,String password,String path){
        // 1.参数校验
        AssertUtil.isNotEmpty(account,"请输入登录账号");
        AssertUtil.isNotEmpty(password,"请输入登录密码");
        // 2.构建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        // 3.构建请求体(请求参数)
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("username",account);
        body.add("password",password);
        body.setAll(BeanUtil.beanToMap(oAuth2ClientConfiguration));
        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
        // 4.设置 Authorization
        restTemplate.getInterceptors()
                .add(new BasicAuthenticationInterceptor(oAuth2ClientConfiguration.getClientId(),
                        oAuth2ClientConfiguration.getSecret()));
        // 5.发送请求
        ResponseEntity<ResultInfo> result = restTemplate
                .postForEntity(oauthServiceName + "oauth/token", entity, ResultInfo.class);
        // 6.处理返回结果
        AssertUtil.isTrue(result.getStatusCode() != HttpStatus.OK,"登录失败");
        ResultInfo resultInfo = result.getBody();
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE){
            // 登录失败
            resultInfo.setData(resultInfo.getMessage());
            return resultInfo;
        }
        // 这里的 Data 是一个 LinkedHashMap 转成了域对象 OAuthDinerInfo
        OAuthDinerInfo dinerInfo = BeanUtil.fillBeanWithMap(
                (LinkedHashMap) resultInfo.getData(),
                new OAuthDinerInfo(),false);
        // 根据业务需求返回视图对象
        LoginDinerInfo loginDinerInfo = new LoginDinerInfo();
        loginDinerInfo.setToken(dinerInfo.getAccessToken());
        loginDinerInfo.setAvatarUrl(dinerInfo.getAvatarUrl());
        loginDinerInfo.setNickname(dinerInfo.getNickname());
        return ResultInfoUtil.buildSuccess(path,loginDinerInfo);
    }

}

DinersController.java

package com.imooc.diners.controller;

import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.diners.service.DinersService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 食客服务控制层
 */
@RestController
@Api(tags = "食客相关接口")
public class DinersController {

    @Resource
    private DinersService dinersService;

    @Resource
    private HttpServletRequest request;

    @GetMapping("signin")
    public ResultInfo signIn(String account, String password){
        return dinersService.signIn(account,password, request.getServletPath());
    }
}

7.7 启动类

DinersApplication.java

7.8 测试

http://localhost:8081/signin?account=test&password=123456

8. 读取Redis登录用户信息和清空Redis用户信息

1.用户登录信息对象

common 公共项目添加用户登录信息对象

SignInDinerInfo.java

package com.imooc.commons.model.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@ApiModel(value = "SignInDinerInfo",description = "登录用户信息")
public class SignInDinerInfo {
    @ApiModelProperty("主键")
    private Integer id;
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("昵称")
    private String nickname;
    @ApiModelProperty("手机号")
    private String phone;
    @ApiModelProperty("邮箱")
    private String email;
    @ApiModelProperty("头像")
    private String avatarUrl;
    @ApiModelProperty("角色")
    private String roles;
}

2.用户中心

UserController.java

package com.imooc.oauth2.server.controller;

import cn.hutool.core.bean.BeanUtil;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.model.domain.SignInIdentity;
import com.imooc.commons.model.vo.SignInDinerInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 用户中心
 */
@RestController
public class UserController {

    @Resource
    private HttpServletRequest request;

    @GetMapping("user/me")
    public ResultInfo getCurrentUser(Authentication authentication){
        // 获取登录用户的信息,然后设置
        SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();
        // 转为前端可用的视图对象
        SignInDinerInfo dinerInfo = new SignInDinerInfo();
        BeanUtils.copyProperties(signInIdentity,dinerInfo);
        return ResultInfoUtil.buildSuccess(request.getServletPath(),dinerInfo);
    }

}

3.认证失败处理

MyAutheticationEntryPoint.java

package com.imooc.oauth2.server.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 认证失败处理
 */
@Component
public class MyAutheticationEntryPoint implements AuthenticationEntryPoint {

    @Resource
    private ObjectMapper objectMapper;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException{

        //返回JSON
        response.setContentType("application/json;charset=utf-8");
        // 状态码 401
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        // 写出
        PrintWriter out = response.getWriter();
        String errorMessage = authException.getMessage();
        if (StringUtils.isBlank(errorMessage)){
            errorMessage = "登录失败!";
        }
        ResultInfo result = ResultInfoUtil.buildError(ApiConstant.ERROR_CODE,
                errorMessage, request.getRequestURI());
        out.write(objectMapper.writeValueAsString(result));
        out.flush();
        out.close();
    }
}

4.资源服务

ResourceServerConfig.java

package com.imooc.oauth2.server.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

import javax.annotation.Resource;

/**
 * 资源服务
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Resource
    private MyAutheticationEntryPoint autheticationEntryPoint;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.authenticationEntryPoint(autheticationEntryPoint);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 配置放行的资源
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .requestMatchers()
                .antMatchers("/user/**");
    }
}

5.测试

重新启动Oauth2ServerApplication 

http://localhost:8082/user/me?access_token=73853d2d-9a99-4428-98e5-8354408b9255

这种方法也可以

UserController.java

package com.imooc.oauth2.server.controller;

import cn.hutool.core.bean.BeanUtil;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.model.domain.SignInIdentity;
import com.imooc.commons.model.vo.SignInDinerInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 用户中心
 */
@RestController
public class UserController {

    @Resource
    private HttpServletRequest request;

    @Resource
    private RedisTokenStore redisTokenStore;

    @GetMapping("user/me")
    public ResultInfo getCurrentUser(Authentication authentication){
        // 获取登录用户的信息,然后设置
        SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();
        // 转为前端可用的视图对象
        SignInDinerInfo dinerInfo = new SignInDinerInfo();
        BeanUtils.copyProperties(signInIdentity,dinerInfo);
        return ResultInfoUtil.buildSuccess(request.getServletPath(),dinerInfo);
    }
    // http://localhost:8082/user/me?access_token=fee64a86-489f-493b-b622-dfa7c23cb501


    /**
     * 安全退出
     * @param access_token
     * @param authorization
     * @return
     */
    @GetMapping("user/logout")
    public ResultInfo logout(String access_token,String authorization){
        //判断access_token是否为空,为空将authorization 赋值给access_token
        if (StringUtils.isBlank(access_token)){
            access_token = authorization;
        }
        //判断authorizaiton 是否为空
        if (StringUtils.isBlank(access_token)){
            return ResultInfoUtil.buildSuccess(request.getServletPath(),"退出成功");
        }
        //判断bearer token 是否为空
        if (access_token.toLowerCase().contains("bearer ".toLowerCase())){
            access_token = access_token.toLowerCase().replace("bearer ","");
        }
        //清除redis token 信息
        OAuth2AccessToken oAuth2AccessToken = redisTokenStore.readAccessToken(access_token);
        if (oAuth2AccessToken != null){
            redisTokenStore.removeAccessToken(oAuth2AccessToken);
            OAuth2RefreshToken refreshToken = oAuth2AccessToken.getRefreshToken();
            redisTokenStore.removeRefreshToken(refreshToken);
        }
        return ResultInfoUtil.buildSuccess(request.getServletPath(),"退出成功");
    }
}

再次重启服务 Oauth2ServerApplication 

http://localhost:8082/user/logout?access_token=73853d2d-9a99-4428-98e5-8354408b9255

或者这种方式

9. 网关登录校验 - 验证Redis中心令牌是否有效

1.配置文件

pom.xml(ms-gateway)

<?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">
    <parent>
        <artifactId>food-social-contact-parent</artifactId>
        <groupId>com.imooc</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ms-gateway</artifactId>

    <dependencies>
        <!-- spring cloud gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- commons 公共项目 -->
        <dependency>
            <groupId>com.imooc</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
            <!-- 和 webflux 冲突 -->
            <exclusions>
                <exclusion>
                    <groupId>com.battcn</groupId>
                    <artifactId>swagger-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 自定义的元数据依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

2.配置文件 

application.yml

3.白名单

网关白名单配置类

IgnoreUrlsConfig.java

package com.imooc.gateway.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 网关白名单配置
 */
@Data
@Component
@ConfigurationProperties(prefix = "secure.ignore")
public class IgnoreUrlsConfig {

    private List<String> urls;
}

4.Rest配置类

RestTemplateConfiguration.java

package com.imooc.gateway.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfiguration {
    
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

5.登录返回处理

HandleException.java

package com.imooc.gateway.component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

import java.nio.charset.Charset;

@Component
public class HandleException {

    @Resource
    private ObjectMapper objectMapper;

    public Mono<Void> writeError(ServerWebExchange exchange,String error){
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        ResultInfo resultInfo = ResultInfoUtil.buildError(ApiConstant.NO_LOGIN_CODE,
                ApiConstant.NO_LOGIN_MESSAGE,
                request.getURI().getPath());
        String resultInfoJson = null;
        DataBuffer buffer = null;
        try {
            resultInfoJson = objectMapper.writeValueAsString(resultInfo);
            buffer = response.bufferFactory()
                    .wrap(resultInfoJson.getBytes(Charset.forName("UTF-8")));
        } catch (JsonProcessingException ex) {
            ex.printStackTrace();
        }
        return response.writeWith(Mono.just(buffer));
    }
}

6.全局过滤器

AuthGlobalFilter.java

package com.imooc.gateway.filter;

import com.imooc.gateway.component.HandleException;
import com.imooc.gateway.config.IgnoreUrlsConfig;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * 网关全局过滤器
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    //网关白名单配置
    @Resource
    private IgnoreUrlsConfig ignoreUrlsConfig;

    @Resource
    private RestTemplate restTemplate;

    //集中返回处理
    @Resource
    private HandleException handleException;

    /**
     * 身份校验处理
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.判断当前的请求是否在白名单中
        AntPathMatcher pathMatcher = new AntPathMatcher();
        boolean flag = false;
        String path = exchange.getRequest().getURI().getPath();
        for (String url : ignoreUrlsConfig.getUrls()) {
            if (pathMatcher.match(url,path)){
                flag = true;
                break;
            }
        }
        //2.白名单放行
        if (flag){
            return chain.filter(exchange);
        }
        //3.获取 access_token
        String access_token = exchange.getRequest().getQueryParams().getFirst("access_token");
        //4.判断 access_token 是否为空
        if (StringUtils.isBlank(access_token)){
            return handleException.writeError(exchange,"请登录");
        }
        //5.校验 token 是否有效
        //ms-oauth2-server 注册中心服务名
        String checkTokenUrl = "http://ms-oauth2-server/oauth/check_token?token=".concat(access_token);
        try {
            //6.发送远程请求,验证token
            ResponseEntity<String> entity = restTemplate.getForEntity(checkTokenUrl, String.class);
            //token 无效的业务逻辑处理
            if (entity.getStatusCode() != HttpStatus.OK){
                return handleException.writeError(exchange,
                        "Token was not recognised,token: ".concat(access_token));
            }
            if (StringUtils.isBlank(entity.getBody())){
                return handleException.writeError(exchange,
                        "This token is invalid: ".concat(access_token));
            }
        }catch (Exception e){
            return handleException.writeError(exchange,
                    "Token was not recognised,token: ".concat(access_token));
        }
        //7.放行
        return chain.filter(exchange);
    }

    /**
     * 网关过滤器的排序,数字越小优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

7.测试

重新启动GatewayApplication

localhost:80相当于网关

http://localhost/diners/signin?account=test&password=123456

http://localhost/auth/user/me?access_token=653cd330-951a-4909-a6e9-4b3be46d2de0

 http://localhost/auth/oauth/check_token?token=653cd330-951a-4909-a6e9-4b3be46d2de0

http://localhost/auth/user/logout?access_token=653cd330-951a-4909-a6e9-4b3be46d2de0

10. 认证授权中心业务时序图总结

客户端请求资源(未登录)执行流程

请求资源->判断是否有令牌->没有令牌请登录

 http://localhost/diners/hello?name=redis

客户发起登录请求->白名单放行->校验账号密码->账号密码不合法

客户发起登录请求->白名单放行->校验账号密码->登录->登录校验->生成令牌并存储至Redis->返回令牌信息

客户端请求资源(已登录)执行流程 

请求资源->判断是否有令牌->校验令牌->令牌有效->放行,继续请求资源->业务处理->返回

http://localhost/auth/user/me?access_token=83932db5-34a5-4f83-b1c9-9d37e61eb56f 

http://localhost/diners/hello?name=redis&access_token=83932db5-34a5-4f83-b1c9-9d37e61eb56f

实现单点登录

http://localhost/auth/user/logout?access_token=5fb694e8-58b0-48e1-a20f-2340d485a47a

11. Redis保存手机短信验证码

11.1 Redis 配置类 

RedisTemplateConfiguration.java

package com.imooc.diners.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
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 RedisTemplateConfiguration {
    /**
     * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置key和value的序列化规则
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

11.2 公共枚举类

RedisKeyConstant.java

package com.imooc.commons.constant;

import lombok.Getter;

@Getter
public enum RedisKeyConstant {
    verify_code("verify_code:", "验证码");

    private String key;
    private String desc;

    RedisKeyConstant(String key,String desc){
        this.key = key;
        this.desc = desc;
    }
}

11.3 Service

SendVerifyCodeService.java

package com.imooc.diners.service;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.imooc.commons.constant.RedisKeyConstant;
import com.imooc.commons.utils.AssertUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 发送验证码业务逻辑层
 */
@Service
public class SendVerifyCodeService {

    @Resource
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 发送验证码
     * @param phone
     */
    public void send(String phone){

        //1.检查非空
        AssertUtil.isNotEmpty(phone,"手机号不能为空");
        //2.根据手机号查询是否已生成验证码,已生成直接返回
        if (!checkCodeIsExpired(phone)){
            return;
        }
        //3.生成6位验证码
        String code = RandomUtil.randomNumbers(6);
        //4.调用短信服务发送短信
        //5.发送成功,将code 保存至 Redis ,失效时间 60s
        String key = RedisKeyConstant.verify_code.getKey() + phone;
        redisTemplate.opsForValue().set(key,code,60, TimeUnit.SECONDS);
    }

    /**
     * 根据手机号查询是否已生成验证码
     * @param phone
     * @return
     */
    private boolean checkCodeIsExpired(String phone){
        String key = RedisKeyConstant.verify_code.getKey() + phone;
        String code = redisTemplate.opsForValue().get(key);
        return StrUtil.isBlank(code) ? true : false;
    }

}

11.4 Controller

SendVerifyCodeController.java

package com.imooc.diners.controller;

import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import com.imooc.diners.service.SendVerifyCodeService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 发送验证码控制层
 */
@RestController
public class SendVerifyCodeController {

    @Resource
    private SendVerifyCodeService sendVerifyCodeService;

    @Resource
    private HttpServletRequest request;

    /**
     * 发送验证码
     * @param phone
     * @return
     */
    @GetMapping("send")
    public ResultInfo send(String phone){
        sendVerifyCodeService.send(phone);
        return ResultInfoUtil.buildSuccess("发送成功",request.getServletPath());
    }
}

11.5 网关配置

application.yml

11.6  测试

重新启动服务

http://localhost/diners/send?phone=12311112222

12. 用户注册功能

12.1 是否已注册

Mapper

DinersMapper

package com.imooc.diners.mapper;

import com.imooc.commons.model.pojo.Diners;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**
 * 食客Mapper
 */
public interface DinersMapper {
    //根据手机号查询食客信息
    @Select("select id, username, phone, email, is_vaild " +
            "from t_diners where phone = #{phone}")
    Diners selectByPhone(@Param("phone") String phone);
}

DinersService.java

DinersController

 application.yml

重新启动DinersApplication、GatewayApplication服务

数据库有的

http://localhost/diners/checkPhone?phone=12311112222

数据库没有的 

http://localhost/diners/checkPhone?phone=12311112223

要写一个全局异常处理,给客户端返回一个友好的提示 

12.2 全局异常处理

handler

GlobalExceptionHandler.java

package com.imooc.diners.handler;

import com.imooc.commons.exception.ParameterException;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

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

@RestControllerAdvice  //将输出的内容写入到 ResponseBody 中
@Slf4j //开启日志
public class GlobalExceptionHandler {
    @Resource
    private HttpServletRequest request;

    @ExceptionHandler(ParameterException.class)
    public ResultInfo<Map<String,String>> handlerParameterException(ParameterException ex){
        String path = request.getRequestURI();
        ResultInfo<Map<String,String>> resultInfo =
                ResultInfoUtil.buildError(ex.getErrorCode(), ex.getMessage(), path);
        return resultInfo;
    }

    @ExceptionHandler(Exception.class)
    public ResultInfo<Map<String,String>> handlerException(Exception ex){
        log.info("未知异常:{}",ex);
        String path = request.getRequestURI();
        ResultInfo<Map<String,String>> resultInfo = ResultInfoUtil.buildError(path);
        return resultInfo;
    }
}

重新启动DinersApplication服务

http://localhost/diners/checkPhone?phone=12311112223

返回正常

12.3 完成注册

12.3.1 DTO

DinersDTO.java

package com.imooc.commons.model.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

@Getter
@Setter
@ApiModel(description = "注册用户信息")
public class DinersDTO implements Serializable {

    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("密码")
    private String password;
    @ApiModelProperty("手机号")
    private String phone;
    @ApiModelProperty("验证码")
    private String verifyCode;

}

12.3.2 Mapper

DinersMapper.java

package com.imooc.diners.mapper;

import com.imooc.commons.model.dto.DinersDTO;
import com.imooc.commons.model.pojo.Diners;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**
 * 食客Mapper
 */
public interface DinersMapper {
    //根据手机号查询食客信息
    @Select("select id, username, phone, email, is_valid " +
            "from t_diners where phone = #{phone}")
    Diners selectByPhone(@Param("phone") String phone);

    //根据用户名查询食客信息
    @Select("select id, username, phone, email, is_valid " +
            "from t_diners where username = #{username}")
    Diners selectByUsername(@Param("username") String username);

    //新增食客信息
    @Insert("insert into" +
            " t_diners (username, password, phone, roles, is_valid, create_date,update_date) " +
            "values (#{username}, #{password}, #{phone}, \"ROLE_USER\", 1, now(),now())")
    int save(DinersDTO dinersDTO);

}

12.3.3 Service

SendVerifyCodeService.java

DinersService.java

 @Resource
    private SendVerifyCodeService sendVerifyCodeService;
    /**
     * 用户注册
     * @param dinersDTO
     * @param path
     * @return
     */
    public ResultInfo register(DinersDTO dinersDTO,String path){
        //1.参数非空校验
        String username = dinersDTO.getUsername();
        AssertUtil.isNotEmpty(username,"请输入用户名");
        String password = dinersDTO.getPassword();
        AssertUtil.isNotEmpty(password,"请输入密码");
        String phone = dinersDTO.getPhone();
        AssertUtil.isNotEmpty(phone,"请输入手机号");
        String verifyCode = dinersDTO.getVerifyCode();
        AssertUtil.isNotEmpty(verifyCode,"请输入验证码");
        //2.验证码一致性校验(验证码是否已过期)
        String code = sendVerifyCodeService.getCodeByPhone(phone);
        //    验证码是否过期
        AssertUtil.isNotEmpty(code,"验证码已过期,请重新发送");
        //验证码一致性校验
        AssertUtil.isTrue(!dinersDTO.getVerifyCode().equals(code),"验证码不一致,请重新输入");
        //3.验证用户名是否已注册
        Diners diners = dinersMapper.selectByUsername(username.trim());
        AssertUtil.isTrue(diners !=null,"用户名已存在,请重新输入");
        //4.注册
        //  密码加密
        dinersDTO.setPassword(DigestUtil.md5Hex(password.trim()));
        dinersMapper.save(dinersDTO);
        //  自动登录
        return signIn(username.trim(),password.trim(),path);
    }

12.3.4 Controller

DinersController.java

12.3.5 网关配置

application.yml(ms-gateway)

12.3.6 测试

1.检测是否注册

http://localhost/diners/checkPhone?phone=12311113333

2.发送验证码

3.拿验证码

4.登录(60s会过期,再发一遍就可以了)

下面是一些其它错误展示:

1.请输入用户名(或者密码、手机号、验证码)

2.验证码已过期,请重新发送

3.验证码不一致,请重新输入

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1522715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云-零基础入门推荐系统 【特征工程】

文章目录 赛题介绍评价方式理解赛题理解制作特征和标签&#xff0c; 转成监督学习问题导包df节省内存函数训练和验证集的划分获取历史点击和最后一次点击读取训练、验证及测试集读取召回列表读取各种Embedding读取文章信息读取数据对训练数据做负采样将召回数据转换成字典制作与…

spring boot集成redis实现共享存储session

spring boot集成redis实现共享存储session redis实现共享存储session 首先下载redis,我下载的版本是5.0.14,目前官网貌似找不到5.x版本&#xff0c;可以自行去网上寻找。我这里的springboot版本是2.6.4引入redis依赖 <!-- https://mvnrepository.com/artifact/org.spring…

麒麟系统Redis7.2哨兵集群部署

redis哨兵集群部署 1、原理 Redis 哨兵模式是指在 Redis 集群中,有一组专门的进程(即哨兵进程)负责监控主节点和从节点的状态,并在发现故障时自动进行故障转移,以保证 Redis 集群的高可用性。 Redis 提供了哨兵的命令,哨兵命令是一个独立的进程,哨兵进程会周期性地向主…

YOLOv9改进策略:注意力机制 | SimAM(无参Attention),效果秒杀CBAM、SE

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;SimAM是一种轻量级的自注意力机制&#xff0c;其网络结构与Transformer类似&#xff0c;但是在计算注意力权重时使用的是线性层而不是点积 yolov9-c-CoordAtt summary: 972 layers, 51024476 parameters, 510…

“一键秒变!PNG到JPG,图片批量转换新体验“

在这个数字时代&#xff0c;图片已成为我们生活与工作中不可或缺的一部分。无论是社交媒体上的个人分享&#xff0c;还是商务场合中的项目展示&#xff0c;一张好的图片往往能起到事半功倍的效果。然而&#xff0c;面对堆积如山的PNG图片&#xff0c;你是否曾感到力不从心&…

Linux-centos如何搭建yum源仓库

1.本地搭建&#xff08;无需连接外网&#xff09; 1.1检查网络配置&#xff0c;及网络连接 打开虚拟机&#xff0c;点击【编辑——虚拟网络编辑器】 点击【仅主机模式】查看子网段是否和局内IP匹配 进入局内&#xff0c;查看网络IP是否在你上述设置的网段内&#xff0c;如果不…

【PyTorch】成功解决ModuleNotFoundError: No module named ‘torch’

【PyTorch】成功解决ModuleNotFoundError: No module named ‘torch’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希…

SQLiteC/C++接口详细介绍之sqlite3类(十四)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十三&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十五&#xff09; 43.sqlite3_preupdate_hook sqlite3_preup…

【工具】一键生成动态歌词字幕

那眼神如此熟悉 让人着迷无力抗拒 一次又一次相遇 在眼前却遥不可及 命运总爱淘气 将一切都藏匿 曾有你的回忆 无痕迹 若不是心心相吸 又怎么会一步一步靠近 &#x1f3b5; 董真《思如雪》 下载LRC歌词 https://www.musicenc.com/article/50287.htmlhttp…

Android 系统的启动过程

Android 系统的启动流程&#xff1a; RomBoot&#xff08;只读存储器引导程序&#xff09;&#xff1a;这是设备上电时运行的初始软件。RomBoot执行基本的硬件初始化&#xff0c;确保硬件处于可以运行后续启动阶段的状态。这一阶段非常重要&#xff0c;因为它为整个启动过程奠定…

VMwareWorkstation16与Ubuntu 22.04.6 LTS下载与安装

一、准备工作 VMware Workstation Pro 16官网下载&#xff1a; https://customerconnect.vmware.com/cn/downloads/info/slug/desktop_end_user_computing/vmware_workstation_pro/16_0。下载需要账号登录。 二、安装 双击exe文件稍等一会会弹出安装程序&#xff0c;如图 这…

【Miniconda】基于conda避免运行多个PyTorch项目时发生版本冲突

【Miniconda】基于conda避免运行多个PyTorch项目时发生版本冲突 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到…

高效备考2025年AMC8竞赛:吃透2000-2024年600道真题(免费送题)

我们继续来随机看五道AMC8的真题和解析&#xff0c;根据实践经验&#xff0c;对于想了解或者加AMC8美国数学竞赛的考生来说&#xff0c;吃透AMC8历年真题是备考更加科学、有效的方法之一。 即使不参加AMC8竞赛&#xff0c;吃透了历年真题600道和背后的知识体系&#xff0c;那么…

爬虫学习 Scrapy中间件代理UA随机selenium使用

目录 中间件UA、代理处理---process_requestUA随机 代理处理seleniumscrapy 中间件 控制台操作 (百度只起个名 scrapy startproject mid scrapy genspider baidu baidu.com setting.py内 ROBOTSTXT_OBEY FalseLOG_LEVEL "WARNING"运行 scrapy crawl baidu middle…

数据可视化-ECharts Html项目实战(1)

在之前的文章中&#xff0c;我们学习了如何安装Visual Studio Code并下载插件&#xff0c;想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 安装 Visual Studio…

【IC设计】Verilog线性序列机点灯案例(三)(小梅哥课程)

声明&#xff1a;案例和代码来自小梅哥课程&#xff0c;本人仅对知识点做做笔记&#xff0c;如有学习需要请支持官方正版。 文章目录 该系列目录设计目标设计思路RTL及Testbench代码RTL代码Testbench代码 仿真结果上板视频 该系列目录 Verilog线性序列机点灯案例(一)&#xff…

LangChain: 调研报告

概述 LangChain是一个用于开发由语言模型驱动的应用程序的框架。它允许创建能够连接到上下文源&#xff08;如提示指令、少量示例、内容基础等&#xff09;的应用程序&#xff0c;并且能够进行推理&#xff08;基于提供的上下文如何回答问题、采取何种行动等&#xff09;。提供…

Ps:历史记录画笔工具

历史记录画笔工具 History Brush Tool可通过选择历史记录面板中的一个特定状态作为绘画源&#xff0c;然后使用画笔在图像的特定区域绘制&#xff0c;将这些区域恢复到选定的历史状态。 快捷键&#xff1a;Y 如果对图像进行了广泛的编辑&#xff0c;但希望将图像的一小部分恢复…

Explain 关键字

优质博文&#xff1a;IT-BLOG-CN explain关键字可以模拟优化器执行 SQL 查询语句&#xff0c;从而知道 MySQL 是如何处理 SQL 语句的。分析查询语句或表结构的性能瓶颈。执行语句&#xff1a;explain SQL语句。表头信息如下&#xff1a; 一、ID 参数 select 查询的序列号&…

算法---滑动窗口练习-8(最小覆盖子串)

最小覆盖子串 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;最小覆盖子串 2. 讲解算法原理 滑动窗口哈希表 算法的主要思想是使用滑动窗口的方法。它使用两个哈希表 hash1 和 hash2 来记录字符串 s 和 t 中各个字符的出现次数。其中 hash2 用于记…