一、需要升级的组件
名称 | 原版本 | 目标版本 |
---|---|---|
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.servlet
javax.validation → jakarta.validation
javax.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 = "*";
@Override
public 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))));
});
}
@Override
public 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 {
/**
* 允许匿名访问的地址
*/
@Resource
private PermitAllUrlProperties permitAllUrl;
/**
* 自定义用户认证逻辑
*/
@Resource
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@Resource
private AuthenticationEntryPointImpl unauthorizedHandler;
/**
* 退出处理类
*/
@Resource
private LogoutSuccessHandlerImpl logoutSuccessHandler;
/**
* token认证过滤器
*/
@Resource
private JwtAuthenticationTokenFilter authenticationTokenFilter;
/**
* 跨域过滤器
*/
@Resource
private CorsFilter corsFilter;
@Bean
public 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 | 用户登录后可访问
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// CSRF禁用,因为不使用session
http.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,退出成功后处理logoutSuccessHandler
http.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler));
// 添加JWT filter,添加该过滤器放在UsernamePasswordAuthenticationFilter过滤器之前
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
http.addFilterBefore(corsFilter, LogoutFilter.class);
return http.build();
}
/**
* 强散列哈希加密实现,这里会使得密码校验的时候默认使用该加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
application.yml
redis配置
spring:
data: # ←----- ######## 改动在这里,多了一级data #########
# redis配置
redis:
# 地址
host: localhost
port: 6379
# 数据库索引
database: 2
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
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>
- 还有其它报错,可根据具体错误信息查看源码、百度、大模型解决,提升解决问题能力。相信自己是最棒的!!