Ruoyi-Vue 升级JDK21、Springboot3、Mybatis3
一、需要升级的组件
名称 | 原版本 | 目标版本 |
---|---|---|
JDK | 1.8 | 21 |
Springboot、Redis | 2.5.13 | 3.2.5 |
Servlet | 4.0.1 | 6.0.0 |
Mybatis | 2.0.7 | 3.0.3 |
Mysql驱动 | 8.0.28 | 8.3.0 |
Lombok | 1.18.20 | 1.18.30 |
Kaptcha | 2.3.2 | 2.3.3 |
Flowable | 1.8 | 21 |
jaxb-api | 2.3.0 | 2.3.1 |
二、详细说明
JDK
<java.version>1.8</java.version><!-- ↓↓↓改为下面↓↓↓ --><java.version>21</java.version>
Springboot、Redis
<springboot.version>2.5.13</springboot.version><!-- ↓↓↓改为下面↓↓↓ --><springboot.version>3.2.5</springboot.version>
Servlet
依赖调整
<!-- servlet包 -->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId>
</dependency><!-- ↓↓↓改为下面↓↓↓ --><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId>
</dependency>
全局包名更换
javax.servlet → jakarta.servletjavax.validation → jakarta.validationjavax.annotation → jakarta.annotation
Mybatis
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version><!-- ↓↓↓改为下面↓↓↓ --><mybatis-spring-boot.version>3.0.3</mybatis-spring-boot.version>
Mysql驱动
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><!-- ↓↓↓改为下面↓↓↓ --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId>
</dependency>
Lombok
<lombok.version>1.18.20</lombok.version><!-- ↓↓↓改为下面↓↓↓ --><lombok.version>1.18.30</lombok.version>
Kaptcha
<kaptcha.version>2.3.2</kaptcha.version>
<!-- ↓↓↓改为下面↓↓↓ -->
<kaptcha.version>2.3.3</kaptcha.version><!-- 验证码 -->
<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>${kaptcha.version}</version>
</dependency><!-- ↓↓↓改为下面↓↓↓ --><!-- 验证码 --><dependency><groupId>pro.fessional</groupId><artifactId>kaptcha</artifactId><exclusions><exclusion><artifactId>servlet-api</artifactId><groupId>jakarta.servlet</groupId></exclusion></exclusions></dependency>
Easy-captcha
<dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version>
</dependency><!-- ↓↓↓后面添加↓↓↓ --><dependency><groupId>org.openjdk.nashorn</groupId><artifactId>nashorn-core</artifactId><version>15.4</version>
</dependency>
Flowable
<dependency><groupId>org.flowable</groupId><artifactId>flowable-engine</artifactId><version>6.7.2</version>
</dependency>
<dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-basic</artifactId><version>6.7.2</version>
</dependency><!-- ↓↓↓改为下面↓↓↓ --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>7.1.0</version>
</dependency>
jaxb-api
<dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version>
</dependency><!-- ↓↓↓后面添加↓↓↓ --><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version>
</dependency>
三、相关配置类调整
PermitAllUrlProperties
@Configuration
public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware {private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");private ApplicationContext applicationContext;private List<String> urls = new ArrayList<>();public String ASTERISK = "*";@Overridepublic void afterPropertiesSet() {RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();map.keySet().forEach(info -> {HandlerMethod handlerMethod = map.get(info);// 获取方法上边的注解 替代path variable 为 *Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
// Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
// .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));Optional.ofNullable(method).flatMap(anonymous -> Optional.ofNullable(info.getPatternsCondition())).ifPresent(patterns -> patterns.getPatterns().forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));// 获取类上边的注解, 替代path variable 为 *Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
// Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
// .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));Optional.ofNullable(controller).flatMap(anonymous -> Optional.ofNullable(info.getPatternsCondition())).ifPresent(patterns -> patterns.getPatterns().forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));});}@Overridepublic void setApplicationContext(ApplicationContext context) throws BeansException {this.applicationContext = context;}public List<String> getUrls() {return urls;}public void setUrls(List<String> urls) {this.urls = urls;}
}
SecurityConfig
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {/*** 允许匿名访问的地址*/@Resourceprivate PermitAllUrlProperties permitAllUrl;/*** 自定义用户认证逻辑*/@Resourceprivate UserDetailsService userDetailsService;/*** 认证失败处理类*/@Resourceprivate AuthenticationEntryPointImpl unauthorizedHandler;/*** 退出处理类*/@Resourceprivate LogoutSuccessHandlerImpl logoutSuccessHandler;/*** token认证过滤器*/@Resourceprivate JwtAuthenticationTokenFilter authenticationTokenFilter;/*** 跨域过滤器*/@Resourceprivate CorsFilter corsFilter;@Beanpublic AuthenticationManager authenticationManagerBean(){DaoAuthenticationProvider provider = new DaoAuthenticationProvider();provider.setUserDetailsService(userDetailsService);provider.setPasswordEncoder(bCryptPasswordEncoder());return new ProviderManager(provider);}/*** anyRequest | 匹配所有请求路径* access | SpringEl表达式结果为true时可以访问* anonymous | 匿名可以访问* denyAll | 用户不能访问* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问* hasRole | 如果有参数,参数表示角色,则其角色可以访问* permitAll | 用户可以任意访问* rememberMe | 允许通过remember-me登录的用户访问* authenticated | 用户登录后可访问*/@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {// CSRF禁用,因为不使用sessionhttp.csrf(AbstractHttpConfigurer::disable)// authenticationEntryPoint认证失败处理类;accessDeniedHandler授权失败.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))// 基于token,所以不需要session.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))// 过滤请求// 对于登录login 允许匿名访问,但是如果携带认证token反而不能访问.authorizeHttpRequests(auth -> {permitAllUrl.getUrls().forEach(url -> auth.requestMatchers(url).permitAll());auth.requestMatchers("/login", "/app-user/login").anonymous().requestMatchers(HttpMethod.GET,"/","/*.html","/**.html","/*.css",// 前端資源放行"/static/**",// 由于安卓手机微信公众号页面无法实现blog二进制流的读取下载pdf,// 故这里暂时先放开,后续有客户沟通是否需要做公众号卡号绑定业务增强安全性"/*.js",// 文件上傳路徑放行"/captcha/*","/home/management/uploadPath/**`").permitAll()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();}).headers(AbstractHttpConfigurer::disable);//注销配置,退出后跳转到/logout,退出成功后处理logoutSuccessHandlerhttp.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler));// 添加JWT filter,添加该过滤器放在UsernamePasswordAuthenticationFilter过滤器之前http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);// 添加CORS filterhttp.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);http.addFilterBefore(corsFilter, LogoutFilter.class);return http.build();}/*** 强散列哈希加密实现,这里会使得密码校验的时候默认使用该加密实现*/@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}
application.yml
redis配置
spring:data: # ←----- ######## 改动在这里,多了一级data ########## redis配置redis:# 地址host: localhostport: 6379# 数据库索引database: 2# 密码password: # 连接超时时间timeout: 10slettuce:pool:# 连接池中的最小空闲连接min-idle: 0# 连接池中的最大空闲连接max-idle: 8# 连接池的最大数据库连接数max-active: 8# #连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms
pom.yml
maven打包插件
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration>
</plugin><!-- ↓↓↓改为下面↓↓↓ --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>${java.version}</source><target>${java.version}</target><encoding>${project.build.sourceEncoding}</encoding><compilerArgs> <!--←----- ######## 改动在这里,加了这个配置 ######### --><arg>-parameters</arg></compilerArgs></configuration>
</plugin>
四、补充
- 项目启动报错
Failed to start bean ‘documentationPluginsBootstrapper‘; nested exception is java.lang.NullPointerException
解决:暂时关闭swagger 或 升级
-
循环依赖问题
解决:加@Lasy注解或根据实际业务情况调整代码调用 -
验证码问题
解决:添加解析器(上面已提到)
<dependency><groupId>org.openjdk.nashorn</groupId><artifactId>nashorn-core</artifactId><version>15.4</version>
</dependency>
- 还有其它报错,可根据具体错误信息查看源码、百度、大模型解决,提升解决问题能力。相信自己是最棒的!!