项目笔记

63 阅读5分钟

尚硅谷金融宝

代码生成器

逻辑删除

封装统一返回结果

自定义断言处理异常

数据字典

表设计优化

vhr微人事

苍穹外卖

JWT登录

JWT完整流程如下: image.png

  • 用户使用账号和密码发起 POST 请求;
  • 服务器使用私钥创建一个 JWT;
  • 服务器返回这个 JWT 给浏览器;
  • 浏览器将该 JWT 串在请求头中像服务器发送请求;
  • 服务器验证该 JWT;
  • 返回响应的资源给浏览器。

JWT的优点

  • 不需要在服务端保存会话信息,特别适用于分布式微服务。
  • 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据
  • 简洁(Compact):可以通过URL, POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
  • 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。

注意事项

  • JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
  • JWT 不加密的情况下,不能将秘密数据写入 JWT。
  • JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
  • JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
  • JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
  • 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

全局异常处理

ThreadLocal线程局部变量

ThreadLocal不是一个Thread,而是Thread的局部变量

ThreadLocal为每个线程单独提供一个存储空间,具有线程隔离效果,只有在线程内才能获取到对应的值,线程外则不能访问。

客户端每一次访问,tomacat都会开辟一个线程

所以当我们保证在线程的生命周期只内,能访问到,就可以把值放到里面。

QQ_1744315771253.png 用户每次访问会被拦截器拦截,从请求携带的token,解析JWT出当前登录员工id,并放入线程局部变量

解决原理 代码中已经提供了封装好的ThreadLocal,名字是BaseContext 因为每次请求都会用开辟一个线程 而且每次请求前端都会带token过来,所以它发送保存用户的时候也会带token过来进行验证 所以将jwt验证出来的token放置在ThreadLocal中 因为这次保存和他是一个线程,所以ThreadLocal还是一个,所以保存的时候直接从ThreadLocal中获取即可

解决方式 在jwt验证的代码中放置token到ThreadLocal 在service中获取ThreadLocal将id放进去

请求返回结果时间转换

由mybatis查询的结果时间由字符串转换为"yyyy-MM-dd HH:mm:ss"

方式一 单独处理 @JsonFormat

方式二 全局处理 扩展WebMvcConfiguration消息转换器

在spring中配置webMvc有两种方法,一种是继承WebMvcConfigurationSupport,另一种方式就是继承WebMvcConfigurer,但是要多加一个@EnableWebMvc注解。

  • WebMvcConfigurer是一个接口,用于配置全局的SpringMVC的相关属性,采用JAVABEAN的方式来代替传统的XML配置文件,提供了跨域设置、静态资源处理器、类型转化器、自定义拦截器、页面跳转等能力。
  • WebMvcConfigurationSupport类是SpringMVC提供的扩展类,用于提供拦截器、资源处理器等注册功能。

自动填充公共创建时间和修改时间

使用自定义注解,搭配AOP和反射

/**
 * 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //数据库操作类型:UPDATE INSERT
    OperationType value();
}
/**
 * 自定义切面,实现公共字段自动填充处理逻辑
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    /**
     * 切入点
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中进行公共字段的赋值
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        log.info("开始进行公共字段自动填充...");

        //获取到当前被拦截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = autoFill.value();//获得数据库操作类型

        //获取到当前被拦截的方法的参数--实体对象
        Object[] args = joinPoint.getArgs();
        if(args == null || args.length == 0){
            return;
        }

        Object entity = args[0];

        //准备赋值的数据
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

        //根据当前不同的操作类型,为对应的属性通过反射来赋值
        if(operationType == OperationType.INSERT){
            //为4个公共字段赋值
            try {
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对象属性赋值
                setCreateTime.invoke(entity,now);
                setCreateUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(operationType == OperationType.UPDATE){
            //为2个公共字段赋值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对象属性赋值
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

黑马头条

表字段设置

tinyint类型 image.png

乐优商城

畅享商城

学成在线

蘑菇博客

mall商城