当前位置: 首页 > news >正文

从授权校验看SpringBoot自动装配

背景

最近需要实现一个对于系统的授权检测功能,即当SpringBoot应用被启动时,需要当前设备是否具有有效的的授权许可信息,若无则直接退出应用。具体的实现方案请继续看下文。

环境

Ruoyi-Vue SpringBoot3 RuoYi-Vue: 🎉 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统,同时提供了 Vue3 的版本 - Gitee.comicon-default.png?t=O83Ahttps://gitee.com/y_project/RuoYi-Vue/tree/springboot3/

初始实现

基于文章开头所提到需求,很容易想到可以在主启动类中编写相关代码来实现,由于我们需要的是在类加载初期校验,并且我们可能需要从配置文件中获取部分信息,因此需要使用如下方法来编写代码。

首先在当前主启动类所在的包下新建utils包,用于校验代码的编写,

新建校验工具类InitCheckUtil,代码如下

@Component
public class InitCheckUtil {@Value("${init.key}")private String key;@PostConstructpublic void check() {if (StringUtils.isBlank(key) || !"ruoyi".equals(key)) {System.out.println("系统未授权,请联系管理员");System.exit(0);}else{System.out.println("current key is: " + key + "system is authorized");}}
}

不难看出,这个校验工具的逻辑为,首先从配置文件(application.yml)中加载了init.key的值,配文件信息如下:

之后通过check方法来判断获取的key是否为 ruoyi ,若不满足条件则直接退出。执行代码后结果如下:

可以看到已经打印了当前key值,并表明系统已经进行了了授权。需要注意的是,这里的@PostConstruct注解确保了Spring容器启动时就会执行这个方法。

目前来看似乎已经完成了我们的需求,但是如果现在想要在项目启动前就执行呢,而不是等到容器加载时在校验,可以看到输出当前key信息之前已经有其它的日志信息,更为复杂的项目在启动时往往伴随着更多的前置初始化的东西,那么,如何在SpringBoot类启动前就进行校验呢?

ApplicationContextInitializer

上一小节我们已经实现了一个初步版本,而为了能够满足新的启动之前的即校验需求,我们需要做进一步的改动。首先可以想到的便是在主启动类中进行加载,直接在启动方法之前,使用创建InitCheckUtil对象的方式,然后调用其check方法。

如果此时直接启动项目,将会出现如下效果:

看起来似乎实现了启动前的校验,但是我们并没有更改配置文件中的key值,正常情况下服务应该是启动成功的状态。但目前状况的原因是由于我们现在的代码先于Spring容器执行,因此@Value注解将无法正常读取到key值,进而校验失败,因此InitCheckUtil类的代码变为:

public class InitCheckUtil {private String key;public InitCheckUtil(){}public InitCheckUtil(String key){this.key = key;}public void check() {if (StringUtils.isBlank(key) || !"ruoyi".equals(key)) {System.out.println("系统未授权,请联系管理员");System.exit(0);}else{System.out.println("current key is: " + key + ", system is authorized");}}
}

但由于没有了Spring容器的能力的加持,此时对于配置文件读取就无法直接使用@Value注解来获取了,因此我们现在使用使用一种新的方式来实现对配置文件的读取,即通过ApplicationContextInitializer 接口,它的作用在于当容器启动之前就可以读取配置文件中的值,从而满足我们当前的需求,具体实现如下,新建CheckConfigInitlnitializer类并实现 ApplicationContextInitializer 接口的 initialize 方法,在该方法中通过applicationContext获取ConfigurableEnvironment 对象,进而获取配置文件中的信息后传入校验类中实现校验:

public class CheckConfigInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {ConfigurableEnvironment environment = applicationContext.getEnvironment();String initKey = environment.getProperty("init.key");InitCheckUtil checkUtil = new InitCheckUtil(initKey);checkUtil.check();}
}

由于我们更改了配置文件的获取方式,因此主启动类的中内容也需要做相关的更改,如下所示增加了对于初始化器的注册,从而可以确保可以在整个容器启动之前完成校验。

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class RuoYiApplication
{public static void main(String[] args){SpringApplication application = new SpringApplication(RuoYiApplication.class);application.addInitializers(new CheckConfigInitializer());application.run( args);System.out.println("(♥◠‿◠)ノ゙  若依启动成功   ლ(´ڡ`ლ)゙  \n" +" .-------.       ____     __        \n" +" |  _ _   \\      \\   \\   /  /    \n" +" | ( ' )  |       \\  _. /  '       \n" +" |(_ o _) /        _( )_ .'         \n" +" | (_,_).' __  ___(_ o _)'          \n" +" |  |\\ \\  |  ||   |(_,_)'         \n" +" |  | \\ `'   /|   `-'  /           \n" +" |  |  \\    /  \\      /           \n" +" ''-'   `'-'    `-..-'              ");}
}

 此时系统可正常启动,并输出相应的校验信息如下

自动装配

在上一步中我们已经基本实现了对于文章开头提出需求问题的解决,但是若后续存在类似需求时需要我们不断往主主启动类中添加代码。这种方法看起来极为的不优雅,整个启动类会变得愈发臃肿。因此我们可以通过SpringBoot中的自动装配机制对上面的代码进行进一步的调整优化。

其实目前优化的方式严格意义上时上一步的另一种实现方式,这里将通过使用spring.factories文件来代替在主启动类中显式的注册初始化类。通过在项目当前模块的resources目录下新建META-INF 文件夹(不存在则新建),并新增 spring.factories 文件,该文件的内容将在Spring容器启动前进行扫描并加载,若为了实现通用性可将其迁移至common模块中,此处仅做展示用,方便起见未进行迁移。

 在spring.factories 文件中添加如下内容,其中 key 为 org.springframework.context.ApplicationContextInitialize 固定值,value 为我们自定义的实现的全包名。

org.springframework.context.ApplicationContextInitializer=com.ruoyi.utils.CheckConfigInitializer

这个时候就可以将启动类中的代码还原为原始的状态,再次启动程序后发现系统可以正常运行,其原理为,当SpringApplication初始化时通过SpringFactoriesLoader获取到配置在 META-INF/spring.factories 文件中的 ApplicationContextInitializer 的所有实现类,进而加载到容器中,而在这个过程中将实现对了系统启动的初步校验。

http://www.lryc.cn/news/513757.html

相关文章:

  • tensorboard的界面参数与图像数据分析讲解
  • MTK 平台关于WIFI 6E P2P的解说
  • 离线语音识别+青云客语音机器人(幼儿园级别教程)
  • leetcode hot 100 跳跃游戏
  • 陪诊陪护助浴系统源码:JAVA养老护理助浴陪诊小程序医院陪护陪诊小程序APP源码
  • 怎么在家访问公司服务器?
  • asp.net core框架搭建4-部署IIS/Nginx/Docker
  • ubuntu中zlib安装的步骤是什么
  • 代码随想录算法训练营第二十天-二叉树-669. 修剪二叉搜索树
  • 发现API安全风险,F5随时随地保障应用和API安全
  • 【AI学习】2024年末一些AI总结的摘录
  • ws长时间不发消息会断连吗?
  • 使用 ASP.NET Core wwwroot 上传和存储文件
  • 【每日学点鸿蒙知识】人脸活体检测、NodeController刷新、自动关闭输入框、Row设置中间最大宽、WebView单例
  • Android TV端弹出的PopupWindow没有获取焦点
  • 从0开始的docker镜像制作-ubuntu22.04
  • 1Panel自建RustDesk服务器方案实现Windows远程macOS
  • STM32完全学习——使用定时器1精确延时
  • 深度学习——损失函数汇总
  • 1、单片机寄存器-io输入实验笔记
  • 记忆旅游系统|Java|SSM|VUE| 前后端分离
  • CentOS7下的 OpenSSH 服务器和客户端
  • RabbitMQ基础篇之Java客户端 Topic交换机
  • 微服务-Sentinel新手入门指南
  • 传统听写与大模型听写比对
  • http性能测试命令ab
  • 前端:轮播图常见的几种实现方式
  • Pytest基础01: 入门demo脚本
  • ruoyi 多租户 开启后针对某一条sql不适用多租户; 若依多租户sql规则修改
  • driftingblues6靶机