springboot3.0+JWT+RBAC+spronfdoc(swagger)正式上路 weir 2022-06-04 09:51:40.0 java,springboot,jwt 1037 源码地址:https://gitee.com/weir_admin/weir-project/tree/spring-3.0/security-base QQ群:545513598 另外视频讲解地址: 今日头条:https://www.ixigua.com/7103782477642400271 B站:https://www.bilibili.com/video/BV12B4y1X72B?spm_id_from=333.999.0.0 知乎:https://www.zhihu.com/zvideo/1514977180505681920 spring6.0和springboot3.0也将在今年11月份正式出来,spring加紧布局新一代框架。 由于企业级j2ee规范更名为jakarta,从jakarta9开始包名结构发生了重要变化,比如: ```java import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; ``` servlet都会加jakarta不再是javax了。 所以java企业级api都要进行重构,好在jakarta9 10已经发布所以spring框架也要跟进形式。 大家在很长时间以来对企业级j2ee标准可能都认识的不是很多,十多年来spring普及在全球和国内企业,大家很少去了解j2ee标准,但是从2022年开始如果你还从事java开发,情况可能要发生变化了jakarta标准必须要有所了解了。 新一代java框架比如quarkus,helidon等都在布局自己下一代java框架,特别是在云原生的背景下,java后端服务在golang语言架构的冲击下,spring体系越来越无法满足企业的高要求。 springboot3.0也开发快一年了吧将在2022底正式发布下一波技术浪潮即将到来,技术更新换代,特别是jdk17的发布,各种性能非常亮眼,而从全球来看jdk1.8还处于主流地位,从今年开始就会慢慢发生变化。 我也会在这下半年持续关注springboot3.0的发布,在实践中总结技术细节以开源 代码demo方式让大家学习掌握基本的技术入门。 本篇要分享就是非常基础的jwt+rbac+新一代swagger(springdoc-openapi)的整合。 具体细节大家看源码就行了,我在这里只是提示几点: 1.pom.xml包引用: 我测试用的是mybatis-plus 发现springboot3.0竟然可以用这就非常好了。 注意swagger的引用: org.springdoc springdoc-openapi-starter-webmvc-ui 2.0.0-M3 jwt工具包: org.springframework.boot spring-boot-starter-oauth2-resource-server 其他的大家看源码就可以了。 2.实体RBAC模型: 这个我就不过说了,都是比较成熟的技术。 3.WebSecurityConfigurerAdapter 过时问题: 具体什么时候过时也不是那么重要了,但是官方是不推荐使用了springboot3.0好像还有这个方法,新的使用方式我贴出来: ```java package com.weir.security.config; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtEncoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; //import org.springframework.security.oauth2.jwt.JwtDecoder; //import org.springframework.security.oauth2.jwt.JwtEncoder; //import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; //import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.source.ImmutableJWKSet; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; import com.weir.security.jwt.JWTAuthenticationFilter; import com.weir.security.jwt.JWTAuthenticationManager; // 开启方法注解功能 @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) @Configuration public class SecurityConfig { @Autowired private JWTAuthenticationManager jwtAuthenticationManager; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // restful具有先天的防范csrf攻击,所以关闭这功能 http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // .anyRequest().permitAll() .antMatchers("/actuator/**").permitAll() .antMatchers("/login").permitAll() .antMatchers("/send_sms_code").permitAll() .antMatchers("/check_sms_code").permitAll() .antMatchers("/update_pwd").permitAll() .antMatchers("/init_perm").permitAll() .antMatchers("/upload_one").permitAll() .antMatchers("/video/**").permitAll() .antMatchers("/download/**").permitAll() // swagger start .antMatchers("/swagger-ui/").permitAll().antMatchers("/swagger-resources/**").permitAll() .antMatchers("/swagger-ui.html").permitAll().antMatchers("/swagger-resources/**").permitAll() .antMatchers("/images/**").permitAll().antMatchers("/webjars/**").permitAll() .antMatchers("/v2/api-docs").permitAll().antMatchers("/configuration/ui").permitAll() .antMatchers("/v3/api-docs").permitAll().antMatchers("/configuration/ui").permitAll() .antMatchers("/configuration/security").permitAll() .antMatchers("/v3/api-docs/**").permitAll() .antMatchers("/swagger-ui/**").permitAll() // swagger end .anyRequest().authenticated().and() // 添加属于我们自己的过滤器,注意因为我们没有开启formLogin(),所以UsernamePasswordAuthenticationFilter根本不会被调用 .addFilterAt(new JWTAuthenticationFilter(jwtAuthenticationManager), UsernamePasswordAuthenticationFilter.class) // 前后端分离本身就是无状态的,所以我们不需要cookie和session这类东西。所有的信息都保存在一个token之中。 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); return http.build(); } @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().antMatchers("/static/**","/**/*.js", "/**/*.json", "/**/*.css", "/**/*.js", "/**/*.map", "/**/*.png", "/**/*.html", "/**/*.jpg", "/**/*.svg", "/**/*.ico", "/**/*.mp4"); } @Value("${jwt.public.key}") RSAPublicKey key; @Value("${jwt.private.key}") RSAPrivateKey priv; @Bean JwtDecoder jwtDecoder() { return NimbusJwtDecoder.withPublicKey(this.key).build(); } @Bean JwtEncoder jwtEncoder() { JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build(); JWKSource jwks = new ImmutableJWKSet<>(new JWKSet(jwk)); return new NimbusJwtEncoder(jwks); } } ``` 基本都是用@bean方式使用,大家对看看理解下就行了也没什么技术难度。 4. swagger(springdoc-openapi): ```java package com.weir.security.config; import io.swagger.v3.oas.annotations.ExternalDocumentation; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.info.Contact; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityScheme; import org.springframework.context.annotation.Configuration; /** * * @author weir * */ @OpenAPIDefinition( info = @Info(title = "SpringBoot3.x 集成 springdoc-openapi-ui", description = "SpringBoot3.x 集成 springdoc-openapi-ui 测试文档", version = "1.0.0", license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0") // contact = @Contact(name = "RtxTitanV", url = "https://blog.csdn.net/RtxTitanV", email = "RtxTitanV@xxx.com") ), externalDocs = @ExternalDocumentation(description = "参考文档", url = "https://github.com/springdoc/springdoc-openapi-demos/tree/2.x"), security = @SecurityRequirement(name = "JWT")) @SecurityScheme(name = "JWT", type = SecuritySchemeType.HTTP, bearerFormat = "JWT", scheme = "bearer", in = SecuritySchemeIn.HEADER) @Configuration public class OpenApiConfig {} ``` 其实跟springfox差距不大。 5.mybatis-plus跟springboot3.0兼容问题: ```java package org.springframework.core; import java.io.IOException; /** * 兼容 mybatis-plus 3.5.1 * mybatis-plus 的 MybatisSqlSessionFactoryBean 中使用到了这个异常 * Spring 6 开始移除了该异常 * * @author xiongxiaoyang * @date 2022/5/12 */ public class NestedIOException extends IOException { } ``` 这个我在视频中好像忘了说了,就是这个异常的处理spring6没有了,要想使用mybatis-plus就需要单独加一下,我想后面mybatis-plus社区会主动修复这个问题。 好了先写到这里吧,看代码更快一点,springboot3.0迎接到来。