tienchin项目笔记
项目结构分析
项目结构:多模块(不是微服务)
整体思路:
- 依赖链最底层:common,提供了公用的工具,定义了统一的controller,BaseEntity实体类公共对象,以及其他的操作工具
- common的上一层:framework,主要是配置类,在这里配置了系统的一些东西,例如security aop 数据源的配置等等
- 剩下的对应了三个的功能模块
- system:系统管理,相等于具体的业务。比如系统管理中的用户管理,角色管理,菜单管理等等,包括系统监控什么的,都是写在system里面的
- generator:代码生成
- quartz:定时任务
- ui:前端项目
- admin:项目唯一的统一入口,controller都是在这里写的,上面的common等,都会被admin所依赖,这里的controller会调用对应的service
登录
验证码响应结果分析:
Base64字符串转图片:https://tool.jisuapi.com/base642pic.html
验证码生成接口分析:
url:localhost/dev-api/captchaImage
AjaxResult是一个封装的返回工具类
验证码配置分析
使用github上的kaptcha开源工具
导入maven依赖
1 | <dependency> |
接下来只要提供一个CaptchaConfig就行
再在里面提供一个bean
1 | package com.lcdzzz.kaptcha; |
搞一个图片返回的controller
1 |
|
接下里我们访问localhost:8082/img
就会生成图片。
BUT!!!其实这个验证码工具,自己提供了个servlet,其实我们不需要自己写,直接用它提供的servlet
怎么用呢?
注册一个bean,原先的bean就不使用了
1 |
|
只有当:使用这个工具提供的验证码接口, properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
这个配置才是有效的
此时,只有hello一个接口:
1 |
|
结果:

=======总结========
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
- 如果想用Kaptcha本身提供的servlet来生成验证码的话,它就会自动把验证码文本存入session中
- 但如果是自己写的接口,比如最开始的方法,那么文本就需要自己手动去存入session中了
tienchin项目本身
回归项目本身,其实properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
这行配置没必要,不需要
为什么呢?
- 生成验证码接口是我们自己写的
- tienchin项目本身没有用session
- 所以tienchin中Captconfig中的bean,最终目的就是生成字符串/数学运算的验证码
数学运算方面有意思的是这行代码,意思是验证码文本生成器是:KaptchaTextCreator
1 | // 验证码文本生成器 |
登录
流程分析
登录核心:
SysLoginService类下的login方法中的
1
2
3
4
5// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
// 这句话的意思就是去执行登录
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
登录成功后生成一个jwt字符串,
return tokenService.createToken(loginUser);
从这里点进createToken查看public String createToken(LoginUser loginUser) { String token = IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser);//所谓的刷新,就是存入到redis里面去 Map<String, Object> claims = new HashMap<>(); claims.put(Constants.LOGIN_USER_KEY, token); return createToken(claims); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4. 点进上面的refreshToken
```java
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}暂且不考虑jwt原先的作用(例如无状态登录),其实这里的jwt作用已经“变形了”,实际上又变成了httpsession的作用,【虽然没有用session,但其实用户信息已经存入到redis里面了】
所谓的jwt,其实返回的仅仅只是uuid【相当于sessionid】,以后来回都传uuid
以后发送请求时,都会携带jwt字符串
登录成功后,f12查看token

这个token是一个jwt字符串,而jwt字符串是分为了三部分【都是用base64编码】,如下
1 | eyJhbGciOiJIUzUxMiJ9 //指定算法名字 |
- 使用base64解码工具【https://base64.us/】查看内容

流程分析总结
综上,登录流程就是:登录成功后,返回一个jwt字符串,以后每次发送请求都会携带它。jwt字符串分为三部分,第一部分是算法,第二部分是核心信息,核心信息里面只有一个uuid。
同时,它把用户信息存入redis里面中【登录信息,管理员,名字,leader,部门等等,都存在redis里面】,如图【左边相等于id,右边则是信息】

- 以后每次访问接口时,都要带上jwt字符串

所谓的jwt说白了就像用session时的jsessionid一样。只不过jsessionid是浏览器自动携带,而现在是自己手动加上jwt参数的
每次请求都需要校验参数,而这个是在哪里校验的呢?
jwt校验
核心部分就是framework.security.filter下的JwtAuthenticationTokenFilter
1 |
|
每次请求的时候,都带上jwt,然后拿着jwt,去redis里面查看用户信息,,然后存到SecurityContextHolder里面去
springsecurity登录配置分析
详见framework.config下的SecurityConfig类,以后准备再看看springsecurity
自定义动态数据源
思路分析
- 自定义一个注解@DataSource,将来可以将该注解加service层在方法或者类上面,表示方法或者类中的所有方法都使用某一个数据源
- 对于第一步,如果某个方法上面有@DataSource注解,那么就将该方法需要使用的数据源名称存入ThreadLocal。
- 自定义切面,在切面中解析@DataSource注解的时候,将@DataSource注解所标记的数据源存入到ThreadLocal中。
- 最后,当Mapper执行的时候,需要DataSource,他会自动去AbstractRoutingDataSource类中查找需要的数据源,我们只需要在AbstractRoutingDataSource中返回ThreadLocal中的值
综上:用@DataSource注解,在一个方法或者一个类上面,去标注你想使用哪个数据源。然后将来在这个AOP(切面)里面解析这个注解,把想使用的数据源的名字找出来,存在ThreadLocal里面去。当以后真正需要用的时候,人家会自动的从AbstractRoutingDataSource里面去查找需要的数据源。
所以,我们要做的就是:重写(自己写一个类继承)AbstractRoutingDataSource,然后在它的方法里面去返回ThreadLocal里边所存储的数据源的名字。最后它会根据名字找到对应的数据源
详细可见:自定义动态数据源
自定义限流注解
详见:自定义限流注解