高级Redis应用进阶 一站式Redis解决方案-Redis-2 Redis基础数据类型与基本使用
源代码在GitHub - 629y/food-social-contact-parent: redis项目-美食社交APP
1. Redis基本数据类型
1.字符串(strings)
set username zhangsan
get username
mset age 18 address bj
设置多个
mget username age
获取多个
incr num
incr-递增+1 num-变量从0开始
decr num
decr-递减-1
incrby num 2
incrby-以每次(+2)递增
decrby num 3
decrby-以每次(-3)递减
del num
删除数据
2.散列(hashes)
hset userInfo username zhangsan age 18 address bj
hget userInfo username
hget userInfo age
hmget userInfo username age
获取多个
hgetall userInfo
获取全部
hlen userInfo
获取key的数量
hincrby userInfo age 2
hincrby-(age)以每次(+2)递增
hdel userInfo age
删除age
del userInfo
删除userInfo
3.列表(lists)
lpush student zhangsan lisi wangwu
lpush-左插入
rpush student tianqi
rpush-右插入
lpop student
lpop-左弹出
rpop student
rpop-右弹出
lrange student 0 1
student中从下标为(0)到(1)排序查看
4.集合(sets)-去重
sadd nums 1 2 3
sadd nums 1 1 2 2 3 3
不会添加重复的
smembers nums
查看nums中的所有
srem nums 2
移除2
spop nums
随机弹出
sadd nums1 1 2 3
sadd nums2 2 3 4
sinter nums1 nums2
sinter-交集-2,3
sdiff nums1 nums2
(nums1)相对于(nums2)的差集-1
sunion nums1 nums2
并集-1,2,3,4
5.有序集合(sorted sets)
zadd rank 66 zhangsan 88 lisi 77 wangwu 99 zhaoliu
zadd rank 66 zhangsan 88 lisi 77 wangwu 99 zhaoliu
去重添加rank
zrange rank 0 3
从下标(0)到(3)按权重输出
zrangebyscore rank 77 99
将分数是在(77)-(99)之间的按权重输出
zrem rank zhaoliu
移除zhaoliu
zcard rank
统计rank中的数量
zcount rank 77 88
统计在包含在(77)-(88)之间的rank中的数量
zrank rank wangwu
查看wangwu在rank中的下标
zrevrank rank zhangsan
查看zhangsan在rank中的翻转下标
2. Redis之Sorted Set底层算法分析
二分查找
3. 认证中心需求分析
用户登录
4. 公共项目环境搭建
创建数据库
db_imooc.sql
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 80018
Source Host : localhost:3306
Source Schema : db_imooc
Target Server Type : MySQL
Target Server Version : 80018
File Encoding : 65001
Date: 14/11/2020 19:06:41
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_dictionary
-- ----------------------------
DROP TABLE IF EXISTS `t_dictionary`;
CREATE TABLE `t_dictionary` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`data` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`display_order` int(11) NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 415 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_dictionary
-- ----------------------------
INSERT INTO `t_dictionary` VALUES (1, 'TableType ', 'Regular|大厅', 0);
INSERT INTO `t_dictionary` VALUES (2, 'TableType ', 'Bar|吧台', 0);
INSERT INTO `t_dictionary` VALUES (3, 'TableType ', 'Window|靠窗', 0);
INSERT INTO `t_dictionary` VALUES (4, 'TableType ', 'Outdoor|户外', 0);
INSERT INTO `t_dictionary` VALUES (5, 'TableType ', 'Private|包间', 0);
INSERT INTO `t_dictionary` VALUES (7, 'RestaurantTag ', '24|hours|24小时营业', 0);
INSERT INTO `t_dictionary` VALUES (8, 'RestaurantTag ', 'Afternoon|tea|下午茶', 0);
INSERT INTO `t_dictionary` VALUES (9, 'RestaurantTag ', 'All|you|can|eat|自助餐', 0);
INSERT INTO `t_dictionary` VALUES (10, 'RestaurantTag ', 'Bistros|酒馆', 0);
INSERT INTO `t_dictionary` VALUES (11, 'RestaurantTag ', 'Breakfast|早餐', 0);
INSERT INTO `t_dictionary` VALUES (12, 'RestaurantTag ', 'Bund|view|外滩风景', 0);
INSERT INTO `t_dictionary` VALUES (13, 'RestaurantTag ', 'Classic|Shanghai|老上海', 0);
INSERT INTO `t_dictionary` VALUES (14, 'RestaurantTag ', 'Cocktails|鸡尾酒', 0);
INSERT INTO `t_dictionary` VALUES (15, 'RestaurantTag ', 'Credit|cards|accepted|可刷卡', 0);
INSERT INTO `t_dictionary` VALUES (16, 'RestaurantTag ', 'Delivery|可送外卖', 0);
INSERT INTO `t_dictionary` VALUES (17, 'RestaurantTag ', 'Pet|friendly|宠物友好', 0);
INSERT INTO `t_dictionary` VALUES (18, 'RestaurantTag ', 'Kids|friendly||适合小孩', 0);
INSERT INTO `t_dictionary` VALUES (19, 'RestaurantTag ', 'Fine|dining|顶级餐厅', 0);
INSERT INTO `t_dictionary` VALUES (20, 'RestaurantTag ', 'Free|parking|免费停车', 0);
INSERT INTO `t_dictionary` VALUES (21, 'RestaurantTag ', 'Lounge|酒廊', 0);
INSERT INTO `t_dictionary` VALUES (22, 'RestaurantTag ', 'Lunch|set|午市套餐', 0);
INSERT INTO `t_dictionary` VALUES (23, 'RestaurantTag ', 'Group|dining|团体', 0);
INSERT INTO `t_dictionary` VALUES (24, 'RestaurantTag ', 'Healthy|健康', 0);
INSERT INTO `t_dictionary` VALUES (25, 'RestaurantTag ', 'Historic|building|历史建筑', 0);
INSERT INTO `t_dictionary` VALUES (26, 'RestaurantTag ', 'Hotel|restaurant||酒店餐厅', 0);
INSERT INTO `t_dictionary` VALUES (27, 'RestaurantTag ', 'Ice|cream|冰激凌', 0);
INSERT INTO `t_dictionary` VALUES (28, 'RestaurantTag ', 'Late|night|dining|夜宵', 0);
INSERT INTO `t_dictionary` VALUES (29, 'RestaurantTag ', 'Non-smoking|有无烟区', 0);
INSERT INTO `t_dictionary` VALUES (30, 'RestaurantTag ', 'Notable|wine|list|葡萄酒', 0);
INSERT INTO `t_dictionary` VALUES (32, 'RestaurantTag ', 'Outdoor|seating|户外餐桌', 0);
INSERT INTO `t_dictionary` VALUES (33, 'RestaurantTag ', 'Performance|现场表演', 0);
INSERT INTO `t_dictionary` VALUES (34, 'RestaurantTag ', 'Romantic||浪漫', 0);
INSERT INTO `t_dictionary` VALUES (35, 'RestaurantTag ', 'Ramen|日式拉面', 0);
INSERT INTO `t_dictionary` VALUES (36, 'RestaurantTag ', 'Salads|沙拉', 0);
INSERT INTO `t_dictionary` VALUES (37, 'RestaurantTag ', 'Sandwiches & Delis|三明治&熟食', 0);
INSERT INTO `t_dictionary` VALUES (38, 'RestaurantTag ', 'Smoothies|冰沙', 0);
INSERT INTO `t_dictionary` VALUES (39, 'RestaurantTag ', 'Tapas|西班牙小吃', 0);
INSERT INTO `t_dictionary` VALUES (40, 'RestaurantTag ', 'Themed|restaurant|主题餐厅', 0);
INSERT INTO `t_dictionary` VALUES (41, 'RestaurantTag ', 'Villa|别墅', 0);
INSERT INTO `t_dictionary` VALUES (43, 'Cuisine ', 'American|北美菜', 0);
INSERT INTO `t_dictionary` VALUES (45, 'Cuisine ', 'Australian|澳洲菜', 0);
INSERT INTO `t_dictionary` VALUES (48, 'Cuisine ', 'Barbecue|烧烤', 0);
INSERT INTO `t_dictionary` VALUES (50, 'Cuisine ', 'Beijing|京菜', 0);
INSERT INTO `t_dictionary` VALUES (63, 'Cuisine ', 'Dongbei|东北菜', 0);
INSERT INTO `t_dictionary` VALUES (65, 'Cuisine ', 'Hunan|湘菜', 0);
INSERT INTO `t_dictionary` VALUES (68, 'Cuisine ', 'French|法国菜', 0);
INSERT INTO `t_dictionary` VALUES (70, 'Cuisine ', 'Fusion|创意菜', 0);
INSERT INTO `t_dictionary` VALUES (71, 'Cuisine ', 'German|德国菜', 0);
INSERT INTO `t_dictionary` VALUES (72, 'Cuisine ', 'Grocery|杂货', 0);
INSERT INTO `t_dictionary` VALUES (73, 'Cuisine ', 'Halal|清真', 0);
INSERT INTO `t_dictionary` VALUES (76, 'Cuisine ', 'Hot Pot|火锅', 0);
INSERT INTO `t_dictionary` VALUES (79, 'Cuisine ', 'Indian|印度菜', 0);
INSERT INTO `t_dictionary` VALUES (80, 'Cuisine ', 'Indonesian|印尼菜', 0);
INSERT INTO `t_dictionary` VALUES (81, 'Cuisine ', 'Italian|意大利菜', 0);
INSERT INTO `t_dictionary` VALUES (82, 'Cuisine ', 'Japanese|日本料理', 0);
INSERT INTO `t_dictionary` VALUES (84, 'Cuisine ', 'Jiangxi|赣菜', 0);
INSERT INTO `t_dictionary` VALUES (88, 'Cuisine ', 'Malaysian|马来西亚菜', 0);
INSERT INTO `t_dictionary` VALUES (91, 'Cuisine ', 'Mediterranean|地中海菜', 0);
INSERT INTO `t_dictionary` VALUES (92, 'Cuisine ', 'Mexican / Tex-Mex|墨西哥菜', 0);
INSERT INTO `t_dictionary` VALUES (94, 'Cuisine ', 'Other|其他', 0);
INSERT INTO `t_dictionary` VALUES (102, 'Cuisine ', 'Portuguese|葡国菜', 0);
INSERT INTO `t_dictionary` VALUES (103, 'Cuisine ', 'Russian|俄国菜', 0);
INSERT INTO `t_dictionary` VALUES (104, 'Cuisine ', 'Sandwiches & Delis|三明治&简食', 0);
INSERT INTO `t_dictionary` VALUES (107, 'Cuisine ', 'Shaoxing|绍兴菜', 0);
INSERT INTO `t_dictionary` VALUES (111, 'Cuisine ', 'Shanghainese|上海菜', 0);
INSERT INTO `t_dictionary` VALUES (112, 'Cuisine ', 'Singaporean|新加坡菜', 0);
INSERT INTO `t_dictionary` VALUES (113, 'Cuisine ', 'South American|南美菜', 0);
INSERT INTO `t_dictionary` VALUES (114, 'Cuisine ', 'Spanish|西班牙菜', 0);
INSERT INTO `t_dictionary` VALUES (115, 'Cuisine ', 'Steakhouse|牛排店', 0);
INSERT INTO `t_dictionary` VALUES (117, 'Cuisine ', 'Taiwanese|台湾菜', 0);
INSERT INTO `t_dictionary` VALUES (118, 'Cuisine ', 'Thai|泰国菜', 0);
INSERT INTO `t_dictionary` VALUES (121, 'Cuisine ', 'Turkish|土耳其菜', 0);
INSERT INTO `t_dictionary` VALUES (122, 'Cuisine ', 'Vegetarian|素食', 0);
INSERT INTO `t_dictionary` VALUES (123, 'Cuisine ', 'Vietnamese|越南菜', 0);
INSERT INTO `t_dictionary` VALUES (124, 'Cuisine ', 'Wine Bar|红酒吧', 0);
INSERT INTO `t_dictionary` VALUES (126, 'Cuisine ', 'Yunnan|云南菜', 0);
INSERT INTO `t_dictionary` VALUES (129, 'Cuisine ', 'Zhejiang|浙菜', 0);
INSERT INTO `t_dictionary` VALUES (130, 'nations ', 'Afghanistan | 阿富汗', 0);
INSERT INTO `t_dictionary` VALUES (131, 'nations ', 'Albania | 阿尔巴尼亚', 0);
INSERT INTO `t_dictionary` VALUES (132, 'nations ', 'Algeria | 阿尔及利亚', 0);
INSERT INTO `t_dictionary` VALUES (133, 'nations ', 'Andorra | 安道尔', 0);
INSERT INTO `t_dictionary` VALUES (134, 'nations ', 'Angola | 安哥拉', 0);
INSERT INTO `t_dictionary` VALUES (135, 'nations ', 'Argentina | 阿根廷', 0);
INSERT INTO `t_dictionary` VALUES (136, 'nations ', 'Armenia | 亚美尼亚', 0);
INSERT INTO `t_dictionary` VALUES (137, 'nations ', 'Australia | 澳大利亚', 0);
INSERT INTO `t_dictionary` VALUES (138, 'nations ', 'Austria | 奥地利', 0);
INSERT INTO `t_dictionary` VALUES (139, 'nations ', 'Azerbaijan | 阿塞拜疆', 0);
INSERT INTO `t_dictionary` VALUES (140, 'nations ', 'Bahamas | 巴哈马', 0);
INSERT INTO `t_dictionary` VALUES (141, 'nations ', 'Bahrain | 巴林', 0);
INSERT INTO `t_dictionary` VALUES (142, 'nations ', 'Bangladesh | 孟加拉国', 0);
INSERT INTO `t_dictionary` VALUES (143, 'nations ', 'Barbados | 巴巴多斯', 0);
INSERT INTO `t_dictionary` VALUES (144, 'nations ', 'Belarus | 白俄罗斯', 0);
INSERT INTO `t_dictionary` VALUES (145, 'nations ', 'Belgium | 比利时', 0);
INSERT INTO `t_dictionary` VALUES (146, 'nations ', 'Belize | 伯利兹', 0);
INSERT INTO `t_dictionary` VALUES (147, 'nations ', 'Benin | 柏林', 0);
INSERT INTO `t_dictionary` VALUES (148, 'nations ', 'Bhutan | 不丹', 0);
INSERT INTO `t_dictionary` VALUES (149, 'nations ', 'Bolivia | 玻利维亚', 0);
INSERT INTO `t_dictionary` VALUES (150, 'nations ', 'Bosnia-Herzegovina | 波斯尼亚和黑塞哥维那', 0);
INSERT INTO `t_dictionary` VALUES (151, 'nations ', 'Botswana | 博茨瓦纳', 0);
INSERT INTO `t_dictionary` VALUES (152, 'nations ', 'Brazil | 巴西', 0);
INSERT INTO `t_dictionary` VALUES (154, 'nations ', 'Brunei | 文莱', 0);
INSERT INTO `t_dictionary` VALUES (155, 'nations ', 'Bulgaria | 保加利亚', 0);
INSERT INTO `t_dictionary` VALUES (156, 'nations ', 'Burkina | 布基纳法索', 0);
INSERT INTO `t_dictionary` VALUES (157, 'nations ', 'Burma (Myanmar) | 缅甸', 0);
INSERT INTO `t_dictionary` VALUES (158, 'nations ', 'Burundi | 布隆迪', 0);
INSERT INTO `t_dictionary` VALUES (159, 'nations ', 'Cambodia | 柬埔寨', 0);
INSERT INTO `t_dictionary` VALUES (160, 'nations ', 'Cameroon | 喀麦隆', 0);
INSERT INTO `t_dictionary` VALUES (161, 'nations ', 'Canada | 加拿大', 0);
INSERT INTO `t_dictionary` VALUES (162, 'nations ', 'Cape Verde Islands | 佛得角群岛', 0);
INSERT INTO `t_dictionary` VALUES (163, 'nations ', 'Chad | 乍得', 0);
INSERT INTO `t_dictionary` VALUES (164, 'nations ', 'Chile | 智利', 0);
INSERT INTO `t_dictionary` VALUES (166, 'nations ', 'Colombia | 哥伦比亚', 0);
INSERT INTO `t_dictionary` VALUES (167, 'nations ', 'Congo | 刚果', 0);
INSERT INTO `t_dictionary` VALUES (168, 'nations ', 'Costa Rica | 哥斯达黎加', 0);
INSERT INTO `t_dictionary` VALUES (169, 'nations ', 'Croatia | 克罗地亚', 0);
INSERT INTO `t_dictionary` VALUES (170, 'nations ', 'Cuba | 古巴', 0);
INSERT INTO `t_dictionary` VALUES (171, 'nations ', 'Cyprus | 塞浦路斯', 0);
INSERT INTO `t_dictionary` VALUES (172, 'nations ', 'Czech Republic | 捷克共和国', 0);
INSERT INTO `t_dictionary` VALUES (173, 'nations ', 'Denmark | 丹麦', 0);
INSERT INTO `t_dictionary` VALUES (174, 'nations ', 'Djibouti | 吉布提', 0);
INSERT INTO `t_dictionary` VALUES (175, 'nations ', 'Dominica | 多米尼加', 0);
INSERT INTO `t_dictionary` VALUES (176, 'nations ', 'Dominican Republic | 多米尼加国共和国', 0);
INSERT INTO `t_dictionary` VALUES (177, 'nations ', 'Ecuador | 厄瓜多尔', 0);
INSERT INTO `t_dictionary` VALUES (178, 'nations ', 'Egypt | 埃及', 0);
INSERT INTO `t_dictionary` VALUES (179, 'nations ', 'El Salvador | 萨尔瓦多', 0);
INSERT INTO `t_dictionary` VALUES (180, 'nations ', 'England | 英格兰', 0);
INSERT INTO `t_dictionary` VALUES (181, 'nations ', 'Eritrea | 厄立特里亚', 0);
INSERT INTO `t_dictionary` VALUES (182, 'nations ', 'Estonia | 爱沙尼亚', 0);
INSERT INTO `t_dictionary` VALUES (183, 'nations ', 'Ethiopia | 埃塞俄比亚', 0);
INSERT INTO `t_dictionary` VALUES (184, 'nations ', 'Fiji | 斐济', 0);
INSERT INTO `t_dictionary` VALUES (185, 'nations ', 'Finland | 芬兰', 0);
INSERT INTO `t_dictionary` VALUES (186, 'nations ', 'France | 法国', 0);
INSERT INTO `t_dictionary` VALUES (187, 'nations ', 'Gabon | 加蓬', 0);
INSERT INTO `t_dictionary` VALUES (188, 'nations ', 'Gambia | 冈比亚', 0);
INSERT INTO `t_dictionary` VALUES (189, 'nations ', 'Georgia | 格鲁吉亚', 0);
INSERT INTO `t_dictionary` VALUES (190, 'nations ', 'Germany | 德国', 0);
INSERT INTO `t_dictionary` VALUES (191, 'nations ', 'Ghana | 加纳', 0);
INSERT INTO `t_dictionary` VALUES (192, 'nations ', 'Greece | 希腊', 0);
INSERT INTO `t_dictionary` VALUES (193, 'nations ', 'Grenada | 格林纳达', 0);
INSERT INTO `t_dictionary` VALUES (194, 'nations ', 'Guatemala | 危地马拉', 0);
INSERT INTO `t_dictionary` VALUES (195, 'nations ', 'Guinea | 几内亚', 0);
INSERT INTO `t_dictionary` VALUES (196, 'nations ', 'Guyana | 圭亚那', 0);
INSERT INTO `t_dictionary` VALUES (197, 'nations ', 'Haiti | 海地', 0);
INSERT INTO `t_dictionary` VALUES (198, 'nations ', 'Netherlands | 荷兰', 0);
INSERT INTO `t_dictionary` VALUES (199, 'nations ', 'Honduras | 洪都拉斯', 0);
INSERT INTO `t_dictionary` VALUES (200, 'nations ', 'Hungary | 匈牙利', 0);
INSERT INTO `t_dictionary` VALUES (201, 'nations ', 'Iceland | 冰岛', 0);
INSERT INTO `t_dictionary` VALUES (202, 'nations ', 'India | 印度', 0);
INSERT INTO `t_dictionary` VALUES (203, 'nations ', 'Indonesia | 印度尼西亚', 0);
INSERT INTO `t_dictionary` VALUES (204, 'nations ', 'Iran | 伊朗', 0);
INSERT INTO `t_dictionary` VALUES (205, 'nations ', 'Iraq | 伊拉克', 0);
INSERT INTO `t_dictionary` VALUES (206, 'nations ', 'Ireland | 爱尔兰', 0);
INSERT INTO `t_dictionary` VALUES (207, 'nations ', 'Italy | 意大利', 0);
INSERT INTO `t_dictionary` VALUES (208, 'nations ', 'Jamaica | 牙买加', 0);
INSERT INTO `t_dictionary` VALUES (209, 'nations ', 'Japan | 日本', 0);
INSERT INTO `t_dictionary` VALUES (210, 'nations ', 'Jordan | 约旦', 0);
INSERT INTO `t_dictionary` VALUES (211, 'nations ', 'Kazakhstan | 哈萨克斯坦', 0);
INSERT INTO `t_dictionary` VALUES (212, 'nations ', 'Kenya | 肯尼亚', 0);
INSERT INTO `t_dictionary` VALUES (213, 'nations ', 'Kuwait | 科威特', 0);
INSERT INTO `t_dictionary` VALUES (214, 'nations ', 'Laos | 老挝', 0);
INSERT INTO `t_dictionary` VALUES (215, 'nations ', 'Latvia | 拉脱维亚', 0);
INSERT INTO `t_dictionary` VALUES (216, 'nations ', 'Lebanon | 黎巴嫩', 0);
INSERT INTO `t_dictionary` VALUES (217, 'nations ', 'Liberia | 利比里亚', 0);
INSERT INTO `t_dictionary` VALUES (218, 'nations ', 'Libya | 利比亚', 0);
INSERT INTO `t_dictionary` VALUES (219, 'nations ', 'Liechtenstein | 列支敦士登', 0);
INSERT INTO `t_dictionary` VALUES (220, 'nations ', 'Lithuania | 立陶宛', 0);
INSERT INTO `t_dictionary` VALUES (221, 'nations ', 'Luxembourg | 卢森堡', 0);
INSERT INTO `t_dictionary` VALUES (222, 'nations ', 'Macedonia马其顿', 0);
INSERT INTO `t_dictionary` VALUES (223, 'nations ', 'Madagascar | 马达加斯加', 0);
INSERT INTO `t_dictionary` VALUES (224, 'nations ', 'Malawi | 马拉维', 0);
INSERT INTO `t_dictionary` VALUES (225, 'nations ', 'Malaysia | 马来西亚', 0);
INSERT INTO `t_dictionary` VALUES (226, 'nations ', 'Maldives | 马尔代夫', 0);
INSERT INTO `t_dictionary` VALUES (227, 'nations ', 'Mali | 马里', 0);
INSERT INTO `t_dictionary` VALUES (228, 'nations ', 'Malta | 马耳他', 0);
INSERT INTO `t_dictionary` VALUES (229, 'nations ', 'Mauritania | 毛里塔尼亚', 0);
INSERT INTO `t_dictionary` VALUES (230, 'nations ', 'Mauritius | 毛里求斯', 0);
INSERT INTO `t_dictionary` VALUES (231, 'nations ', 'Mexico | 墨西哥', 0);
INSERT INTO `t_dictionary` VALUES (232, 'nations ', 'Moldova | 摩尔多瓦', 0);
INSERT INTO `t_dictionary` VALUES (233, 'nations ', 'Monaco | 摩纳哥', 0);
INSERT INTO `t_dictionary` VALUES (234, 'nations ', 'Mongolia | 蒙古', 0);
INSERT INTO `t_dictionary` VALUES (235, 'nations ', 'Montenegro | 黑山', 0);
INSERT INTO `t_dictionary` VALUES (236, 'nations ', 'Morocco | 摩洛哥', 0);
INSERT INTO `t_dictionary` VALUES (237, 'nations ', 'Mozambique | 莫桑比克', 0);
INSERT INTO `t_dictionary` VALUES (239, 'nations ', 'Namibia | 纳米比亚', 0);
INSERT INTO `t_dictionary` VALUES (240, 'nations ', 'Nepal | 尼泊尔', 0);
INSERT INTO `t_dictionary` VALUES (242, 'nations ', 'New Zealand | 新西兰', 0);
INSERT INTO `t_dictionary` VALUES (243, 'nations ', 'Nicaragua | 尼加拉瓜', 0);
INSERT INTO `t_dictionary` VALUES (244, 'nations ', 'Niger | 尼日尔', 0);
INSERT INTO `t_dictionary` VALUES (245, 'nations ', 'Nigeria | 尼日利亚', 0);
INSERT INTO `t_dictionary` VALUES (246, 'nations ', 'North Korea | 朝鲜', 0);
INSERT INTO `t_dictionary` VALUES (247, 'nations ', 'Norway | 挪威', 0);
INSERT INTO `t_dictionary` VALUES (248, 'nations ', 'Oman | 阿曼', 0);
INSERT INTO `t_dictionary` VALUES (249, 'nations ', 'Pakistan | 巴基斯坦', 0);
INSERT INTO `t_dictionary` VALUES (250, 'nations ', 'Panama | 巴拿马', 0);
INSERT INTO `t_dictionary` VALUES (251, 'nations ', 'Papua New Guinea | 巴布亚新几内亚', 0);
INSERT INTO `t_dictionary` VALUES (252, 'nations ', 'Paraguay | 巴拉圭', 0);
INSERT INTO `t_dictionary` VALUES (253, 'nations ', 'Peru | 秘鲁', 0);
INSERT INTO `t_dictionary` VALUES (254, 'nations ', 'Philippines | 菲律宾', 0);
INSERT INTO `t_dictionary` VALUES (255, 'nations ', 'Poland | 波兰', 0);
INSERT INTO `t_dictionary` VALUES (256, 'nations ', 'Portugal | 葡萄牙', 0);
INSERT INTO `t_dictionary` VALUES (257, 'nations ', 'Qatar | 卡塔尔', 0);
INSERT INTO `t_dictionary` VALUES (258, 'nations ', 'Romania | 罗马尼亚', 0);
INSERT INTO `t_dictionary` VALUES (259, 'nations ', 'Russia | 俄罗斯', 0);
INSERT INTO `t_dictionary` VALUES (260, 'nations ', 'Rwanda | 卢旺达', 0);
INSERT INTO `t_dictionary` VALUES (261, 'nations ', 'Saudi Arabia | 沙特阿拉伯', 0);
INSERT INTO `t_dictionary` VALUES (262, 'nations ', 'Scotland | 苏格兰', 0);
INSERT INTO `t_dictionary` VALUES (263, 'nations ', 'Senegal | 塞内加尔', 0);
INSERT INTO `t_dictionary` VALUES (264, 'nations ', 'Serbia | 塞尔维亚', 0);
INSERT INTO `t_dictionary` VALUES (265, 'nations ', 'Seychelles | 塞舌尔', 0);
INSERT INTO `t_dictionary` VALUES (266, 'nations ', 'Sierra Leone | 塞拉里昂', 0);
INSERT INTO `t_dictionary` VALUES (267, 'nations ', 'Singapore | 新加坡', 0);
INSERT INTO `t_dictionary` VALUES (268, 'nations ', 'Slovakia | 斯洛伐克', 0);
INSERT INTO `t_dictionary` VALUES (269, 'nations ', 'Slovenia | 斯洛伐尼亚', 0);
INSERT INTO `t_dictionary` VALUES (270, 'nations ', 'Solomon Islands | 所罗门群岛', 0);
INSERT INTO `t_dictionary` VALUES (271, 'nations ', 'Somalia | 索马里', 0);
INSERT INTO `t_dictionary` VALUES (272, 'nations ', 'South Africa | 南非', 0);
INSERT INTO `t_dictionary` VALUES (273, 'nations ', 'South Korea | 韩国', 0);
INSERT INTO `t_dictionary` VALUES (274, 'nations ', 'Spain | 西班牙', 0);
INSERT INTO `t_dictionary` VALUES (275, 'nations ', 'Sri Lanka | 斯里兰卡', 0);
INSERT INTO `t_dictionary` VALUES (276, 'nations ', 'Sudan | 苏丹', 0);
INSERT INTO `t_dictionary` VALUES (277, 'nations ', 'Suriname | 苏里南', 0);
INSERT INTO `t_dictionary` VALUES (278, 'nations ', 'Swaziland | 斯维士兰', 0);
INSERT INTO `t_dictionary` VALUES (279, 'nations ', 'Sweden | 瑞典', 0);
INSERT INTO `t_dictionary` VALUES (280, 'nations ', 'Switzerland | 瑞士', 0);
INSERT INTO `t_dictionary` VALUES (281, 'nations ', 'Syria | 叙利亚', 0);
INSERT INTO `t_dictionary` VALUES (282, 'nations ', 'Taiwan | 台湾', 0);
INSERT INTO `t_dictionary` VALUES (283, 'nations ', 'Tajikistan | 塔吉克斯坦', 0);
INSERT INTO `t_dictionary` VALUES (284, 'nations ', 'Tanzania | 坦桑尼亚', 0);
INSERT INTO `t_dictionary` VALUES (285, 'nations ', 'Thailand | 泰国', 0);
INSERT INTO `t_dictionary` VALUES (286, 'nations ', 'Togo | 多哥', 0);
INSERT INTO `t_dictionary` VALUES (287, 'nations ', 'Trinidad and Tobago | 特里尼达和多巴哥', 0);
INSERT INTO `t_dictionary` VALUES (289, 'nations ', 'Tunisia | 突尼斯', 0);
INSERT INTO `t_dictionary` VALUES (290, 'nations ', 'Turkey | 土耳其', 0);
INSERT INTO `t_dictionary` VALUES (291, 'nations ', 'Turkmenistan | 土库曼斯坦', 0);
INSERT INTO `t_dictionary` VALUES (292, 'nations ', 'Tuvalu | 图瓦卢', 0);
INSERT INTO `t_dictionary` VALUES (293, 'nations ', 'Uganda | 乌干达', 0);
INSERT INTO `t_dictionary` VALUES (294, 'nations ', 'Ukraine | 乌克兰', 0);
INSERT INTO `t_dictionary` VALUES (295, 'nations ', 'United Arab Emirates | 阿拉伯联合大公国', 0);
INSERT INTO `t_dictionary` VALUES (296, 'nations ', 'United Kingdom | 联合王国', 0);
INSERT INTO `t_dictionary` VALUES (297, 'nations ', 'U.S.A. | 美国', 0);
INSERT INTO `t_dictionary` VALUES (298, 'nations ', 'Uruguay | 乌拉圭', 0);
INSERT INTO `t_dictionary` VALUES (299, 'nations ', 'Uzbekistan | 乌兹别克斯坦', 0);
INSERT INTO `t_dictionary` VALUES (300, 'nations ', 'Vanuatu | 瓦努阿图', 0);
INSERT INTO `t_dictionary` VALUES (301, 'nations ', 'Vatican City | 梵蒂冈', 0);
INSERT INTO `t_dictionary` VALUES (302, 'nations ', 'Venezuela | 委内瑞拉', 0);
INSERT INTO `t_dictionary` VALUES (303, 'nations ', 'Vietnam | 越南', 0);
INSERT INTO `t_dictionary` VALUES (304, 'nations ', 'Wales | 威尔士', 0);
INSERT INTO `t_dictionary` VALUES (305, 'nations ', 'Western Samoa | 西萨摩亚', 0);
INSERT INTO `t_dictionary` VALUES (306, 'nations ', 'Yemen | 也门', 0);
INSERT INTO `t_dictionary` VALUES (307, 'nations ', 'Yugoslavia | 南斯拉夫', 0);
INSERT INTO `t_dictionary` VALUES (308, 'nations ', 'Zaire | 扎伊尔', 0);
INSERT INTO `t_dictionary` VALUES (309, 'nations ', 'Zambia | 赞比亚', 0);
INSERT INTO `t_dictionary` VALUES (310, 'nations ', 'Zimbabwe | 津巴布韦', 0);
INSERT INTO `t_dictionary` VALUES (311, 'nations ', 'Anhui | 安徽', 0);
INSERT INTO `t_dictionary` VALUES (312, 'nations ', 'Fujian | 福建', 0);
INSERT INTO `t_dictionary` VALUES (313, 'nations ', 'Gansu | 甘肃', 0);
INSERT INTO `t_dictionary` VALUES (314, 'nations ', 'Guangdong | 广东', 0);
INSERT INTO `t_dictionary` VALUES (315, 'nations ', 'Guizhou | 贵州', 0);
INSERT INTO `t_dictionary` VALUES (316, 'nations ', 'Hainan | 海南', 0);
INSERT INTO `t_dictionary` VALUES (317, 'nations ', 'Hebei | 河北', 0);
INSERT INTO `t_dictionary` VALUES (318, 'nations ', 'Heilongjiang | 黑龙江', 0);
INSERT INTO `t_dictionary` VALUES (319, 'nations ', 'Henan | 河南', 0);
INSERT INTO `t_dictionary` VALUES (320, 'nations ', 'Hubei | 湖北', 0);
INSERT INTO `t_dictionary` VALUES (321, 'nations ', 'Hunan | 湖南', 0);
INSERT INTO `t_dictionary` VALUES (322, 'nations ', 'Jiangsu | 江苏', 0);
INSERT INTO `t_dictionary` VALUES (323, 'nations ', 'Jiangxi | 江西', 0);
INSERT INTO `t_dictionary` VALUES (324, 'nations ', 'Jilin | 吉林', 0);
INSERT INTO `t_dictionary` VALUES (325, 'nations ', 'Liaoning | 辽宁', 0);
INSERT INTO `t_dictionary` VALUES (326, 'nations ', 'Qinghai | 青海', 0);
INSERT INTO `t_dictionary` VALUES (327, 'nations ', 'Shaanxi | 陕西', 0);
INSERT INTO `t_dictionary` VALUES (328, 'nations ', 'Shandong | 山东', 0);
INSERT INTO `t_dictionary` VALUES (329, 'nations ', 'Shanxi | 山西', 0);
INSERT INTO `t_dictionary` VALUES (330, 'nations ', 'Sichuan | 四川', 0);
INSERT INTO `t_dictionary` VALUES (331, 'nations ', 'Yunnan | 云南', 0);
INSERT INTO `t_dictionary` VALUES (332, 'nations ', 'Zhejiang | 浙江', 0);
INSERT INTO `t_dictionary` VALUES (333, 'nations ', 'Guangxi | 广西', 0);
INSERT INTO `t_dictionary` VALUES (334, 'nations ', 'Inner Mongolia | 内蒙古', 0);
INSERT INTO `t_dictionary` VALUES (335, 'nations ', 'Ningxia | 宁夏', 0);
INSERT INTO `t_dictionary` VALUES (336, 'nations ', 'Xinjiang | 新疆', 0);
INSERT INTO `t_dictionary` VALUES (337, 'nations ', 'Tibet | 西藏', 0);
INSERT INTO `t_dictionary` VALUES (338, 'nations ', 'Beijing | 北京', 0);
INSERT INTO `t_dictionary` VALUES (339, 'nations ', 'Chongqing | 重庆', 0);
INSERT INTO `t_dictionary` VALUES (340, 'nations ', 'Shanghai | 上海', 0);
INSERT INTO `t_dictionary` VALUES (341, 'nations ', 'Tianjin | 天津', 0);
INSERT INTO `t_dictionary` VALUES (342, 'nations ', 'Hong Kong | 香港', 0);
INSERT INTO `t_dictionary` VALUES (343, 'nations ', 'Macau | 澳门', 0);
INSERT INTO `t_dictionary` VALUES (346, 'RestaurantTag ', 'Wifi|无线上网', 0);
INSERT INTO `t_dictionary` VALUES (349, 'RestaurantTag ', 'Good|View|有景观位', 0);
INSERT INTO `t_dictionary` VALUES (351, 'RestaurantTag ', 'Big|Party|大型宴会', 0);
INSERT INTO `t_dictionary` VALUES (352, 'RestaurantTag ', 'Birthday|Party|生日宴会', 0);
INSERT INTO `t_dictionary` VALUES (353, 'RestaurantTag ', 'BYOB|自带酒水', 0);
INSERT INTO `t_dictionary` VALUES (354, 'RestaurantStyle ', '朋友聚餐', 0);
INSERT INTO `t_dictionary` VALUES (355, 'RestaurantStyle ', '家庭聚会', 0);
INSERT INTO `t_dictionary` VALUES (356, 'RestaurantStyle ', '随便吃吃', 0);
INSERT INTO `t_dictionary` VALUES (357, 'RestaurantStyle ', '休闲小憩', 0);
INSERT INTO `t_dictionary` VALUES (358, 'RestaurantStyle ', '情侣约会', 0);
INSERT INTO `t_dictionary` VALUES (359, 'RestaurantStyle ', '商务宴请', 0);
INSERT INTO `t_dictionary` VALUES (360, 'InviteStatus ', '即将接洽', 0);
INSERT INTO `t_dictionary` VALUES (361, 'InviteStatus ', '等待老板决定', 0);
INSERT INTO `t_dictionary` VALUES (362, 'InviteStatus ', '合同签署中', 0);
INSERT INTO `t_dictionary` VALUES (363, 'InviteStatus ', '合同已经签订', 0);
INSERT INTO `t_dictionary` VALUES (370, 'Cuisine', 'All-You-Can-Eat|自助餐', 0);
INSERT INTO `t_dictionary` VALUES (371, 'Cuisine', 'Bar|酒吧', 0);
INSERT INTO `t_dictionary` VALUES (372, 'Cuisine', 'Cafe|咖啡厅', 0);
INSERT INTO `t_dictionary` VALUES (373, 'Cuisine', 'Cantonese|粤菜', 0);
INSERT INTO `t_dictionary` VALUES (374, 'Cuisine', 'Dessert|甜品', 0);
INSERT INTO `t_dictionary` VALUES (375, 'Cuisine', 'Global Cuisine|环球美食', 0);
INSERT INTO `t_dictionary` VALUES (376, 'Cuisine', 'Middle Eastern|中东菜', 0);
INSERT INTO `t_dictionary` VALUES (377, 'Cuisine', 'Southeast Asian|东南亚菜', 0);
INSERT INTO `t_dictionary` VALUES (378, 'Cuisine', 'Zhejiang|浙江菜', 0);
INSERT INTO `t_dictionary` VALUES (379, 'Cuisine', 'Fast Casual|小吃快餐', 0);
INSERT INTO `t_dictionary` VALUES (380, 'nations', 'Israel', 0);
INSERT INTO `t_dictionary` VALUES (381, 'nations', 'East Timor', 0);
INSERT INTO `t_dictionary` VALUES (382, 'nations', 'Central African Republic', 0);
INSERT INTO `t_dictionary` VALUES (383, 'nations', 'S?o Tomé and Principe', 0);
INSERT INTO `t_dictionary` VALUES (384, 'nations', 'Ivory Coast', 0);
INSERT INTO `t_dictionary` VALUES (385, 'nations', 'Lesotho', 0);
INSERT INTO `t_dictionary` VALUES (386, 'nations', 'Equatorial Guinea', 0);
INSERT INTO `t_dictionary` VALUES (387, 'nations', 'Guinea Bissau', 0);
INSERT INTO `t_dictionary` VALUES (400, 'Cuisine', 'Sushi|寿司', 0);
INSERT INTO `t_dictionary` VALUES (401, 'Cuisine', 'British|英国菜', 0);
INSERT INTO `t_dictionary` VALUES (402, 'Cuisine', 'Dim Sum|早茶点心', 0);
INSERT INTO `t_dictionary` VALUES (403, 'Cuisine', 'Xibei / Xinjiang|西北菜/新疆菜', 0);
INSERT INTO `t_dictionary` VALUES (405, 'Cuisine', 'Guizhou|黔菜', 0);
INSERT INTO `t_dictionary` VALUES (406, 'Cuisine', 'Pizza|披萨', 0);
INSERT INTO `t_dictionary` VALUES (408, 'Cuisine', 'Seafood|海鲜', 0);
INSERT INTO `t_dictionary` VALUES (409, 'Cuisine', 'Anhui|徽菜', 0);
INSERT INTO `t_dictionary` VALUES (411, 'Cuisine', 'Sichuan|川菜', 0);
INSERT INTO `t_dictionary` VALUES (412, 'Cuisine', 'Korean|韩国料理', 0);
INSERT INTO `t_dictionary` VALUES (413, 'Cuisine', 'Juice & Beverages|果汁饮料', 0);
INSERT INTO `t_dictionary` VALUES (414, 'Cuisine', 'Bakery & Pastries|面包烘焙', 0);
-- ----------------------------
-- Table structure for t_diners
-- ----------------------------
DROP TABLE IF EXISTS `t_diners`;
CREATE TABLE `t_diners` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称',
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`avatar_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '头像',
`roles` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '角色',
`is_valid` tinyint(1) NULL DEFAULT NULL,
`create_date` datetime(0) NULL DEFAULT NULL,
`update_date` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_diners
-- ----------------------------
INSERT INTO `t_diners` VALUES (1, 'abc', '昵称st', '13888888888', 'abc@imooc.com', 'e10adc3949ba59abbe56e057f20f883e', '/abc', 'ROLE_USER', 1, '2020-11-06 16:17:52', '2020-11-06 16:17:55');
INSERT INTO `t_diners` VALUES (2, 'test', 'test', '13666666666', NULL, 'e10adc3949ba59abbe56e057f20f883e', '/test', 'ROLE_USER', 1, '2020-11-12 12:01:13', '2020-11-12 12:01:13');
INSERT INTO `t_diners` VALUES (3, 'test2', 'test2', '13666666667', NULL, 'e10adc3949ba59abbe56e057f20f883e', '/test2', 'ROLE_USER', 1, '2020-11-12 17:47:12', '2020-11-12 17:47:12');
INSERT INTO `t_diners` VALUES (5, 'aaa', 'aaa', '12311112222', NULL, 'e10adc3949ba59abbe56e057f20f883e', '/aaa', 'ROLE_USER', 1, '2020-11-13 12:29:49', '2020-11-13 12:29:49');
-- ----------------------------
-- Table structure for t_feed
-- ----------------------------
DROP TABLE IF EXISTS `t_feed`;
CREATE TABLE `t_feed` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '内容',
`fk_diner_id` int(11) NULL DEFAULT NULL,
`praise_amount` int(11) NULL DEFAULT NULL COMMENT '点赞数量',
`comment_amount` int(11) NULL DEFAULT NULL COMMENT '评论数量',
`fk_restaurant_id` int(11) NULL DEFAULT NULL,
`create_date` datetime(0) NULL DEFAULT NULL,
`update_date` datetime(0) NULL DEFAULT NULL,
`is_valid` tinyint(1) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_feed
-- ----------------------------
-- ----------------------------
-- Table structure for t_follow
-- ----------------------------
DROP TABLE IF EXISTS `t_follow`;
CREATE TABLE `t_follow` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`diner_id` int(11) NULL DEFAULT NULL,
`follow_diner_id` int(11) NULL DEFAULT NULL,
`is_valid` tinyint(1) NULL DEFAULT NULL,
`create_date` datetime(0) NULL DEFAULT NULL,
`update_date` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `index_followeddiner_valid`(`follow_diner_id`, `is_valid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_follow
-- ----------------------------
-- ----------------------------
-- Table structure for t_restaurant
-- ----------------------------
DROP TABLE IF EXISTS `t_restaurant`;
CREATE TABLE `t_restaurant` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'the En Name of the restaurant',
`CnName` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`X` double NULL DEFAULT NULL,
`Y` double NULL DEFAULT NULL,
`Location` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'En location of the restaurant',
`CnLocation` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`Area` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'city.district.neighbourhood\r\nExample: Shanghai.Xuhui.Xujiahui',
`CnArea` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`Traffic` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'the information/descripton of the restaurant',
`Telephone` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Phone of the restaurant',
`Email` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`Website` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`Cuisine` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`AveragePrice` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`AvgLunchPrice` decimal(19, 0) NULL DEFAULT NULL COMMENT 'Average price of lunch',
`Introduction` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Indtroduction of the restaurant',
`Status` int(11) NULL DEFAULT 0 COMMENT '1=Opened 0=Closed',
`CreateDT` datetime(0) NULL DEFAULT NULL,
`IsValid` smallint(1) NULL DEFAULT 1 COMMENT '1=Valid 0=Invalid',
`Thumbnail` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'pics at the list, value would be:\r\nbasepath/original/picname',
`OpenHours` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`LikeVotes` int(10) NULL DEFAULT 0 COMMENT 'the percentage of people like it',
`DislikeVotes` int(10) NULL DEFAULT 0 COMMENT 'How many people votes',
`Amenities` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '设备',
`Tags` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'tags of the restaurant',
`OpenDate` datetime(0) NULL DEFAULT NULL,
`closeDate` datetime(0) NULL DEFAULT NULL,
`CityId` int(11) NULL DEFAULT 21 COMMENT '城市id',
PRIMARY KEY (`id`) USING BTREE,
INDEX `index_isvalid`(`IsValid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_restaurant
-- ----------------------------
INSERT INTO `t_restaurant` VALUES (14, '1931 Pub', '名古', 31.2158508275268, 121.461839852847, '112 Maoming Nan Lu, near Nanchang Lu', '茂名南路112号, 近南昌路', 'Xuhui.Fmr French Concession', '', '', '021 6472 5264', '', '', 'Shanghainese, Chinese', '¥¥', 0, '', NULL, '2014-05-04 19:26:28', 1, '', '', 1, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (15, '2001 Hong Kong Teahouse', '2001港式茶餐', 31.21385, 121.46051, '55 Shaanxi Nan Lu, near Changle Lu', '陕西南路55号, 近长乐路', 'Xuhui.Fmr French Concession', '', '', '021 5467 0205', '', '', 'Dim Sum, Chinese', '¥', 0, '', NULL, '2014-05-04 19:26:28', 4, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (16, '2nd floor', '2nd floor', 31.2162, 121.447998, '2/F, 810 Changle Lu,near Changshu Lu', '长乐路810号2楼, 近常熟路', 'Xuhui.Fmr French Concession', '', '', '13761133471', '', 'http://www.2ndfloor.asia', 'Cafe', '¥', 0, '', NULL, '2014-05-04 19:26:28', 3, '', '', 1, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (17, '400 Celsius', '400 Celsius', 31.19436, 121.43797, '1 Hongqiao Lu, 1/F, Grand Gateway, near Caoxi Bei Lu, Metro Line 1 Xujiahui Station', '虹桥路1号港汇广场1楼, 近漕溪北路, 地铁1号线徐家汇站', 'Xuhui.Xujiahui', '', '', '021 6447 0770', '', '', 'Steakhouse', '¥¥¥¥', 0, '', NULL, '2014-05-04 19:26:28', 3, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (18, '5 on the Bund', '5 on the Bund', 31.234482, 121.490753, 'Five on the Bund,20 Guangdong Lu, near Zhongshan Dong Yi Lu', '广东路20号, 近中山东一路', 'Huangpu.The Bund', '', '', '', '', '', 'Global Cuisine', '¥¥¥¥', 0, '', NULL, '2014-05-04 19:26:28', 3, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (19, '5 Tables Bistro', '5桌餐厅', 31.2174481541175, 121.47318647082, '210 Danshui Lu, near Zizhong Lu', '淡水路210号, 近自忠路', 'Luwan.Xintiandi', '', '', '021 3304 1205', '', 'www.weibo.com/5tables', 'European', '¥¥¥¥', 0, '', NULL, '2014-05-04 19:26:28', 4, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (20, '57 Du Xiang', '57度湘', 31.2250117063411, 121.47824432639, '138 Huaihai Zhong Lu, Infinity Plaza, 4/F, Room 401, near Longmen Lu', '淮海路138号无限度广场4楼401室, 近龙门路', 'Xuhui.Huaihai Zhong Lu', '', '', '021 3315 0057', '', '', 'Hunan, Chinese', '¥', 0, '', NULL, '2014-05-04 19:26:28', 1, 'restaurant/20/restaurant/T/160_160/1399622680327.JPG', 'Daily 11am-9pm', 17, 5, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (21, '609 Pho', '609 Pho', 31.237629, 121.438797, '609 Anyuan Lu, near Jiaozhou Lu', '安源路609号, 近胶州路', 'Jing\'an', '', '', '18201753996', '', '', 'Vietnamese', '¥', 0, '', NULL, '2014-05-04 19:26:28', 4, '', '', 0, 0, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
INSERT INTO `t_restaurant` VALUES (22, '70s Restaurant', '70后饭吧', 31.2398228737211, 121.438096413353, '1217 Changde Lu, near Changshou Lu', '常德路1217号, 近长寿路', 'Putuo', '', '', '021 6040 2808', '', '', 'Chinese', '¥¥', 0, '', NULL, '2014-05-04 19:26:28', 1, 'restaurant/22/restaurant/160_160/14075670693130533.JPG', '', 7, 2, '', '', '2016-01-04 11:22:23', '2016-01-04 11:22:23', 21);
-- ----------------------------
-- Table structure for t_seckill_vouchers
-- ----------------------------
DROP TABLE IF EXISTS `t_seckill_vouchers`;
CREATE TABLE `t_seckill_vouchers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fk_voucher_id` int(11) NULL DEFAULT NULL,
`amount` int(11) NULL DEFAULT NULL,
`start_time` datetime(0) NULL DEFAULT NULL,
`end_time` datetime(0) NULL DEFAULT NULL,
`is_valid` int(11) NULL DEFAULT NULL,
`create_date` datetime(0) NULL DEFAULT NULL,
`update_date` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_seckill_vouchers
-- ----------------------------
-- ----------------------------
-- Table structure for t_voucher
-- ----------------------------
DROP TABLE IF EXISTS `t_voucher`;
CREATE TABLE `t_voucher` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '代金券标题',
`thumbnail` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '缩略图',
`amount` int(11) NULL DEFAULT NULL COMMENT '抵扣金额',
`price` decimal(10, 2) NULL DEFAULT NULL COMMENT '售价',
`status` int(10) NULL DEFAULT NULL COMMENT '-1=过期 0=下架 1=上架',
`expire_time` datetime(0) NULL DEFAULT NULL COMMENT '过期时间',
`redeem_restaurant_id` int(10) NULL DEFAULT NULL COMMENT '验证餐厅',
`stock` int(11) NULL DEFAULT 0 COMMENT '库存',
`stock_left` int(11) NULL DEFAULT 0 COMMENT '剩余数量',
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述信息',
`clause` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '使用条款',
`create_date` datetime(0) NULL DEFAULT NULL,
`update_date` datetime(0) NULL DEFAULT NULL,
`is_valid` tinyint(1) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_voucher
-- ----------------------------
-- ----------------------------
-- Table structure for t_voucher_order
-- ----------------------------
DROP TABLE IF EXISTS `t_voucher_order`;
CREATE TABLE `t_voucher_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_no` int(11) NULL DEFAULT NULL,
`fk_voucher_id` int(11) NULL DEFAULT NULL,
`fk_diner_id` int(11) NULL DEFAULT NULL,
`qrcode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '图片地址',
`payment` tinyint(4) NULL DEFAULT NULL COMMENT '0=微信支付 1=支付宝支付',
`status` tinyint(1) NULL DEFAULT NULL COMMENT '订单状态:-1=已取消 0=未支付 1=已支付 2=已消费 3=已过期',
`fk_seckill_id` int(11) NULL DEFAULT NULL COMMENT '如果是抢购订单时,抢购订单的id',
`order_type` int(11) NULL DEFAULT NULL COMMENT '订单类型:0=正常订单 1=抢购订单',
`create_date` datetime(0) NULL DEFAULT NULL,
`update_date` datetime(0) NULL DEFAULT NULL,
`is_valid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of t_voucher_order
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
创建commons模块
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>food-social-contact-parent</artifactId>
<groupId>com.imooc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>commons</artifactId>
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- swagger -->
<dependency>
<groupId>com.battcn</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
</dependency>
<!-- security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
</dependencies>
</project>
全局常量类
全局异常类
ParameterException.java
package com.imooc.commons.exception;
import com.imooc.commons.constant.ApiConstant;
import lombok.Getter;
import lombok.Setter;
/**
* 全局异常类
*/
@Getter
@Setter
public class ParameterException extends RuntimeException {
private Integer errorCode;
public ParameterException() {
super(ApiConstant.ERROR_MESSAGE);
this.errorCode = ApiConstant.ERROR_CODE;
}
public ParameterException(Integer errorCode) {
this.errorCode = errorCode;
}
public ParameterException(String message) {
super(message);
this.errorCode = ApiConstant.ERROR_CODE;
}
public ParameterException(Integer errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
}
断言工具类
AssertUtil.java
package com.imooc.commons.utils;
import cn.hutool.core.util.StrUtil;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.exception.ParameterException;
/**
* 断言工具类
*/
public class AssertUtil {
/**
* 必须登录
*
* @param accessToken
*/
public static void mustLogin(String accessToken) {
if (StrUtil.isBlank(accessToken)) {
throw new ParameterException(ApiConstant.NO_LOGIN_CODE, ApiConstant.NO_LOGIN_MESSAGE);
}
}
/**
* 判断字符串非空
*
* @param str
* @param message
*/
public static void isNotEmpty(String str, String... message) {
if (StrUtil.isBlank(str)) {
execute(message);
}
}
/**
* 判断对象非空
*
* @param obj
* @param message
*/
public static void isNotNull(Object obj, String... message) {
if (obj == null) {
execute(message);
}
}
/**
* 判断结果是否为真
*
* @param isTrue
* @param message
*/
public static void isTrue(boolean isTrue, String... message) {
if (isTrue) {
execute(message);
}
}
/**
* 最终执行方法
*
* @param message
*/
private static void execute(String... message) {
String msg = ApiConstant.ERROR_MESSAGE;
if (message != null && message.length > 0) {
msg = message[0];
}
throw new ParameterException(msg);
}
}
公共返回对象
公共返回对象工具类
ResultInfoUtil.java
package com.imooc.commons.utils;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;
/**
* 公共返回对象工具类
*/
public class ResultInfoUtil {
/**
* 请求出错返回
*
* @param path 请求路径
* @param <T>
* @return
*/
public static <T> ResultInfo<T> buildError(String path) {
ResultInfo<T> resultInfo = build(ApiConstant.ERROR_CODE,
ApiConstant.ERROR_MESSAGE, path, null);
return resultInfo;
}
/**
* 请求出错返回
*
* @param errorCode 错误代码
* @param message 错误提示信息
* @param path 请求路径
* @param <T>
* @return
*/
public static <T> ResultInfo<T> buildError(int errorCode, String message, String path) {
ResultInfo<T> resultInfo = build(errorCode, message, path, null);
return resultInfo;
}
/**
* 请求成功返回
*
* @param path 请求路径
* @param <T>
* @return
*/
public static <T> ResultInfo<T> buildSuccess(String path) {
ResultInfo<T> resultInfo = build(ApiConstant.SUCCESS_CODE,
ApiConstant.SUCCESS_MESSAGE, path, null);
return resultInfo;
}
/**
* 请求成功返回
*
* @param path 请求路径
* @param data 返回数据对象
* @param <T>
* @return
*/
public static <T> ResultInfo<T> buildSuccess(String path, T data) {
ResultInfo<T> resultInfo = build(ApiConstant.SUCCESS_CODE,
ApiConstant.SUCCESS_MESSAGE, path, data);
return resultInfo;
}
/**
* 构建返回对象方法
*
* @param code
* @param message
* @param path
* @param data
* @param <T>
* @return
*/
public static <T> ResultInfo<T> build(Integer code, String message, String path, T data) {
if (code == null) {
code = ApiConstant.SUCCESS_CODE;
}
if (message == null) {
message = ApiConstant.SUCCESS_MESSAGE;
}
ResultInfo resultInfo = new ResultInfo();
resultInfo.setCode(code);
resultInfo.setMessage(message);
resultInfo.setPath(path);
resultInfo.setData(data);
return resultInfo;
}
}
5. Redis保存授权中心令牌
5.1. 创建ms-oauth2-server模块
5.2. 配置文件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>food-social-contact-parent</artifactId>
<groupId>com.imooc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ms-oauth2-server</artifactId>
<dependencies>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- spring web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring data redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- spring cloud security -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<!-- spring cloud oauth2 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- commons 公共项目 -->
<dependency>
<groupId>com.imooc</groupId>
<artifactId>commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 自定义的元数据依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
5.3. Security 配置类
SecurityConfiguration.java
package com.imooc.oauth2.server.config;
import cn.hutool.crypto.digest.DigestUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.annotation.Resource;
/**
* Security 配置类
*/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//注入 Redis 连接工厂
@Resource
private RedisConnectionFactory redisConnectionFactory;
//初始化 RedisTokenStore 用于将 token 存储至 Redis
@Bean
public RedisTokenStore redisTokenStore(){
RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
redisTokenStore.setPrefix("TOKEN:");//设置key的层级前缀,方便查询
return redisTokenStore;
}
//初始化密码编码器,用 MD5 加密密码
@Bean
public PasswordEncoder passwordEncoder(){
return new PasswordEncoder() {
/**
* 加密
* @param rawPassword 原始密码
* @return
*/
@Override
public String encode(CharSequence rawPassword) {
return DigestUtil.md5Hex(rawPassword.toString());
}
/**
* 校验密码
* @param rawPassword 原始密码
* @param encodedPassword 加密密码
* @return
*/
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return DigestUtil.md5Hex(rawPassword.toString()).equals(encodedPassword);
}
};
}
//初始化认证管理对象
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//放行和认证规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
//放行的请求
.antMatchers("/oauth/**","/actuator/**").permitAll()
.and()
.authorizeRequests()
//其他请求必须认证才能访问
.anyRequest().authenticated();
}
}
application.yml
server:
port: 8082 # 端口
spring:
application:
name: ms-oauth2-server # 应用名
# 数据库
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/db_imooc?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
# Redis
redis:
port: 6379
host: 127.0.0.1
timeout: 3000
database: 2
password: 111111
# swagger
swagger:
base-package: com.imooc.oauth2
title: 慕课美食社交食客API接口文档
# Oauth2
client:
oauth2:
client-id: appId # 客户端标识 ID
secret: 123456 # 客户端安全码
# 授权类型
grant_types:
- password
- refresh_token
# token 有效时间,单位秒
token-validity-time: 3600
refresh-token-validity-time: 3600
# 客户端访问范围
scopes:
- api
- all
# 配置 Eureka Server 注册中心
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://localhost:8080/eureka/
# Mybatis
mybatis:
configuration:
map-underscore-to-camel-case: true # 开启驼峰映射
# 指标监控健康检查
management:
endpoints:
web:
exposure:
include: "*" # 暴露的端点
5.5 客户端配置类
ClientOAuth2DataConfiguration.java
package com.imooc.oauth2.server.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 客户端配置类
*/
@Component
@ConfigurationProperties(prefix = "client.oauth2")
@Data
public class ClientOAuth2DataConfiguration {
// 客户端标识 ID
private String clientId;
// 客户端安全码
private String secret;
// 授权类型
private String[] grantTypes;
// token有效期
private int tokenValidityTime;
// refresh-token有效期
private int refreshTokenValidityTime;
// 客户端访问范围
private String[] scopes;
}
5.6. 登录逻辑
5.6.1 公共实体类
BaseModel.java
package com.imooc.commons.model.base;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* 实体对象公共属性
*/
@Getter
@Setter
public class BaseModel implements Serializable {
private Integer id;
private Date createDate;
private Date updateDate;
private int isValid;
}
5.6.2 食客实体类
Diners.java
package com.imooc.commons.model.pojo;
import com.imooc.commons.model.base.BaseModel;
import lombok.Getter;
import lombok.Setter;
/**
* 食客实体类
*/
@Getter
@Setter
public class Diners extends BaseModel {
// 主键
private Integer id;
// 用户名
private String username;
// 昵称
private String nickname;
// 密码
private String password;
// 手机号
private String phone;
// 邮箱
private String email;
// 头像
private String avatarUrl;
// 角色
private String roles;
}
5.6.4 登录校验
UserService.java
package com.imooc.oauth2.server.service;
import com.imooc.commons.model.pojo.Diners;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.oauth2.server.mapper.DinersMapper;
import io.swagger.annotations.Authorization;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
/**
* 登录校验
*/
@Service
public class UserService implements UserDetailsService {
@Resource
private DinersMapper dinersMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AssertUtil.isNotEmpty(username,"请输入用户名");
Diners diners = dinersMapper.selectByAccountInfo(username);
if (diners == null){
throw new UsernameNotFoundException("用户名或密码错误,请重新输入");
}
return new User(username, diners.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(diners.getRoles()));
}
}
5.4 授权服务
AuthorizationServerConfiguration.java
package com.imooc.oauth2.server.config;
import com.imooc.oauth2.server.service.UserService;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.annotation.Resource;
/**
* 授权服务
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
// 客户端配置类
@Resource
private ClientOAuth2DataConfiguration clientOAuth2DataConfiguration;
//密码编码器
@Resource
private PasswordEncoder passwordEncoder;
// 认证管理对象
@Resource
private AuthenticationManager authenticationManager;
// RedisTokenSore
@Resource
private RedisTokenStore redisTokenStore;
// 登录校验
@Resource
private UserService userService;
/**
* 配置令牌端点安全约束
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 允许访问 token 的公钥,默认 /oauth/token_key 是受保护的
security.tokenKeyAccess("permitAll()")
// 允许检查 token 的状态,默认 /oauth/check_token 是受保护的
.checkTokenAccess("permitAll()");
}
/**
* 客户端配置 - 授权模型
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient(clientOAuth2DataConfiguration.getClientId()) // 客户端标识 ID
.secret(passwordEncoder.encode(clientOAuth2DataConfiguration.getSecret())) // 客户端安全码
.authorizedGrantTypes(clientOAuth2DataConfiguration.getGrantTypes()) // 授权类型
.accessTokenValiditySeconds(clientOAuth2DataConfiguration.getTokenValidityTime()) // token 有效期
.refreshTokenValiditySeconds(clientOAuth2DataConfiguration.getRefreshTokenValidityTime()) // 刷新 token 的有效期
.scopes(clientOAuth2DataConfiguration.getScopes()); // 客户端访问范围
}
/**
* 配置授权以及令牌的访问端点和令牌服务
* @param endpoints the endpoints configurer
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 认证器
endpoints.authenticationManager(authenticationManager)
// 具体登录的方法
.userDetailsService(userService)
// token 存储的方式:Redis
.tokenStore(redisTokenStore);
}
}
5.7 启动类
Oauth2ServerApplication.java
package com.imooc.oauth2.server;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.imooc.oauth2.server.mapper")
@SpringBootApplication
public class Oauth2ServerApplication {
public static void main(String[] args) {
SpringApplication.run(Oauth2ServerApplication.class,args);
}
}
5.8 测试
启动RegistryApplication、Oauth2ServerApplication
用postman测试
6. 重构认证授权中心增强令牌返回结果
6.1 重构认证端点返回结果
OAuthController.java
package com.imooc.oauth2.server.controller;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Oauth2 控制器
*/
@RestController
@RequestMapping("oauth")
public class OAuthController {
@Resource
private TokenEndpoint tokenEndpoint;
@Resource
private HttpServletRequest request;
@PostMapping("token")
public ResultInfo postAccessToken(Principal principal, @RequestParam Map<String, String> parameters)
throws HttpRequestMethodNotSupportedException {
return custom(tokenEndpoint.postAccessToken(principal, parameters).getBody());
}
/**
* 自定义 Token 返回对象
* @param accessToken
* @return
*/
private ResultInfo custom(OAuth2AccessToken accessToken) {
DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
Map<String, Object> data = new LinkedHashMap(token.getAdditionalInformation());
data.put("accessToken", token.getValue());
data.put("expireIn", token.getExpiresIn());
data.put("scopes", token.getScope());
if (token.getRefreshToken() != null) {
data.put("refreshToken", token.getRefreshToken().getValue());
}
return ResultInfoUtil.buildSuccess(request.getServletPath(), data);
}
}
6.2 重构登录逻辑,增加令牌返回结果
添加登录认证对象
SignInIdentity.java
package com.imooc.commons.model.domain;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 登录认证对象
*/
@Getter
@Setter
public class SignInIdentity implements UserDetails {
// 主键
private Integer id;
// 用户名
private String username;
// 昵称
private String nickname;
// 密码
private String password;
// 手机号
private String phone;
// 邮箱
private String email;
// 头像
private String avatarUrl;
// 角色
private String roles;
// 是否有效 0=无效 1=有效
private int isValid;
// 角色集合, 不能为空
private List<GrantedAuthority> authorities;
// 获取角色信息
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (StrUtil.isNotBlank(this.roles)){
// 获取数据库中的角色信息
Lists.newArrayList();
this.authorities = Stream.of(this.roles.split(",")).map(role -> {
return new SimpleGrantedAuthority(role);
}).collect(Collectors.toList());
}else{
// 如果角色为空则设置为 ROLE_USER
this.authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
}
return this.authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.isValid != 0 ;
}
}
修改登录认证对象
UserService.java
令牌增强对象,增强返回的结果
AuthorizationServerConfiguration.java
7. 食客服务登录业务完善
7.1 添加依赖
pom.xml(ms-diners)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>food-social-contact-parent</artifactId>
<groupId>com.imooc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ms-diners</artifactId>
<dependencies>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- spring web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- spring data redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- commons 公共项目 -->
<dependency>
<groupId>com.imooc</groupId>
<artifactId>commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 自定义的元数据依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
7.2 配置文件
application.yml(ms-diners)
server:
port: 8081 # 端口
spring:
application:
name: ms-diners # 应用名
# 数据库
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/db_imooc?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
# Redis
redis:
port: 6379
host: 127.0.0.1
timeout: 3000
password: 111111
# swagger
swagger:
base-package: com.imooc.diners
title: 慕课美食社交食客API接口文档
# Oauth2 客户端信息
oauth2:
client:
client-id: appId
secret: 123456
grant_type: password
scope: api
# oauth2 服务地址
service:
name:
ms-oauth-server: http://ms-oauth2-server/
# 配置 Eureka Server 注册中心
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://localhost:8080/eureka/
# Mybatis
mybatis:
configuration:
map-underscore-to-camel-case: true # 开启驼峰映射
7.3 客户端配置类
OAuth2ClientConfiguration.java
package com.imooc.diners.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 客户端配置类
*/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "oauth2.client")
public class OAuth2ClientConfiguration {
private String clientId;
private String secret;
private String grant_type;
private String scope;
}
7.4 Rest配置类
RestTemplateConfiguration.java
package com.imooc.diners.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.client.RestTemplate;
/**
* Rest 配置类
*/
@Configuration
public class RestTemplateConfiguration {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
7.6 实体类
OAuthDinerInfo.java
视图对象实体类
LoginDinerInfo.java
7.5 登录逻辑
最终调用的是授权认证中心,
食客服务业务逻辑层
DinersService.java
package com.imooc.diners.service;
import cn.hutool.core.bean.BeanUtil;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.commons.utils.ResultInfoUtil;
import com.imooc.diners.config.OAuth2ClientConfiguration;
import com.imooc.diners.domain.OAuthDinerInfo;
import com.imooc.diners.vo.LoginDinerInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.LinkedHashMap;
/**
* 食客服务业务逻辑层
*/
@Service
public class DinersService {
@Resource
private RestTemplate restTemplate;
@Value("${service.name.ms-oauth-server}")
private String oauthServiceName;
@Resource
private OAuth2ClientConfiguration oAuth2ClientConfiguration;
/**
* 登录
* @param account 帐号:用户名或手机或邮箱
* @param password 密码
* @param path 请求路径
* @return
*/
public ResultInfo signIn(String account,String password,String path){
// 1.参数校验
AssertUtil.isNotEmpty(account,"请输入登录账号");
AssertUtil.isNotEmpty(password,"请输入登录密码");
// 2.构建请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 3.构建请求体(请求参数)
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("username",account);
body.add("password",password);
body.setAll(BeanUtil.beanToMap(oAuth2ClientConfiguration));
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
// 4.设置 Authorization
restTemplate.getInterceptors()
.add(new BasicAuthenticationInterceptor(oAuth2ClientConfiguration.getClientId(),
oAuth2ClientConfiguration.getSecret()));
// 5.发送请求
ResponseEntity<ResultInfo> result = restTemplate
.postForEntity(oauthServiceName + "oauth/token", entity, ResultInfo.class);
// 6.处理返回结果
AssertUtil.isTrue(result.getStatusCode() != HttpStatus.OK,"登录失败");
ResultInfo resultInfo = result.getBody();
if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE){
// 登录失败
resultInfo.setData(resultInfo.getMessage());
return resultInfo;
}
// 这里的 Data 是一个 LinkedHashMap 转成了域对象 OAuthDinerInfo
OAuthDinerInfo dinerInfo = BeanUtil.fillBeanWithMap(
(LinkedHashMap) resultInfo.getData(),
new OAuthDinerInfo(),false);
// 根据业务需求返回视图对象
LoginDinerInfo loginDinerInfo = new LoginDinerInfo();
loginDinerInfo.setToken(dinerInfo.getAccessToken());
loginDinerInfo.setAvatarUrl(dinerInfo.getAvatarUrl());
loginDinerInfo.setNickname(dinerInfo.getNickname());
return ResultInfoUtil.buildSuccess(path,loginDinerInfo);
}
}
DinersController.java
package com.imooc.diners.controller;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.diners.service.DinersService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* 食客服务控制层
*/
@RestController
@Api(tags = "食客相关接口")
public class DinersController {
@Resource
private DinersService dinersService;
@Resource
private HttpServletRequest request;
@GetMapping("signin")
public ResultInfo signIn(String account, String password){
return dinersService.signIn(account,password, request.getServletPath());
}
}
7.7 启动类
DinersApplication.java
7.8 测试
http://localhost:8081/signin?account=test&password=123456
8. 读取Redis登录用户信息和清空Redis用户信息
1.用户登录信息对象
common 公共项目添加用户登录信息对象
SignInDinerInfo.java
package com.imooc.commons.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@ApiModel(value = "SignInDinerInfo",description = "登录用户信息")
public class SignInDinerInfo {
@ApiModelProperty("主键")
private Integer id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("手机号")
private String phone;
@ApiModelProperty("邮箱")
private String email;
@ApiModelProperty("头像")
private String avatarUrl;
@ApiModelProperty("角色")
private String roles;
}
2.用户中心
UserController.java
package com.imooc.oauth2.server.controller;
import cn.hutool.core.bean.BeanUtil;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.model.domain.SignInIdentity;
import com.imooc.commons.model.vo.SignInDinerInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* 用户中心
*/
@RestController
public class UserController {
@Resource
private HttpServletRequest request;
@GetMapping("user/me")
public ResultInfo getCurrentUser(Authentication authentication){
// 获取登录用户的信息,然后设置
SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();
// 转为前端可用的视图对象
SignInDinerInfo dinerInfo = new SignInDinerInfo();
BeanUtils.copyProperties(signInIdentity,dinerInfo);
return ResultInfoUtil.buildSuccess(request.getServletPath(),dinerInfo);
}
}
3.认证失败处理
MyAutheticationEntryPoint.java
package com.imooc.oauth2.server.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 认证失败处理
*/
@Component
public class MyAutheticationEntryPoint implements AuthenticationEntryPoint {
@Resource
private ObjectMapper objectMapper;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException{
//返回JSON
response.setContentType("application/json;charset=utf-8");
// 状态码 401
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// 写出
PrintWriter out = response.getWriter();
String errorMessage = authException.getMessage();
if (StringUtils.isBlank(errorMessage)){
errorMessage = "登录失败!";
}
ResultInfo result = ResultInfoUtil.buildError(ApiConstant.ERROR_CODE,
errorMessage, request.getRequestURI());
out.write(objectMapper.writeValueAsString(result));
out.flush();
out.close();
}
}
4.资源服务
ResourceServerConfig.java
package com.imooc.oauth2.server.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import javax.annotation.Resource;
/**
* 资源服务
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Resource
private MyAutheticationEntryPoint autheticationEntryPoint;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(autheticationEntryPoint);
}
@Override
public void configure(HttpSecurity http) throws Exception {
// 配置放行的资源
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/user/**");
}
}
5.测试
重新启动Oauth2ServerApplication
http://localhost:8082/user/me?access_token=73853d2d-9a99-4428-98e5-8354408b9255
这种方法也可以
UserController.java
package com.imooc.oauth2.server.controller;
import cn.hutool.core.bean.BeanUtil;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.model.domain.SignInIdentity;
import com.imooc.commons.model.vo.SignInDinerInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* 用户中心
*/
@RestController
public class UserController {
@Resource
private HttpServletRequest request;
@Resource
private RedisTokenStore redisTokenStore;
@GetMapping("user/me")
public ResultInfo getCurrentUser(Authentication authentication){
// 获取登录用户的信息,然后设置
SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();
// 转为前端可用的视图对象
SignInDinerInfo dinerInfo = new SignInDinerInfo();
BeanUtils.copyProperties(signInIdentity,dinerInfo);
return ResultInfoUtil.buildSuccess(request.getServletPath(),dinerInfo);
}
// http://localhost:8082/user/me?access_token=fee64a86-489f-493b-b622-dfa7c23cb501
/**
* 安全退出
* @param access_token
* @param authorization
* @return
*/
@GetMapping("user/logout")
public ResultInfo logout(String access_token,String authorization){
//判断access_token是否为空,为空将authorization 赋值给access_token
if (StringUtils.isBlank(access_token)){
access_token = authorization;
}
//判断authorizaiton 是否为空
if (StringUtils.isBlank(access_token)){
return ResultInfoUtil.buildSuccess(request.getServletPath(),"退出成功");
}
//判断bearer token 是否为空
if (access_token.toLowerCase().contains("bearer ".toLowerCase())){
access_token = access_token.toLowerCase().replace("bearer ","");
}
//清除redis token 信息
OAuth2AccessToken oAuth2AccessToken = redisTokenStore.readAccessToken(access_token);
if (oAuth2AccessToken != null){
redisTokenStore.removeAccessToken(oAuth2AccessToken);
OAuth2RefreshToken refreshToken = oAuth2AccessToken.getRefreshToken();
redisTokenStore.removeRefreshToken(refreshToken);
}
return ResultInfoUtil.buildSuccess(request.getServletPath(),"退出成功");
}
}
再次重启服务 Oauth2ServerApplication
http://localhost:8082/user/logout?access_token=73853d2d-9a99-4428-98e5-8354408b9255
或者这种方式
9. 网关登录校验 - 验证Redis中心令牌是否有效
1.配置文件
pom.xml(ms-gateway)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>food-social-contact-parent</artifactId>
<groupId>com.imooc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ms-gateway</artifactId>
<dependencies>
<!-- spring cloud gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- commons 公共项目 -->
<dependency>
<groupId>com.imooc</groupId>
<artifactId>commons</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 和 webflux 冲突 -->
<exclusions>
<exclusion>
<groupId>com.battcn</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 自定义的元数据依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2.配置文件
application.yml
3.白名单
网关白名单配置类
IgnoreUrlsConfig.java
package com.imooc.gateway.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 网关白名单配置
*/
@Data
@Component
@ConfigurationProperties(prefix = "secure.ignore")
public class IgnoreUrlsConfig {
private List<String> urls;
}
4.Rest配置类
RestTemplateConfiguration.java
package com.imooc.gateway.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfiguration {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
5.登录返回处理
HandleException.java
package com.imooc.gateway.component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.nio.charset.Charset;
@Component
public class HandleException {
@Resource
private ObjectMapper objectMapper;
public Mono<Void> writeError(ServerWebExchange exchange,String error){
ServerHttpResponse response = exchange.getResponse();
ServerHttpRequest request = exchange.getRequest();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResultInfo resultInfo = ResultInfoUtil.buildError(ApiConstant.NO_LOGIN_CODE,
ApiConstant.NO_LOGIN_MESSAGE,
request.getURI().getPath());
String resultInfoJson = null;
DataBuffer buffer = null;
try {
resultInfoJson = objectMapper.writeValueAsString(resultInfo);
buffer = response.bufferFactory()
.wrap(resultInfoJson.getBytes(Charset.forName("UTF-8")));
} catch (JsonProcessingException ex) {
ex.printStackTrace();
}
return response.writeWith(Mono.just(buffer));
}
}
6.全局过滤器
AuthGlobalFilter.java
package com.imooc.gateway.filter;
import com.imooc.gateway.component.HandleException;
import com.imooc.gateway.config.IgnoreUrlsConfig;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
/**
* 网关全局过滤器
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
//网关白名单配置
@Resource
private IgnoreUrlsConfig ignoreUrlsConfig;
@Resource
private RestTemplate restTemplate;
//集中返回处理
@Resource
private HandleException handleException;
/**
* 身份校验处理
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.判断当前的请求是否在白名单中
AntPathMatcher pathMatcher = new AntPathMatcher();
boolean flag = false;
String path = exchange.getRequest().getURI().getPath();
for (String url : ignoreUrlsConfig.getUrls()) {
if (pathMatcher.match(url,path)){
flag = true;
break;
}
}
//2.白名单放行
if (flag){
return chain.filter(exchange);
}
//3.获取 access_token
String access_token = exchange.getRequest().getQueryParams().getFirst("access_token");
//4.判断 access_token 是否为空
if (StringUtils.isBlank(access_token)){
return handleException.writeError(exchange,"请登录");
}
//5.校验 token 是否有效
//ms-oauth2-server 注册中心服务名
String checkTokenUrl = "http://ms-oauth2-server/oauth/check_token?token=".concat(access_token);
try {
//6.发送远程请求,验证token
ResponseEntity<String> entity = restTemplate.getForEntity(checkTokenUrl, String.class);
//token 无效的业务逻辑处理
if (entity.getStatusCode() != HttpStatus.OK){
return handleException.writeError(exchange,
"Token was not recognised,token: ".concat(access_token));
}
if (StringUtils.isBlank(entity.getBody())){
return handleException.writeError(exchange,
"This token is invalid: ".concat(access_token));
}
}catch (Exception e){
return handleException.writeError(exchange,
"Token was not recognised,token: ".concat(access_token));
}
//7.放行
return chain.filter(exchange);
}
/**
* 网关过滤器的排序,数字越小优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
7.测试
重新启动GatewayApplication
localhost:80相当于网关
http://localhost/diners/signin?account=test&password=123456
http://localhost/auth/user/me?access_token=653cd330-951a-4909-a6e9-4b3be46d2de0
http://localhost/auth/oauth/check_token?token=653cd330-951a-4909-a6e9-4b3be46d2de0
http://localhost/auth/user/logout?access_token=653cd330-951a-4909-a6e9-4b3be46d2de0
10. 认证授权中心业务时序图总结
客户端请求资源(未登录)执行流程
请求资源->判断是否有令牌->没有令牌请登录
http://localhost/diners/hello?name=redis
客户发起登录请求->白名单放行->校验账号密码->账号密码不合法
客户发起登录请求->白名单放行->校验账号密码->登录->登录校验->生成令牌并存储至Redis->返回令牌信息
客户端请求资源(已登录)执行流程
请求资源->判断是否有令牌->校验令牌->令牌有效->放行,继续请求资源->业务处理->返回
http://localhost/auth/user/me?access_token=83932db5-34a5-4f83-b1c9-9d37e61eb56f
http://localhost/diners/hello?name=redis&access_token=83932db5-34a5-4f83-b1c9-9d37e61eb56f
实现单点登录
http://localhost/auth/user/logout?access_token=5fb694e8-58b0-48e1-a20f-2340d485a47a
11. Redis保存手机短信验证码
11.1 Redis 配置类
RedisTemplateConfiguration.java
package com.imooc.diners.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisTemplateConfiguration {
/**
* redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置key和value的序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
11.2 公共枚举类
RedisKeyConstant.java
package com.imooc.commons.constant;
import lombok.Getter;
@Getter
public enum RedisKeyConstant {
verify_code("verify_code:", "验证码");
private String key;
private String desc;
RedisKeyConstant(String key,String desc){
this.key = key;
this.desc = desc;
}
}
11.3 Service
SendVerifyCodeService.java
package com.imooc.diners.service;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.imooc.commons.constant.RedisKeyConstant;
import com.imooc.commons.utils.AssertUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* 发送验证码业务逻辑层
*/
@Service
public class SendVerifyCodeService {
@Resource
private RedisTemplate<String,String> redisTemplate;
/**
* 发送验证码
* @param phone
*/
public void send(String phone){
//1.检查非空
AssertUtil.isNotEmpty(phone,"手机号不能为空");
//2.根据手机号查询是否已生成验证码,已生成直接返回
if (!checkCodeIsExpired(phone)){
return;
}
//3.生成6位验证码
String code = RandomUtil.randomNumbers(6);
//4.调用短信服务发送短信
//5.发送成功,将code 保存至 Redis ,失效时间 60s
String key = RedisKeyConstant.verify_code.getKey() + phone;
redisTemplate.opsForValue().set(key,code,60, TimeUnit.SECONDS);
}
/**
* 根据手机号查询是否已生成验证码
* @param phone
* @return
*/
private boolean checkCodeIsExpired(String phone){
String key = RedisKeyConstant.verify_code.getKey() + phone;
String code = redisTemplate.opsForValue().get(key);
return StrUtil.isBlank(code) ? true : false;
}
}
11.4 Controller
SendVerifyCodeController.java
package com.imooc.diners.controller;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import com.imooc.diners.service.SendVerifyCodeService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* 发送验证码控制层
*/
@RestController
public class SendVerifyCodeController {
@Resource
private SendVerifyCodeService sendVerifyCodeService;
@Resource
private HttpServletRequest request;
/**
* 发送验证码
* @param phone
* @return
*/
@GetMapping("send")
public ResultInfo send(String phone){
sendVerifyCodeService.send(phone);
return ResultInfoUtil.buildSuccess("发送成功",request.getServletPath());
}
}
11.5 网关配置
application.yml
11.6 测试
重新启动服务
http://localhost/diners/send?phone=12311112222
12. 用户注册功能
12.1 是否已注册
Mapper
DinersMapper
package com.imooc.diners.mapper;
import com.imooc.commons.model.pojo.Diners;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* 食客Mapper
*/
public interface DinersMapper {
//根据手机号查询食客信息
@Select("select id, username, phone, email, is_vaild " +
"from t_diners where phone = #{phone}")
Diners selectByPhone(@Param("phone") String phone);
}
DinersService.java
DinersController
application.yml
重新启动DinersApplication、GatewayApplication服务
数据库有的
http://localhost/diners/checkPhone?phone=12311112222
数据库没有的
http://localhost/diners/checkPhone?phone=12311112223
要写一个全局异常处理,给客户端返回一个友好的提示
12.2 全局异常处理
handler
GlobalExceptionHandler.java
package com.imooc.diners.handler;
import com.imooc.commons.exception.ParameterException;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.utils.ResultInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@RestControllerAdvice //将输出的内容写入到 ResponseBody 中
@Slf4j //开启日志
public class GlobalExceptionHandler {
@Resource
private HttpServletRequest request;
@ExceptionHandler(ParameterException.class)
public ResultInfo<Map<String,String>> handlerParameterException(ParameterException ex){
String path = request.getRequestURI();
ResultInfo<Map<String,String>> resultInfo =
ResultInfoUtil.buildError(ex.getErrorCode(), ex.getMessage(), path);
return resultInfo;
}
@ExceptionHandler(Exception.class)
public ResultInfo<Map<String,String>> handlerException(Exception ex){
log.info("未知异常:{}",ex);
String path = request.getRequestURI();
ResultInfo<Map<String,String>> resultInfo = ResultInfoUtil.buildError(path);
return resultInfo;
}
}
重新启动DinersApplication服务
http://localhost/diners/checkPhone?phone=12311112223
返回正常
12.3 完成注册
12.3.1 DTO
DinersDTO.java
package com.imooc.commons.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
@ApiModel(description = "注册用户信息")
public class DinersDTO implements Serializable {
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
@ApiModelProperty("手机号")
private String phone;
@ApiModelProperty("验证码")
private String verifyCode;
}
12.3.2 Mapper
DinersMapper.java
package com.imooc.diners.mapper;
import com.imooc.commons.model.dto.DinersDTO;
import com.imooc.commons.model.pojo.Diners;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* 食客Mapper
*/
public interface DinersMapper {
//根据手机号查询食客信息
@Select("select id, username, phone, email, is_valid " +
"from t_diners where phone = #{phone}")
Diners selectByPhone(@Param("phone") String phone);
//根据用户名查询食客信息
@Select("select id, username, phone, email, is_valid " +
"from t_diners where username = #{username}")
Diners selectByUsername(@Param("username") String username);
//新增食客信息
@Insert("insert into" +
" t_diners (username, password, phone, roles, is_valid, create_date,update_date) " +
"values (#{username}, #{password}, #{phone}, \"ROLE_USER\", 1, now(),now())")
int save(DinersDTO dinersDTO);
}
12.3.3 Service
SendVerifyCodeService.java
DinersService.java
@Resource
private SendVerifyCodeService sendVerifyCodeService;
/**
* 用户注册
* @param dinersDTO
* @param path
* @return
*/
public ResultInfo register(DinersDTO dinersDTO,String path){
//1.参数非空校验
String username = dinersDTO.getUsername();
AssertUtil.isNotEmpty(username,"请输入用户名");
String password = dinersDTO.getPassword();
AssertUtil.isNotEmpty(password,"请输入密码");
String phone = dinersDTO.getPhone();
AssertUtil.isNotEmpty(phone,"请输入手机号");
String verifyCode = dinersDTO.getVerifyCode();
AssertUtil.isNotEmpty(verifyCode,"请输入验证码");
//2.验证码一致性校验(验证码是否已过期)
String code = sendVerifyCodeService.getCodeByPhone(phone);
// 验证码是否过期
AssertUtil.isNotEmpty(code,"验证码已过期,请重新发送");
//验证码一致性校验
AssertUtil.isTrue(!dinersDTO.getVerifyCode().equals(code),"验证码不一致,请重新输入");
//3.验证用户名是否已注册
Diners diners = dinersMapper.selectByUsername(username.trim());
AssertUtil.isTrue(diners !=null,"用户名已存在,请重新输入");
//4.注册
// 密码加密
dinersDTO.setPassword(DigestUtil.md5Hex(password.trim()));
dinersMapper.save(dinersDTO);
// 自动登录
return signIn(username.trim(),password.trim(),path);
}
12.3.4 Controller
DinersController.java
12.3.5 网关配置
application.yml(ms-gateway)
12.3.6 测试
1.检测是否注册
http://localhost/diners/checkPhone?phone=12311113333
2.发送验证码
3.拿验证码
4.登录(60s会过期,再发一遍就可以了)
下面是一些其它错误展示:
1.请输入用户名(或者密码、手机号、验证码)
2.验证码已过期,请重新发送
3.验证码不一致,请重新输入