编写u-center模块功能
# 编写u-center模块功能
- 通过代码生成器生成各种类
# 通过代码生成器生成各种类
相关的依赖
<!--数据库相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!-- RESTful APIs swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
代码生成器的代码
public class GeneratorCodes {
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir("D:\\code\\MoYuJun\\u-center\\src\\main\\java");
gc.setAuthor("sob");
gc.setOpen(false);//生成以后是否打开文件夹
gc.setSwagger2(true);//实体属性 Swagger2 注解
gc.setFileOverride(true);//true表示覆盖原来的
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://192.168.220.128:3306/mo_yu_ucenter?characterEncoding=utf-8&useSSL=false&useUnicode=true");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("uc");
pc.setParent("net.sunofbeach");
pc.setController("api");
pc.setMapper("mapper");
pc.setService("service");
pc.setEntity("pojo");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
//多个表的时候,写多个就可以
strategy.setInclude("uc_app","uc_black_list","uc_fans","uc_images","uc_login_record","uc_register_info","uc_settings","uc_statistics","uc_token","uc_user","uc_user_info");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.execute();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
同学们要修改上面的配置,如果不放心,可以先测试一个数据表,而不是全部。
# 编写统一返回结果R
在common模块下,我们创建以下类
package net.sunofbeach.common.response;
import lombok.Data;
/**
* 返回的类
* 数据有:
* - 是否成功:success [true/false] 类型 boolean
* - 状态码:code [20000/40000] 类型 int
* - 消息:msg 对code的说明,比如说:操作成功;操作失败;登录成功... 类型 字符串类型
* - 返回的数据:data 类型object
*/
@Data
public class R {
public static final int CODE_SUCCESS = 20000;
public static final int CODE_FAILED = 40000;
//是否成功
private boolean success;
//状态码
private int code;
//描述
private String msg;
//数据
private Object data;
//提供一些静态的方法,可以快速地创建返回对象
public static R SUCCESS(String msg) {
R r = new R();
r.code = CODE_SUCCESS;
r.msg = msg;
r.success = true;
return r;
}
public static R SUCCESS(String msg, Object data) {
R success = SUCCESS(msg);
success.data = data;
return success;
}
public static R FAILED(String msg) {
R r = new R();
r.code = CODE_FAILED;
r.msg = msg;
r.success = false;
return r;
}
public static R FAILED(String msg, Object data) {
R success = FAILED(msg);
success.data = data;
return success;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
我们其他模块都依赖于此模块,都使用到返回结果,所以我们写到公共模块里。
测试:
/**
* <p>
* 前端控制器
* </p>
*
* @author sob
* @since 2021-10-20
*/
@RestController
@RequestMapping("/uc/user")
public class UserController {
@GetMapping("/test")
public R testResponse() {
return R.SUCCESS("请求成功.");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
请求地址:
localhost:40100/uc/user/test
返回结果
{
"success": true,
"code": 20000,
"msg": "请求成功.",
"data": null
}
2
3
4
5
6
# 接口分类
我们的接口主要是分两部分,一部分是给门户使用的,一部分是管理中心使用的。
所以我们可以对我们的API进行分包管理一下,比说如:
portal和admin
# 接口重构
我们目前api里的接口都是根据数据表生成的,不一定完全符合我们的需求。所以我们有必要对此进行一个修改,根据我们面的功能进行修改。
- portal
- UserController,user,用户的基本功能,比如说登录,注册
- 登录 sing_in(post)
- 退出登录 sign_out (get)
- 注册 sig_up(post)
- CheckController,check,帮助用户类实现基本功能的工具接口,比如说验证码,邮箱是否有注册检查,用户名是否有注册检查之类的
- 获取验证码,verify_code(get)
- 获取邮箱验证码,email_code (get)
- 检查邮箱是否有注册 email(get)
- 检查手机号是否有注册 phone (get)
- 检查用户名是否有注册 user_name (get)
- 检查token是否有效 token (get)
- UserInfoController,info 用户信息接口
- 获取用户信息 (get)
- 更新用户信息(put)
- ImageController ,image 图片上传接口
- 头像上传,avatar(post)
- app的Logo上传 app_logo(post)
- 用户封面上传 user_cover(post)
- AppController,app,第三方开发者使用统一用户中心时,需要创建应用,不支持删除
- 创建应用(post)
- 修改应用信息(只允许修改logo和名称)(put)
- 获取应用列表 list (get)
- 获取某个应用的信息(get)
- FansController,fans,粉丝接口
- 关注 follow (post)
- 取消关注 unfollow (put)
- 获取关注列表 follow_list(get)
- 获取粉丝列表 fans_list(get)
- 加入黑名单 black (post)
- 取消黑名单 white (put)
- 获取黑名单列表 black_list(get)
- UserController,user,用户的基本功能,比如说登录,注册
- admin
- UserController,user 用户控制类,获取用户列表,重置用户密码,禁止用户等
- 获取用户列表 list(get)
- 重置用户密码 reset_pwd(put)
- 删除用户 (delete)
- 禁止用户 disable(put)
- 取消禁止 enable(put)
- InfoController,info 统一用户中心的信息接口,比如说当前的在线人数,用户总数量,应用总数
- 当前在线人数 online_count(get)
- 用户中数 user_count(get)
- app总数 app_count(get)
- =====================
- 今天注册人数 today_reg_count(get)
- 本周注册人数 toweek_reg_count(get)
- 本月注册人数 tomonth_reg_count(get)
- StatisticsController,statistics 统计接口
- //TODO:
- SettingsController,settings 设置相关的接口
- 注册方式 reg_type (put)
- 获取注册方式 reg_type(get)
- 邮件服务器 email (put)
- 获取邮件服务器信息 email(get)
- 敏感词 taboo (put)
- 获取敏感词列表 taboo(get)
- 管理员账号 account (put)
- UserController,user 用户控制类,获取用户列表,重置用户密码,禁止用户等
# 用户注册流程
- 获取图灵验证 captcha (get)
- 检查当前手机号码是否有被注册 phone_num (get)
- 获取手机验证码 phone_verify_code (get)
- 检查当前邮箱是否有被注册 email (get)
- 获取邮箱验证码 email_verify_code (get)
- 检查昵称是否有被使用 nickname (get)
- 提交注册信息 sign_up (post)
# redis配置
首先是redis的安装,如果没有学习博客系统同学,可以参考一下这篇文章,或者去看视频。
- redis的安装文章:
- 本地版本: 安装redis-6.2.5(linux版) (opens new window)
- 安装redis-6.2.5(单机版) (opens new window)
- docker版本:阳光沙滩博客系统-Redis (opens new window)
- docker搭建redis以及项目配置 (opens new window)
添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
1
2
3
4配置
spring: redis: database: 1 #我的0号库使用在其他项目上,我用1号库 host: 192.168.220.128 port: 6379 password: 123456
1
2
3
4
5
6工具类
package net.sunofbeach.common.utils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @Component public class RedisUtil { @Resource private RedisTemplate redisTemplate; public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } /** * 指定缓存失效时间 * * @param key 键 * @param time 时间(秒) * @return */ private boolean expire(String key, long time) { try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据key 获取过期时间 * * @param key 键 不能为null * @return 时间(秒) 返回0代表为永久有效 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 判断key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存 * * @param key 可以传一个值 或多个 */ @SuppressWarnings("unchecked") public void del(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { redisTemplate.delete(key[0]); } else { redisTemplate.delete(CollectionUtils.arrayToList(key)); } } } /** * 普通缓存获取 * * @param key 键 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 普通缓存放入 * * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 false 失败 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 递增 * * @param key 键 * @param delta 要增加几(大于0) * @return */ public long incr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递增因子必须大于0"); } return redisTemplate.opsForValue().increment(key, delta); } /** * 递减 * * @param key 键 * @param delta 要减少几(小于0) * @return */ public long decr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递减因子必须大于0"); } return redisTemplate.opsForValue().increment(key, -delta); } /** * HashGet * * @param key 键 不能为null * @param item 项 不能为null * @return 值 */ public Object hget(String key, String item) { return redisTemplate.opsForHash().get(key, item); } /** * 获取hashKey对应的所有键值 * * @param key 键 * @return 对应的多个键值 */ public Map<Object, Object> hmget(String key) { return redisTemplate.opsForHash().entries(key); } /** * HashSet * * @param key 键 * @param map 对应多个键值 * @return true 成功 false 失败 */ public boolean hmset(String key, Map<String, Object> map) { try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * HashSet 并设置时间 * * @param key 键 * @param map 对应多个键值 * @param time 时间(秒) * @return true成功 false失败 */ private boolean hmset(String key, Map<String, Object> map, long time) { try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * * @param key 键 * @param item 项 * @param value 值 * @return true 成功 false失败 */ public boolean hset(String key, String item, Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * * @param key 键 * @param item 项 * @param value 值 * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 * @return true 成功 false失败 */ public boolean hset(String key, String item, Object value, long time) { try { redisTemplate.opsForHash().put(key, item, value); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除hash表中的值 * * @param key 键 不能为null * @param item 项 可以使多个 不能为null */ public void hdel(String key, Object... item) { redisTemplate.opsForHash().delete(key, item); } /** * 判断hash表中是否有该项的值 * * @param key 键 不能为null * @param item 项 不能为null * @return true 存在 false不存在 */ public boolean hHasKey(String key, String item) { return redisTemplate.opsForHash().hasKey(key, item); } /** * hash递增 如果不存在,就会创建一个 并把新增后的值返回 * * @param key 键 * @param item 项 * @param by 要增加几(大于0) * @return */ public double hincr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, by); } /** * hash递减 * * @param key 键 * @param item 项 * @param by 要减少记(小于0) * @return */ public double hdecr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, -by); } /** * 根据key获取Set中的所有值 * * @param key 键 * @return */ public Set<Object> sGet(String key) { try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 根据value从一个set中查询,是否存在 * * @param key 键 * @param value 值 * @return true 存在 false不存在 */ public boolean sHasKey(String key, Object value) { try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将数据放入set缓存 * * @param key 键 * @param values 值 可以是多个 * @return 成功个数 */ public long sSet(String key, Object... values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 将set数据放入缓存 * * @param key 键 * @param time 时间(秒) * @param values 值 可以是多个 * @return 成功个数 */ public long sSetAndTime(String key, long time, Object... values) { try { Long count = redisTemplate.opsForSet().add(key, values); if (time > 0) expire(key, time); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 获取set缓存的长度 * * @param key 键 * @return */ public long sGetSetSize(String key) { try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 移除值为value的 * * @param key 键 * @param values 值 可以是多个 * @return 移除的个数 */ public long setRemove(String key, Object... values) { try { Long count = redisTemplate.opsForSet().remove(key, values); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 获取list缓存的内容 * * @param key 键 * @param start 开始 * @param end 结束 0 到 -1代表所有值 * @return */ public List<Object> lGet(String key, long start, long end) { try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 获取list缓存的长度 * * @param key 键 * @return */ public long lGetListSize(String key) { try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 通过索引 获取list中的值 * * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 * @return */ public Object lGetIndex(String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, Object value, long time) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, List<Object> value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, List<Object> value, long time) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据索引修改list中的某条数据 * * @param key 键 * @param index 索引 * @param value 值 * @return */ public boolean lUpdateIndex(String key, long index, Object value) { try { redisTemplate.opsForList().set(key, index, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 移除N个值为value * * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ public long lRemove(String key, long count, Object value) { try { Long remove = redisTemplate.opsForList().remove(key, count, value); return remove; } catch (Exception e) { e.printStackTrace(); return 0; } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559配置
@Configuration public class RedisConfiguration { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16测试
随便往redis里保存一些东西,读写一下就可以知道配置成功没了
# 验证码
推荐使用的开源框架:
https://github.com/anji-plus/captcha
# 创建用户中心的前端项目
我们前端使用vue.js来写
https://cn.vuejs.org/v2/guide/
用户中心的前端主要包括:
- 注册
- 登录
- 找回密码
# 集成验证码功能
- 后端,添加依赖
<!--验证码-->
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>spring-boot-starter-captcha</artifactId>
<version>1.3.0</version>
</dependency>
2
3
4
5
6
添加完以后,import一下,然后就可以了,相关的接口在jar包的controller里有的。
- 前端,前端复制组件,还有assert目录下的资源文件。
然后引入组件,注册组件
import Verify from './components/verifition/Verify.vue'
export default {
name: 'App',
components: {
Verify
},
methods: {
login(params) {
console.log(params);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
使用组件
<Verify
ref="verify"
:captcha-type="'blockPuzzle'"
:img-size="{width:'400px',height:'200px'}"
@success="login"/>
2
3
4
5
解决访问后台的跨域问题
@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws ServletException, IOException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setContentType("application/json; charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Methods", "POST, GET,PUT, OPTIONS, DELETE");//http请求方式
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, token");
filterChain.doFilter(request, response);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
到这里我们就集成完了,接下来,我们稍微写一下前端的页面
# 前端页面
- 注册
- 登录
- 找回密码
安装vue-router
https://router.vuejs.org/installation.html
npm install vue-router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
2
3
4
5
6
编写和注册路由
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false;
import VueRouter from 'vue-router'
Vue.use(VueRouter);
const index = () => import('@/page/home/index');
const login = () => import('@/page/login/index');
const forgot = () => import('@/page/forgot/index');
const register = () => import('@/page/register/index');
const routes = [
{
path: '/index',
name: 'index',
component: index
},
{
path: '/login',
name: 'login',
component: login
},
{
path: '/forgot',
name: 'forgot',
component: forgot
},
{
path: '/register',
name: 'register',
component: register
},
{
path: '',
redirect: '/index'
}
];
const router = new VueRouter({
routes // short for `routes: routes`
});
new Vue({
router,
render: h => h(App),
}).$mount('#app');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 前端引入ElementUI
https://element.eleme.cn/#/zh-CN/component/installation
npm i element-ui -S
npm install element-ui --save
2
3
# 编写注册页面
<template>
<div class="register-page">
<div class="title">注册</div>
<div class="register-form-part">
<el-form label-width="80px" size="medium">
<el-form-item label="邮箱" :required="true">
<el-input v-model="userVo.email"></el-input>
</el-form-item>
<el-form-item label="昵称" :required="true">
<el-input v-model="userVo.name"></el-input>
</el-form-item>
<el-form-item label="密码" :required="true">
<el-input v-model="userVo.password"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="register" type="primary">注 册</el-button>
<span style="margin-left: 20px; color: #7e8c8d">
已注册? 直接登录
</span>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userVo: {
email: '',
name: '',
password: ''
}
}
},
methods: {
register() {
}
}
}
</script>
<style>
.register-form-part .el-input {
width: 400px;
}
.register-form-part {
margin-top: 20px;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 注册功能编写
动作:
用户输入邮箱,获取验证码--->显示图灵验证码(手动校验,不会删除验证)
提交数据:邮箱,图灵验证码内容
填写邮箱验证码和用户名还有密码(MD5)
进行注册,提交邮箱,邮箱验证码,用户名,密码,图灵验证码结果(可以使用官方的API校验,验证成功会删除验证码)
手动校验图灵验证码的代码
//手动判断验证码是否正确
String codeKey = String.format(REDIS_SECOND_CAPTCHA_KEY, captchaVO.getCaptchaVerification());
CaptchaCacheService local = CaptchaServiceFactory.getCache("local");
boolean exists = local.exists(codeKey);
if (exists) {
//存在
//就可发验证
} else {
//不存在
}
2
3
4
5
6
7
8
9
10
# 邮箱发送功能
邮箱验证码发送,跟业务类型有关系。
- 注册时获取邮箱验证码(邮箱地址必须未注册的)
- 找回密码的时候发送邮箱验证码(邮箱地址是必须已经注册的)
- 更邮箱地址的时候,新的邮箱地址必须是未注册的
邮箱地址校验正则表达式
String regEx = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(邮箱地址);
2
3
在博客系统里我们已经做过这样的功能
https://www.sunofbeach.net/a/1273944367953903616
异步发送邮件
https://www.sunofbeach.net/a/1274207577059655680
邮箱SMTP的配置
找到SMTP的设置
异步发送邮件
https://www.sunofbeach.net/a/875112677616123904
# 前端网络模块引入
axios
https://cn.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html
安装axios
https://github.com/axios/axios#installing
npm install axios --save
配置
import axios from 'axios'
axios.defaults.withCredentials = true;
axios.defaults.timeout = 1000000;
axios.defaults.headers.post['Content-Type'] = 'application/x-www=form-urlencoded'
export default {
// get请求
requestGet(url, params = {}) {
return new Promise((resolve, reject) => {
axios.get(url, params).then(res => {
console.log(res);
resolve(res.data)
}).catch(error => {
reject(error)
});
})
},
// post请求
requestPost(url, params = {}) {
return new Promise((resolve, reject) => {
axios.post(url, params).then(res => {
resolve(res.data)
}).catch(error => {
reject(error)
})
})
},
// delete请求
requestDelete(url, params = {}) {
return new Promise((resolve, reject) => {
axios.delete(url, params).then(res => {
resolve(res.data)
}).catch(error => {
reject(error)
})
})
},
// put请求
requestPut(url, params = {}) {
return new Promise((resolve, reject) => {
axios.put(url, params).then(res => {
resolve(res.data)
}).catch(error => {
reject(error)
})
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
api.js
import http from './http';
const base_url = 'http://localhost:40100';
export const SUCCESS_CODE = 20000;
export const sendMailCode = (mailAddress) => {
return http.requestGet(base_url + "/uc/ex/re/mail-code?mail=" + mailAddress)
};
export const registerAccount = (mailCode, userVo) => {
return http.requestPost(base_url + "/uc/user?mailCode=" + mailCode, userVo);
};
2
3
4
5
6
7
8
9
10
11
12
13
# 注册功能实现
数据校验
- 邮箱验证码是否正确
- 邮箱地址是否有注册
- 用户名是否已经注册
默认头像
https://cdn.sunofbeaches.com/images/default_avatar.png
1密码加密
数据入库
返回注册结果
密码加密,添加SpringSecurity的依赖
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2
3
4
5
放行配置
@Configuration
@EnableWebSecurity
public class WebSpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//所有都放行
http.authorizeRequests()
.antMatchers("/**").permitAll()
.and().csrf().disable();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
密码处理的相关代码
@Autowired
protected BCryptPasswordEncoder bCryptPasswordEncoder;
//加密
bCryptPasswordEncoder.encode(sobUser.getPassword())
//校验,输入的原密码和数据库的密文进行对比
bCryptPasswordEncoder.matches(password, userFromDb.getPassword());
2
3
4
5
6
# 数据自动填充
@Component
public class MBMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
this.strictInsertFill(metaObject, "deleted", String.class, "0");
System.out.println("insertFill...");
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
System.out.println("updateFill...");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
逻辑删除配置项
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 输出Sql语句执行日志
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
2
3
4
5
6
7
8
# 登录功能
- 来源判断
- 作用,比如说统计,比如说小尾巴
- 通常的做法携带一个AppID,不同的端,用不同的AppID
- 图灵验证码校验
- 数据校验(用户名、密码)
- 创建token
- 设置cookie
cookie设置的工具类
@Slf4j
public class CookieUtils {
//1年
public static final int default_age = 60 * 60 * 24 * 365;
public static final String domain = "sunofbeaches.com";
/**
* 设置cookie值
*
* @param response
* @param key
* @param value
*/
public static void setUpCookie(HttpServletResponse response, String key, String value) {
setUpCookie(response, key, value, default_age);
}
public static void setUpCookie(HttpServletResponse response, String key, String value, int age) {
Cookie cookie = new Cookie(key, value);
cookie.setPath("/");
/**
* 域名:如果是单点登录,就设置顶级域名
* sunofbeach.net
* https://www.sunofbeach.net/
* https://mp.sunofbeach.net/
*/
//cookie.setDomain(domain);
cookie.setMaxAge(age);
response.addCookie(cookie);
}
/**
* 删除cookie
*
* @param response
* @param key
*/
public static void deleteCookie(HttpServletResponse response, String key) {
setUpCookie(response, key, null, 0);
}
/**
* 获取cookie
*
* @param request
* @param key
* @return
*/
public static String getCookie(HttpServletRequest request, String key) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
log.info("cookies is nuLL...");
return null;
}
for (Cookie cookie : cookies) {
if (key.equals(cookie.getName())) {
return cookie.getValue();
}
}
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
JWT
依赖包:
<!--JJWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
2
3
4
5
6
JwtUtil.java
public class JwtUtil {
//盐值,使用用户特定的盐值
private static String key = "";
//单位是毫秒
private static long ttl = Constants.TimeValueInMillions.HOUR_2;//2个小时
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* @param claims 载荷内容
* @param ttl 有效时长
* @return
*/
public static String createToken(Map<String, Object> claims, long ttl) {
JwtUtil.ttl = ttl;
return createToken(claims);
}
public static String createRefreshToken(String userId, long ttl) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(userId)
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, key);
if (ttl > 0) {
builder.setExpiration(new Date(nowMillis + ttl));
}
return builder.compact();
}
/**
* @param claims 载荷
* @return token
*/
public static String createToken(Map<String, Object> claims) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder()
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, key);
if (claims != null) {
builder.setClaims(claims);
}
if (ttl > 0) {
builder.setExpiration(new Date(nowMillis + ttl));
}
return builder.compact();
}
public static Claims parseJWT(String jwtStr) {
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 解析Token
/**
* 解析Token
*
* @return
*/
@Override
public R checkToken() {
//先是拿到tokenKey
String tokenKey = CookieUtils.getCookie(getRequest(), Constants.User.KEY_SOB_TOKEN);
if (TextUtils.isEmpty(tokenKey)) {
return R.NOT_LOGIN();
}
//先去redis去拿token,有可能没有,有超过2个小时没有活跃了,所以没有了
String token = (String) redisUtil.get(Constants.User.KEY_TOKEN + tokenKey);
String salt = (String) redisUtil.get(Constants.User.KEY_SALT + tokenKey);
if (TextUtils.isEmpty(salt)) {
return R.NOT_LOGIN();
}
if (!TextUtils.isEmpty(token)) {
//有就解析token
try {
Claims claims = JwtUtil.parseJWT(token, salt);
UserVo userVo = ClaimsUtil.claim2User(claims);
return R.SUCCESS("已登录.").setData(userVo);
} catch (Exception e) {
e.printStackTrace();
//走检查refreshToken的路
return checkRefreshToken(tokenKey, salt);
}
} else {
//走检查refreshToken的路
return checkRefreshToken(tokenKey, salt);
}
}
/**
* 从数据库上找到refreshToken,如果没有,就真的没有登录了
* 如果有,判断是否有过期
*
* @param tokenKey
* @return
*/
private R checkRefreshToken(String tokenKey, String salt) {
QueryWrapper<RefreshToken> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("token_key", tokenKey);
queryWrapper.select("refresh_token", "user_id");
RefreshToken refreshToken = tokenService.getOne(queryWrapper);
if (refreshToken != null) {
try {
JwtUtil.parseJWT(refreshToken.getRefreshToken(), salt);
//通过用户ID查到用户的内容,再创建token
String userId = refreshToken.getUserId();
//先删除原来的数据
redisUtil.del(Constants.User.KEY_TOKEN + tokenKey);
QueryWrapper<RefreshToken> refreshTokenQueryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId);
tokenService.remove(refreshTokenQueryWrapper);
//创建新的token
User user = getById(userId);
UserVo userVo = new UserVo();
userVo.setId(userId);
userVo.setUserName(user.getUserName());
userVo.setAvatar(userVo.getAvatar());
userVo.setSex(userVo.getSex());
userVo.setStatus(userVo.getStatus());
userVo.setSalt(userVo.getSalt());
createToken(userVo);
return R.SUCCESS("已登录").setData(userVo);
} catch (Exception e) {
e.printStackTrace();
//过期了就是走未登录
}
}
return R.NOT_LOGIN();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 重置密码
通过邮箱找回密码,首先填写邮箱,发送邮箱验证码,填写邮箱码,验证通过以后,再设置新的密码。
# 定义用户管理的接口
- 用户列表(有条件,搜索)
- 用户禁止
- 重置密码
@RestController
public class UserAdminController {
@GetMapping("/uc/admin/user/list")
public R listUser(@RequestParam(value = "phone", required = false) String phone,
@RequestParam(value = "email", required = false) String email,
@RequestParam(value = "name", required = false) String userName,
@RequestParam(value = "id", required = false) String userId,
@RequestParam(value = "state", required = false) String state) {
return null;
}
@PutMapping("/uc/admin/user/disable/{userId}")
public R disableUser(@PathVariable("userId") String userId) {
return null;
}
@PutMapping("/uc/admin/user/reset/{userId}")
public R resetPassword(@PathVariable("userId") String userId,
@RequestBody RegisterVo registerVo) {
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 实现用户列表的获取
/**
* 获取用户列表
*
* @param page 页码 ,从1开始
* @param phone 手机号
* @param email 邮箱地址
* @param userName 用户名
* @param userId 用户ID
* @param status 状态
* @return
*/
@Override
public R listUser(int page, String phone, String email,
String userName, String userId,
String status) {
//检查页码,不过小
page = checkPage(page);
int size = Constants.DEFAULT_SIZE;
int offset = (page - 1) * size;
//查询数据
List<UserAdminVo> userAdminVos = this.baseMapper.listUser(size, offset, page, phone, email, userName, userId, status);
//有总数量
PageVo<UserAdminVo> pageVo = new PageVo<>();
pageVo.setList(userAdminVos);
pageVo.setCurrentPage(page);
pageVo.setListSize(size);
//不是第一页,就是有上一页
pageVo.setHasPre(page != 1);
//总数,总的页数,是否下一页
//总数要查询出来
//总页数 = 总数/size
// 比如说我65个记录,size是30,应该有3页
// 60个记录的时候,那么就是2页
//如果是55个记录,也是2页
//65/30 = 2.x ==> 3,如果2.0===>2。55/30 = 1.x
//是否有下一页:如果当前页不是最后一页 ,那么就表示有下一页
long totalCount = this.baseMapper.listUserTotalCount(phone, email, userName, userId, status);
pageVo.setTotalCount(totalCount);
//计算总的页数
float tempTotalPage = (totalCount * 1.0f / size) + 0.5f;
log.info("tempTotalPage ==> " + tempTotalPage);
int totalPage = Math.round(tempTotalPage);
pageVo.setTotalPage(totalPage);
pageVo.setHasNext(page != totalPage);
return R.SUCCESS("查询用户列表成功.").setData(pageVo);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
mapper.xml代码
<select id="listUser" resultType="net.sunofbeach.uc.vo.UserAdminVo">
SELECT
uu.`id`,uu.`user_name`,uu.`phone_num`,uu.`email`,uu.`lev`,uu.`sex`,uu.`avatar`,uu.`status`,uu.`create_time`,uui.`compony`,uui.`birthday`,uui.`position`,uui.`good_at`,uui.`location`
FROM `uc_user` uu
LEFT JOIN `uc_user_info` uui ON uu.`id` = uui.`user_id`
<where>
1=1
<if test="email!=null and email!=''">
and uu.`email` = #{email}
</if>
<if test="phone!=null and phone!=''">
and uu.`phone_num` = #{phone}
</if>
<if test="userId!=null and userId!=''">
and uu.`id` = #{userId}
</if>
<if test="userId!=null and userId!=''">
and uu.`id` = #{userId}
</if>
<if test="status!=null and status!=''">
and uu.`status` = #{status}
</if>
</where>
ORDER BY uu.`create_time` DESC
LIMIT #{offset},#{size}
</select>
<select id="listUserTotalCount" resultType="java.lang.Long">
SELECT count(uu.`id`)
FROM `uc_user` uu
<where>
1=1
<if test="email!=null and email!=''">
and uu.`email` = #{email}
</if>
<if test="phone!=null and phone!=''">
and uu.`phone_num` = #{phone}
</if>
<if test="userId!=null and userId!=''">
and uu.`id` = #{userId}
</if>
<if test="userId!=null and userId!=''">
and uu.`id` = #{userId}
</if>
<if test="status!=null and status!=''">
and uu.`status` = #{status}
</if>
</where>
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 实现用户禁止
/**
* 禁止用户
* - 用户是否存在
* - 修改用户状态值
* - 让用户退出登录
* - 登录的代码要检查状态
*
* @param userId
* @return
*/
@Override
public R disableUser(String userId) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", userId);
User user = this.baseMapper.selectOne(queryWrapper);
if (user == null) {
return R.FAILED("用户不存在.");
}
user.setStatus(Constants.User.STATUS_DISABLE);
this.baseMapper.updateById(user);
//让用户退出登录,不能直接调用logout,这样子会让自己退出登录.
//可以通过userId去refreshToken的表里查询相关数据,然后退出登录
QueryWrapper<RefreshToken> tokenQueryWrapper = new QueryWrapper<>();
tokenQueryWrapper.eq("user_id", user.getId());
RefreshToken refreshToken = tokenService.getOne(tokenQueryWrapper);
//各种删除
// token
String tokenKey = refreshToken.getTokenKey();
redisUtil.del(Constants.User.KEY_TOKEN + tokenKey);
//删除refreshToken
//salt
redisUtil.del(Constants.User.KEY_SALT + tokenKey);
tokenService.remove(tokenQueryWrapper);
return R.SUCCESS("已禁用该用户.");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 实现用户密码重置
/**
* 管理员重置密码
* - 查找用户是否存在
* - 对密码判断和处理
* - 修改密码
* - 让用户退出登录
*
* @param userId
* @param registerVo
* @return
*/
@Override
public R resetPasswordByUid(String userId, RegisterVo registerVo) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", userId);
User user = this.baseMapper.selectOne(queryWrapper);
if (user == null) {
return R.FAILED("用户不存在.");
}
if (TextUtils.isEmpty(registerVo.getPassword()) || registerVo.getPassword().length() != 32) {
return R.FAILED("密码格式不对.");
}
String encodePwd = bCryptPasswordEncoder.encode(registerVo.getPassword());
user.setPassword(encodePwd);
this.baseMapper.updateById(user);
//让对应的用户退出登录
doLogoutByUid(userId);
return R.SUCCESS("用户密码重置成功.");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# RBAC (role-base access control)
- 角色
- 权限
- 用户
用户因为是某个角色而有权限,比如你是校长,所以你有删除学生资料的权限。而不是因为你,所有有权限删除学生。
用户-角色,角色-权限
回到我们的案例里:你-管理员,管理员-获取用户列表,管理员-禁止用户,管理员-重置密码
# 涉及到的数据表
- 用户表,已经有了
- 角色表
- 权限表
- 模块表(微服务多应用)
- =======关系表======
- 用户-角色表
- 角色-权限表
# 表的字段
- 角色表
- id:ID
- name:角色名称,ADMIN,SU_ADMIN
- label:标签,比如说管理员,超级管理员
- crate_time:创建时间
- update_time:更新时间
- 权限表
- id:ID
- name:权限名称,controller名:方法名:请求方法
- label:权限标签:禁止用户,重置密码
- api:请求路径
- create_time:创建时间
- update_time:更新时间
- 用户角色表
- id:ID
- user_id:用户ID
- role_id:角色ID
- create_time:创建时间
- update_time:更新时间
- 角色-权限表
- id:ID
- role_id:角色ID
- permission_id:权限ID
- create_time:创建时间
- update_time:更新时间
- 角色表
CREATE TABLE `mo_yu_ucenter`.`uc_role`(
`id` VARCHAR(20) NOT NULL COMMENT 'ID',
`name` VARCHAR(32) COMMENT '角色名称',
`label` VARCHAR(32) COMMENT '标签',
`create_time` DATETIME COMMENT '创建时间',
`update_time` DATETIME COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2
3
4
5
6
7
8
权限表
CREATE TABLE `mo_yu_ucenter`.`uc_permission`(
`id` VARCHAR(20) NOT NULL COMMENT 'ID',
`name` VARCHAR(64) COMMENT '权限名称',
`label` VARCHAR(32) COMMENT '标签',
`api` VARCHAR(128) COMMENT '请求路径',
`model_id` VARCHAR(20) COMMENT '模块Id',
`create_time` DATETIME COMMENT '创建时间',
`update_time` DATETIME COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2
3
4
5
6
7
8
9
10
模块表
CREATE TABLE `mo_yu_ucenter`.`uc_app_model`(
`id` VARCHAR(20) NOT NULL COMMENT 'ID',
`app_name` VARCHAR(32) COMMENT '模块名称',
`label` VARCHAR(32) COMMENT '标签',
`create_time` DATETIME COMMENT '创建时间',
`update_time` DATETIME COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2
3
4
5
6
7
8
用户-角色表
CREATE TABLE `mo_yu_ucenter`.`uc_user_role`(
`id` VARCHAR(20) NOT NULL COMMENT 'ID',
`user_id` VARCHAR(20) COMMENT '用户ID',
`role_id` VARCHAR(20) COMMENT '角色ID',
`create_time` DATETIME COMMENT '创建时间',
`update_time` DATETIME COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2
3
4
5
6
7
8
角色-权限表
CREATE TABLE `mo_yu_ucenter`.`uc_role_permission`(
`id` VARCHAR(20) NOT NULL COMMENT 'ID',
`role_id` VARCHAR(20) COMMENT '角色ID',
`permission_id` VARCHAR(20) COMMENT '权限ID',
`create_time` DATETIME COMMENT '创建时间',
`update_time` DATETIME COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2
3
4
5
6
7
8
# 登录以后token需要包含用户角色
查询语句
SELECT GROUP_CONCAT(ur.`name`) AS roles,uu.`id`,uu.`sex`,uu.`status`,uu.`avatar`,uu.`user_name` FROM `uc_user` uu
LEFT JOIN `uc_user_role` uur ON uu.`id` = uur.`user_id`
LEFT JOIN `uc_role` ur ON uur.`role_id` = ur.`id`
WHERE uu.`id` = '1500421581694861313'
2
3
4
如果报错,加以下配置
[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
2