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

高级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 TransferSource Server         : localhostSource Server Type    : MySQLSource Server Version : 80018Source Host           : localhost:3306Source Schema         : db_imoocTarget Server Type    : MySQLTarget Server Version : 80018File Encoding         : 65001Date: 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 连接工厂@Resourceprivate RedisConnectionFactory redisConnectionFactory;//初始化 RedisTokenStore 用于将 token 存储至 Redis@Beanpublic RedisTokenStore redisTokenStore(){RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);redisTokenStore.setPrefix("TOKEN:");//设置key的层级前缀,方便查询return redisTokenStore;}//初始化密码编码器,用 MD5 加密密码@Beanpublic PasswordEncoder passwordEncoder(){return new PasswordEncoder() {/*** 加密* @param rawPassword 原始密码* @return*/@Overridepublic String encode(CharSequence rawPassword) {return DigestUtil.md5Hex(rawPassword.toString());}/*** 校验密码* @param rawPassword 原始密码* @param encodedPassword 加密密码* @return*/@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {return DigestUtil.md5Hex(rawPassword.toString()).equals(encodedPassword);}};}//初始化认证管理对象@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}//放行和认证规则@Overrideprotected 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.Driverusername: rootpassword: rooturl: jdbc:mysql://127.0.0.1:3306/db_imooc?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false# Redisredis:port: 6379host: 127.0.0.1timeout: 3000database: 2password: 111111# swaggerswagger:base-package: com.imooc.oauth2title: 慕课美食社交食客API接口文档# Oauth2
client:oauth2:client-id: appId # 客户端标识 IDsecret: 123456 # 客户端安全码# 授权类型grant_types:- password- refresh_token# token 有效时间,单位秒token-validity-time: 3600refresh-token-validity-time: 3600# 客户端访问范围scopes:- api- all# 配置 Eureka Server 注册中心
eureka:instance:prefer-ip-address: trueinstance-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 {// 客户端标识 IDprivate 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 {@Resourceprivate DinersMapper dinersMapper;@Overridepublic 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 {// 客户端配置类@Resourceprivate ClientOAuth2DataConfiguration clientOAuth2DataConfiguration;//密码编码器@Resourceprivate PasswordEncoder passwordEncoder;// 认证管理对象@Resourceprivate AuthenticationManager authenticationManager;// RedisTokenSore@Resourceprivate RedisTokenStore redisTokenStore;// 登录校验@Resourceprivate UserService userService;/*** 配置令牌端点安全约束* @param security* @throws Exception*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// 允许访问 token 的公钥,默认 /oauth/token_key 是受保护的security.tokenKeyAccess("permitAll()")// 允许检查 token 的状态,默认 /oauth/check_token 是受保护的.checkTokenAccess("permitAll()");}/*** 客户端配置 - 授权模型* @param clients* @throws Exception*/@Overridepublic 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*/@Overridepublic 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 {@Resourceprivate TokenEndpoint tokenEndpoint;@Resourceprivate 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;// 获取角色信息@Overridepublic 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_USERthis.authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");}return this.authorities;}@Overridepublic String getPassword() {return this.password;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic 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.Driverusername: rootpassword: rooturl: jdbc:mysql://127.0.0.1:3306/db_imooc?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false# Redisredis:port: 6379host: 127.0.0.1timeout: 3000password: 111111# swaggerswagger:base-package: com.imooc.dinerstitle: 慕课美食社交食客API接口文档# Oauth2 客户端信息
oauth2:client:client-id: appIdsecret: 123456grant_type: passwordscope: api# oauth2 服务地址
service:name:ms-oauth-server: http://ms-oauth2-server/# 配置 Eureka Server 注册中心
eureka:instance:prefer-ip-address: trueinstance-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@Beanpublic 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 {@Resourceprivate RestTemplate restTemplate;@Value("${service.name.ms-oauth-server}")private String oauthServiceName;@Resourceprivate 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.设置 AuthorizationrestTemplate.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 转成了域对象 OAuthDinerInfoOAuthDinerInfo 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 {@Resourceprivate DinersService dinersService;@Resourceprivate 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 {@Resourceprivate 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 {@Resourceprivate ObjectMapper objectMapper;@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException{//返回JSONresponse.setContentType("application/json;charset=utf-8");// 状态码 401response.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 {@Resourceprivate MyAutheticationEntryPoint autheticationEntryPoint;@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.authenticationEntryPoint(autheticationEntryPoint);}@Overridepublic 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 {@Resourceprivate HttpServletRequest request;@Resourceprivate 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_tokenif (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@Beanpublic 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 {@Resourceprivate 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 {//网关白名单配置@Resourceprivate IgnoreUrlsConfig ignoreUrlsConfig;@Resourceprivate RestTemplate restTemplate;//集中返回处理@Resourceprivate HandleException handleException;/*** 身份校验处理* @param exchange* @param chain* @return*/@Overridepublic 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_tokenString 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.发送远程请求,验证tokenResponseEntity<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*/@Overridepublic 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*/@Beanpublic 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 {@Resourceprivate 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 ,失效时间 60sString 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 {@Resourceprivate SendVerifyCodeService sendVerifyCodeService;@Resourceprivate 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 {@Resourceprivate 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

 @Resourceprivate 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.rhkb.cn/news/277441.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

由浅到深认识C语言(11):结构体

该文章Github地址&#xff1a;https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.csdn…

HCIP—OSPF课后练习一

本实验模拟了一个企业网络场景&#xff0c;R1、R2、R3为公司总部网络的路由器&#xff0c;R4、R5分别为企业分支机构1和分支机构2的路由器&#xff0c;并且都采用双上行方式与企业总部相连。整个网络都运行OSPF协议&#xff0c;R1、R2、R3之间的链路位于区域0&#xff0c;R4与R…

Java代码基础算法练习-判断字符串是否为回文-2024.03.16

任务描述&#xff1a; 回文串是指一个正读和反读都一样的字符串&#xff0c;比如“level”或者“noon”等。要求输入 一个字符串&#xff0c;判断此字符串是否为回文。&#xff08;注&#xff1a;设字符串长度小于20&#xff09; 任务要求&#xff1a; package suanfa;import…

Redis远程连接本机——Docker

1. Docker拉取redis镜像并创建容器 1.1 拉取redis镜像 如果要指定redis版本&#xff0c;需要使用redis:&#xff08;版本&#xff09;&#xff0c;不写默认最新版本 docker pull redis1.2 创建容器并挂载配置文件 创建一个redis目录&#xff0c;并在其创建一个conf目录和一个d…

代码随想录训练营Day24:● 理论基础 ● 77. 组合

理论基础 回溯算法解决的问题 回溯法&#xff0c;一般可以解决如下几种问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合 切割问题&#xff1a;一个字符串按一定规则有几种切割方式 子集问题&#xff1a;一个N个数的集合里有多少符合条件的子集 排列…

软考79-上午题-【面向对象技术3-设计模式】-结构型设计模式02

一、组合模式 1-1、意图 将对象组合成树型结构&#xff0c;以表示"部分-整体"的层次结构。Composite使得用户对单个对象和组 合对象的使用具有一致性。 示例&#xff1a;对象&#xff1a;文件、文件夹 1-2、结构 Component 为组合中的对象声明接口&#xff1b;在适…

数学建模--MATLAB基本使用

1.线性方程组 这个是一个线性方程组&#xff08;属于线性代数的范畴&#xff09;&#xff0c;Axb类型的方程&#xff0c;如果使用MATLAB进行求解&#xff0c;就需要分别表示A矩阵&#xff08;线性方程组未知数前面的系数&#xff09;&#xff0c;b矩阵&#xff08;表示等式右边…

Stable Diffusion科普文章【附升级gpt4.0秘笈】

随着人工智能技术的飞速发展&#xff0c;我们越来越多地看到计算机生成的艺术作品出现在我们的生活中。其中&#xff0c;Stable Diffusion作为一种创新的图像生成技术&#xff0c;正在引领一场艺术创作的革命。本文将为您科普Stable Diffusion的相关知识&#xff0c;带您走进这…

3.Redis命令

Redis命令 Redis 根据命令所操作对象的不同&#xff0c; 可以分为三大类&#xff1a; 对 Redis 进行基础性操作的命令&#xff0c;对 Key 的操作命令&#xff0c;对 Value 的操作命令。 1.1 Redis 首先通过 redis-cli 命令进入到 Redis 命令行客户端&#xff0c;然后再运行下…

MIT线性代数-方程组的几何解释

文章目录 1. 二维空间1.1 行方向1.2 列方向 2. 三维空间2.1 行方向2.2 列方向 假设有一个方程组 A X B AXB AXB表示如下 2 x − y 0 (1) 2x-y0\tag{1} 2x−y0(1) − x 2 y 3 (2) -x2y3\tag{2} −x2y3(2) 矩阵表示如下&#xff1a; [ 2 − 1 − 1 2 ] [ x y ] [ 0 3 ] (3)…

redis瘦身版

高可用&#xff1a; 主从 哨兵&#xff1a;sentinel&#xff1a; 集群监控 消息通知 故障转移 配置中心 redis cluster &#xff1a;livu livechat中使用了 人家有槽slot 16384个呢 请求发送任意节点 该节点会将请求发送到正确节点上-相亲相爱 1.哈希的方式&#xff0c;将数据…

如何在CasaOS系统玩客云中安装内网穿透工具实现远程访问内网主机下载资源

文章目录 1. CasaOS系统介绍2. 内网穿透安装3. 创建远程连接公网地址4. 创建固定公网地址远程访问 2月底&#xff0c;玩客云APP正式停止运营&#xff0c;不再提供上传、云添加功能。3月初&#xff0c;有用户进行了测试&#xff0c;局域网内的各种服务还能继续使用&#xff0c;但…

RK3568平台开发系列讲解(基础篇)内核是如何发送事件到用户空间

🚀返回专栏总目录 文章目录 一、相关接口函数二、udevadm 命令三、实验沉淀、分享、成长,让自己和他人都能有所收获!😄 一、相关接口函数 kobject_uevent 是 Linux 内核中的一个函数, 用于生成和发送 uevent 事件。 它是 udev 和其他设备管理工具与内核通信的一种方式。…

Day40:安全开发-JavaEE应用SpringBoot框架JWT身份鉴权打包部署JARWAR

目录 SpringBoot-身份鉴权-JWT技术 SpringBoot-打包部署-JAR&WAR 思维导图 Java知识点 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;序列化数据&#xff0c;身份验证&#xff0c;框架开发&#xff0c;第三方组件使用等. 框架库&#xff1a;MyBatis&…

Navicat 面试题及答案整理,最新面试题

Navicat 在数据库管理中的主要用途有哪些&#xff1f; Navicat 是一款数据库管理工具&#xff0c;其主要用途包括&#xff1a; 1、多数据库支持&#xff1a; Navicat 支持多种数据库连接&#xff0c;包括 MySQL、Oracle、PostgreSQL、SQLite、SQL Server 等&#xff0c;方便用…

湖南麒麟SSH服务漏洞

针对湖南麒麟操作系统进行漏洞检测时&#xff0c;会报SSH漏洞风险提醒&#xff0c;具体如下&#xff1a; 针对这些漏洞&#xff0c;可以关闭SSH服务&#xff08;前提是应用已经部署完毕不再需要通过SSH远程访问传输文件的情况下&#xff0c;此时可以通过VNC远程登录方法&#x…

Github Copilot 工具,无需账号,一键激活

① 无需账号&#xff0c;100%认证成功&#xff01;0风险&#xff0c;可联网可更新&#xff0c;&#xff0c;支持copilot版本升级&#xff0c;支持chat ② 支持windows、mac、linux系统等设备 ③一号通用&#xff0c;支持所有IDE(AppCode,CLion,DataGrip,GoLand,IntelliJ IDEA …

51单片机-AT24C02(I2C总线)

目录 一&#xff0c;介绍及元件工作原理 7.时序结构&#xff08;重要&#xff09; 8.i2C总线数据帧&#xff08;重要&#xff09; 二&#xff0c;应用 一&#xff0c;介绍及元件工作原理 1.元件介绍 2.存储器 3.地址总线和数据总线 地址总线只能一次选中一行 4.引脚及应用…

java上传和下载文件使用教程

文章目录 前言一、引入库二、上传文件1.前台2.后台3.测试 三、下载文件(chrome)1.前台2.后台3.测试 总结 前言 本篇文章介绍java中文件的上传和下载&#xff0c;亲测可用&#xff0c;所用案例为springboot项目。 一、引入库 <!-- SpringBoot Web容器 --> <dependenc…

【原创】java+swing+mysql二手车交易管理系统

前言&#xff1a; 本文主要介绍了二手车交易管理设计与实现。首先&#xff0c;通过市场需求&#xff0c;我们确定了二手车的功能&#xff0c;通常的二手车交易系统都是B/S架构&#xff0c;然而我们今天要用javaswing去开发一个C/S架构的二手车交易管理系统&#xff0c;主要功能…