Springboot3 + SpringSecurity + JWT + OpenApi3 实现认证授权

2023-11-11

Springboot3 + SpringSecurity + JWT + OpenApi3 实现双token

目前全网最新的 Spring Security + JWT 实现双 Token 的案例!收藏就对了,欢迎各位看友学习参考。此项目由作者个人创作,可以供大家学习和项目实战使用,创作不易,转载请注明出处!

该项目使用目前最新的 Sprin Boot3 版本,采用目前市面上最主流的 JWT 认证方式,实现双token刷新。

温馨提示:SpringBoot3 版本必须要使用 JDK11 或 JDK19

SpringBoot3 新特性

Spring Boot3 是一个非常重要的版本,将会面临一个新的发展征程!Sprin Boot 3.0 包含了 12 个月以来,151 个人的 5700+ 次 commit 的贡献。这是自 4 年半前发布的 2.0 版本以来的第一次重大修订,这也是第一个支持 Spring Framework 6.0 和 GraaIVM 的 Spring Boot GA 版本。

Spring Boot 3.0 新版本的主要亮点:

  1. 最低要求为 Java 17 ,兼容 Java 19
  2. 支持用 GraalVM 生成原生镜像,代替了 Spring Native
  3. 通过 Micrometer 和 Micrometer 追踪提高应用可观察性
  4. 支持具有 EE 9 baseline 的 Jakarta EE 10

为什么采用双 Token刷新?

**场景假设:**星期四小金上班的时候摸鱼,准备在某APP 上面追剧,已经深深的陷入了角色中无法自拔,此时如果 Token 过期了 ,小金就不得不重新返回登录界面,重新进行登录,那么这样小金的一次完整的追剧体验就被打断了,这种设计带给小金的体验并不好,于是就需要使用双 Token 来解决。

**如何使用:**在小金首次登陆 APP 时,APP 会返回两个 Token 给小金,一个 accessToken,一个 refreshToken,其中 accessToken 的过期时间比较短,refreshToken 的时间比较长。当 accessToken 失效后,会通过 refreshToken 去重新获取 accessToken,这样一来就可以在不被察觉的情况下仍然使小金保持登录状态,让小金误以为自己一直是登录的状态。并且每次使用refreshToken 后会刷新,每一次刷新后的 refreshToken 都是不相同的。

**优势说明:**小金能够有一次完整的追剧体验,除非摸鱼时被老板发现了。accessToken 的存在,保证了登录的正常验证,因为 accessToken 的过期时间比较短,所以也可以保证账号的安全性。refreshToken 的存在,保证了小金无需在短时间内反复的登录来保持 Token 的有效性,同时也保证了活跃用户的登录状态可以一直延续而不需要重新登录,反复刷新也防止了某些不怀好意的人获取 refreshToken 后对用户账号进行不良操作。

一图胜千言:

项目准备

项目采用 Spring Boot 3 + Spring Security + JWT + MyBatis-Plus + Lombok 进行搭建。

创建数据库

user 表

token 表

在实际中应该把 token 信息保存到 redis

创建 Spring Boot 项目

创建一个 Spring Boot 3 项目,一定要选择 Java 17 或者 Java 19

引入依赖

xml复制代码<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>3.0.4</version>
</dependency>

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
</dependency>

编写配置文件

yml复制代码server:
    port: 8417
spring:
    application:
      name: Spring Boot 3 + Spring Security + JWT + OpenAPI3
    datasource:
        url: jdbc:mysql://localhost:3306/w_admin
        username: root
        password: jcjl417
mybatis-plus:
    configuration:
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    global-config:
        db-config:
            table-prefix: t_
            id-type: auto
    type-aliases-package: com.record.security.entity
    mapper-locations: classpath:mapper/*.xml
application:
    security:
        jwt:
            secret-key: VUhJT0pJT0hVWUlHRFVGVFdPSVJISVVHWUZHVkRVR0RISVVIREJZI1VJSEZTVUdZR0ZTVVk=
            expiration: 86400000 # 1天
            refresh-token:
                expiration: 604800000 # 7 天
springdoc:
    swagger-ui:
        path: /docs.html
        tags-sorter: alpha
        operations-sorter: alpha
    api-docs:
        path: /v3/api-docs

项目实现

准备项目所需要的一系列代码,如 entity、controller 、service、mapper 等

系统角色 Role

定义一个角色(Role)枚举,详细代码参考文章结尾处的项目源码

java复制代码public enum Role {

  // 用户
  USER(Collections.emptySet()),
  // 一线人员
  CHASER( ... ),
  // 部门主管
  SUPERVISOR( ... ),
  // 系统管理员
  ADMIN( ... ),
  ;

  @Getter
  private final Set<Permission> permissions;

  public List<SimpleGrantedAuthority> getAuthorities() {
    var authorities = getPermissions()
            .stream()
            .map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
            .collect(Collectors.toList());
    authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
    return authorities;
  }
}

User 实现 UserDetails

温馨提示:

由于 Spring Security 源码设计的时候 ,将用户名和密码属性定义为 username 和 password,所以我们看到的大部分教程都会遵循源码中的方式,习惯性的将用户名定义为 username,密码定义为 password。

其实我们大可不必遵守这个规则,在我的系统中使用邮箱登录,也即是将邮箱(email)作为 Security 中的用户名(username),那么我必须要将用户输入的 email 作为 username 来存放,这会使我感到非常的不适,因为我的系统中正真的 username 将会 用另外一个单词来命名。

如何避免登录时的字段必须设置为 username 和 password 呢?

重写 getter方法, 只有你的系统中登录的用户名和密码属性不是 username 和 password 的情况下 ,你进行重写才会看到下面红色框中的提示。

重写 username 和 password 的 getter方法

java复制代码@Override
public String getUsername() {
    return email;
}

@Override
public String getPassword() {
    return password;
}

Security 配置文件

需要注意的是 WebSecurityConfigurerAdapter 在 Spring Security 中已经被弃用和移除

下面将采用新的配置文件

java复制代码@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfiguration {

  private final JwtAuthenticationFilter jwtAuthFilter;
  private final AuthenticationProvider authenticationProvider;
  private final LogoutHandler logoutHandler;
  private final RestAuthorizationEntryPoint restAuthorizationEntryPoint;
  private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.csrf()
            .disable()
            .authorizeHttpRequests()
            .requestMatchers(
                "/api/v1/auth/**",
                "/api/v1/test/**",
                "/v2/api-docs",
                "/v3/api-docs",
                "/v3/api-docs/**",
                "/swagger-resources",
                "/swagger-resources/**",
                "/configuration/ui",
                "/configuration/security",
                "/swagger-ui/**",
                "/doc.html",
                "/webjars/**",
                "/swagger-ui.html",
                "/favicon.ico"
            ).permitAll()
            .requestMatchers("/api/v1/supervisor/**").hasAnyRole(SUPERVISOR.name(), ADMIN.name())

            .requestMatchers(GET, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_READ.name(), ADMIN_READ.name())
            .requestMatchers(POST, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_CREATE.name(), ADMIN_CREATE.name())
            .requestMatchers(PUT, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_UPDATE.name(), ADMIN_UPDATE.name())
            .requestMatchers(DELETE, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_DELETE.name(), ADMIN_DELETE.name())

            .requestMatchers("/api/v1/chaser/**").hasRole(CHASER.name())

            .requestMatchers(GET, "/api/v1/chaser/**").hasAuthority(CHASER_READ.name())
            .requestMatchers(POST, "/api/v1/chaser/**").hasAuthority(CHASER_CREATE.name())
            .requestMatchers(PUT, "/api/v1/chaser/**").hasAuthority(CHASER_UPDATE.name())
            .requestMatchers(DELETE, "/api/v1/chaser/**").hasAuthority(CHASER_DELETE.name())

            .anyRequest()
            .authenticated()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authenticationProvider(authenticationProvider)
            //添加jwt 登录授权过滤器
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
            .logout()
            .logoutUrl("/api/v1/auth/logout")
            .addLogoutHandler(logoutHandler)
            .logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext())

    ;
    //添加自定义未授权和未登录结果返回
    http.exceptionHandling()
            .accessDeniedHandler(restfulAccessDeniedHandler)
            .authenticationEntryPoint(restAuthorizationEntryPoint);

    return http.build();
  }
}

OpenApi 配置文件

OpenApi 依赖

xml复制代码<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.1.0</version>
</dependency>

OpenApiConfig 配置

OpenApi3 生成接口文档,主要配置如下

  • Api Group(分组)
  • Bearer Authorization(认证)
  • Customer(自定义请求头等)
java复制代码@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI(){
        return new OpenAPI()
                .info(info())
                .externalDocs(externalDocs())
                .components(components())
                .addSecurityItem(securityRequirement())
                ;
    }

    private Info info(){
        return new Info()
                .title("京茶吉鹿的 Demo")
                .version("v0.0.1")
                .description("Spring Boot 3 + Spring Security + JWT + OpenAPI3")
                .license(new License()
                        .name("Apache 2.0") // The Apache License, Version 2.0
                        .url("https://www.apache.org/licenses/LICENSE-2.0.html"))
                .contact(new Contact()
                        .name("京茶吉鹿")
                        .url("http://localost:8417")
                        .email("jc.top@qq.com"))
                .termsOfService("http://localhost:8417")
                ;
    }

    private ExternalDocumentation externalDocs() {
        return new ExternalDocumentation()
                .description("京茶吉鹿的开放文档")
                .url("http://localhost:8417/docs");
    }

    private Components components(){
        return new Components()
                .addSecuritySchemes("Bearer Authorization",
                        new SecurityScheme()
                                .name("Bearer 认证")
                                .type(SecurityScheme.Type.HTTP)
                                .scheme("bearer")
                                .bearerFormat("JWT")
                                .in(SecurityScheme.In.HEADER)
                )
                .addSecuritySchemes("Basic Authorization",
                        new SecurityScheme()
                                .name("Basic 认证")
                                .type(SecurityScheme.Type.HTTP)
                                .scheme("basic")
                )
                ;

    }

    private SecurityRequirement securityRequirement() {
        return new SecurityRequirement()
                .addList("Bearer Authorization");
    }

    private List<SecurityRequirement> security(Components components) {
        return components.getSecuritySchemes()
                .keySet()
                .stream()
                .map(k -> new SecurityRequirement().addList(k))
                .collect(Collectors.toList());
    }


    /**
     * 通用接口
     * @return
     */
    @Bean
    public GroupedOpenApi publicApi(){
        return GroupedOpenApi.builder()
                .group("身份认证")
                .pathsToMatch("/api/v1/auth/**")
                // 为指定组设置请求头
                // .addOperationCustomizer(operationCustomizer())
                .build();
    }
    
    /**
     * 一线人员
     * @return
     */
    @Bean
    public GroupedOpenApi chaserApi(){
        return GroupedOpenApi.builder()
                .group("一线人员")
                .pathsToMatch("/api/v1/chaser/**",
                        "/api/v1/experience/search/**",
                        "/api/v1/log/**",
                        "/api/v1/contact/**",
                        "/api/v1/admin/user/update")
                .pathsToExclude("/api/v1/experience/search/id")
                .build();
    }

    /**
     * 部门主管
     * @return
     */
    @Bean
    public GroupedOpenApi supervisorApi(){
        return GroupedOpenApi.builder()
                .group("部门主管")
                .pathsToMatch("/api/v1/supervisor/**",
                        "/api/v1/experience/**",
                        "/api/v1/schedule/**",
                        "/api/v1/contact/**",
                        "/api/v1/admin/user/update")
                .build();
    }

    /**
     * 系统管理员
     * @return
     */
    @Bean
    public GroupedOpenApi adminApi(){
        return GroupedOpenApi.builder()
                .group("系统管理员")
                .pathsToMatch("/api/v1/admin/**")
                // .addOpenApiCustomiser(openApi -> openApi.info(new Info().title("京茶吉鹿接口—Admin")))
                .build();
    }
}

Security 接口赋权的方式

hasRole及hasAuthority的区别?

hasAuthority能通过的身份必须与字符串一模一样,而hasRole能通过的身前缀必须带有ROLE_,同时可以通过两种字符串,一是带有前缀ROLE_,二是不带前缀ROLE_。

通过配置文件

在配置文件中指明访问路径的权限

java复制代码.requestMatchers("/api/v1/supervisor/**").hasAnyRole(SUPERVISOR.name(), ADMIN.name())
.requestMatchers(GET, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_READ.name(), ADMIN_READ.name())
.requestMatchers(POST, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_CREATE.name(), ADMIN_CREATE.name())
.requestMatchers(PUT, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_UPDATE.name(), ADMIN_UPDATE.name())
.requestMatchers(DELETE, "/api/v1/supervisor/**").hasAnyAuthority(SUPERVISOR_DELETE.name(), ADMIN_DELETE.name())

java复制代码.requestMatchers("/api/v1/chaser/**").hasRole(CHASER.name())
.requestMatchers(GET, "/api/v1/chaser/**").hasAuthority(CHASER_READ.name())
.requestMatchers(POST, "/api/v1/chaser/**").hasAuthority(CHASER_CREATE.name())
.requestMatchers(PUT, "/api/v1/chaser/**").hasAuthority(CHASER_UPDATE.name())
.requestMatchers(DELETE, "/api/v1/chaser/**").hasAuthority(CHASER_DELETE.name())

通过注解

java复制代码@RestController
@RequestMapping("/api/v1/admin")
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "系统管理员权限测试")
public class AdminController {

    @GetMapping
    @PreAuthorize("hasAuthority('admin:read')")
    public String get() {
        return "GET |==| AdminController";
    }


    @PostMapping
    @PreAuthorize("hasAuthority('admin:create')")
    public String post() {
        return "POST |==| AdminController";
    }
}

测试

我们登录认证成功后,系统会为我们返回 access_token 和 refresh_token。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Springboot3 + SpringSecurity + JWT + OpenApi3 实现认证授权 的相关文章

  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • Spark 1.3.1 上的 Apache Phoenix(4.3.1 和 4.4.0-HBase-0.98)ClassNotFoundException

    我正在尝试通过 Spark 连接到 Phoenix 并且在通过 JDBC 驱动程序打开连接时不断收到以下异常 为简洁起见 下面是完整的堆栈跟踪 Caused by java lang ClassNotFoundException org a
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • Liferay ClassNotFoundException:DLFileEntryImpl

    在我的 6 1 0 Portal 实例上 带有使用 ServiceBuilder 和 DL Api 的 6 1 0 SDK Portlet 这一行 DynamicQuery query DynamicQueryFactoryUtil for
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • Spring @RequestMapping 带有可选参数

    我的控制器在请求映射中存在可选参数的问题 请查看下面的控制器 GetMapping produces MediaType APPLICATION JSON VALUE public ResponseEntity
  • 斯坦福 NLP - 处理文件列表时 OpenIE 内存不足

    我正在尝试使用斯坦福 CoreNLP 中的 OpenIE 工具从多个文件中提取信息 当多个文件 而不是一个 传递到输入时 它会给出内存不足错误 All files have been queued awaiting termination
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • getResourceAsStream() 可以找到 jar 文件之外的文件吗?

    我正在开发一个应用程序 该应用程序使用一个加载配置文件的库 InputStream in getClass getResourceAsStream resource 然后我的应用程序打包在一个 jar文件 如果resource是在里面 ja
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两

随机推荐

  • 关于$'\r': command not found错误的一点体会

    今天运行一个其他组开发的jar包 这个jar包由于运行参数是通过命令行的方式输入的 所以需要运行一个shell脚本来启动 启动脚本类似这样 bin bash jdbc driverClassName com mysql jdbc Drive
  • 力扣-->#剑指Offer 897 . 递增顺序搜索树(E)

    class Solution TreeNode curr public TreeNode increasingBST TreeNode root TreeNode first new TreeNode 1 用first 来记录curr的初始
  • 途客圈创业记--读书笔记

    一 初创公司股权结构 2011年6月公司创立 自筹启动资金50万 1 陈天和Alex 每人出资25万元 总计50万元 作为启动资金 2 陈天 CTO兼董事长 股份60 因为是想法的发起人 且在实现这个想法的过程中 Alex CEO 股份40
  • 【C++学习第三讲】C++语句

    目录 C 语句 一 引入 二 声明语句和变量 1 为什么变量必须声明 三 赋值语句 四 cout新花样 五 cout和printf 六 其他C 语句 1 使用cin 2 使用cout进行拼接 七 类简介 一 引入 C 程序是一组函数 而每个
  • Qt::FramelessWindowHint无边框化,移动,大小调整

    QT工作笔记壹 导读 开发环境 QT界面无边框 方法1 方法2 引用 导读 最近工作一个项目需要用QT设计一个UI 看了一下目前主流商业化UI 例如扣扣 微信 网易云音乐 结合个人审美 本人非美术专业 但游戏经验丰富 对人机交互界面有个人看
  • 为什么有了uwsgi 还要 nginx 服务器

    相信每一个使用nginx uwsgi django部署过的人 都感到非常复杂 到底为什么一个项目的发布要经过这么多层级 他们每一层有什么理由存在 这就带大家宏观地看待一下 首先nginx 是对外的服务接口 外部浏览器通过url访问nginx
  • MyBatis 的执行流程

    前言 MyBatis可能很多人都一直在用 但是MyBatis的SQL执行流程可能并不是所有人都清楚了 那么既然进来了 通读本文你将收获如下 1 Mapper接口和映射文件是如何进行绑定的 2 MyBatis中SQL语句的执行流程 3 自定义
  • Js Vue 获取当月第一天、最后一天、当天

    new Date 效果 2023 06 12 注 本文示例以获取当天为例 一 new Date 在vue中使用new Date 获取当月第一天 最后一天 当天 二 使用步骤 1 定义方法 代码如下 示例 param type 0 第一天 1
  • 如何在Gephi中可视化多层网络

    要借助一个插件实现 Isometric Layout 感谢插件作者对gephi社群提供的贡献 作者网页的图片 下载 url https gephi org plugins plugin isometric layout 点击这儿下载再在Ge
  • centos8.5.111安装mysql8.0

    修改网络源 Connecting to 192 168 182 154 22 Connection established To escape to local shell press Ctrl Alt Activate the web c
  • 关于CSS3:justify-self,justify-items和justify-content之间的区别

    这篇文章应该能帮到你 https www codenong com 48535585 总结 flex布局 这三个属性中 只能用justify content属性 1 justify content 2 justify items 3 jus
  • LeetCode: 高频树结构题目总结 - Python

    LeetCode 高频树结构题目总结 问题描述 LeetCode 98 验证二叉搜索树 根据中序遍历 判断大小 LeetCode 99 恢复二叉搜索树 搜索二叉树有两个节点搞错了 恢复好 LeetCode 100 相同的树 LeetCode
  • MyCobot六轴机械臂(六)--Myblockly模块简介

    1 Logic模块 如图3 11所示 表示if 条件 do 程序1 else 程序2 若满足条件则执行程 序1 否则执行程序2 所表示方法的详细讲解可查看图1 2下方的文字讲解 所表示的逻辑判断 返回值为true或者false 可以点击 中
  • spring源码研究之IoC容器在web容器中初始化过程

    前段时间在公司做了一个项目 项目用了spring框架实现 WEB容器是Tomct 5 虽然说把项目做完了 但是一直对spring的IoC容器在web容器如何启动和起作用的并不清楚 所以就抽时间看一下spring的源代码 借此了解它的原理 我
  • 经典SQL题目-求第N高的薪水的解法汇总及知识点复习

    这几天在看Leetcode的时候逐步开始留意SQL题目 不做不知道 一做才感觉自己的SQL太弱了 因此将一道经典题目 求第N高的薪水的解法进行汇总 MySQL 相关解法的原文链接已标注在文末 题目的链接为 第N高的薪水 一 题干 第N高的薪
  • [云原生专题-45]:Kubesphere云治理-基于Kubernetes 构建的企业级容器平台简介与总体架构

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 122905834 目录 前言 第1章
  • pip换源+更改默认安装位置

    本文档创建于2023年3月9日 本文记录了pip换源和更改默认安装位置的操作 主要用于pip的一些配置 方便下载和文件管理 pip换源 使用pip安装库时 如果用默认的库经常会遇到连接不上或下载慢的问题 更换为国内的库下载会更快 临时换源
  • 使用TCP方式拉取Canal数据

    1 Canal对接Kafka联调 1 1 配置修改 canal properties 修改 zk canal zkServers 10 51 50 219 2181 instance properties 开启配置项 canal mq dy
  • 1056 组合数的和

    给定 N 个非 0 的个位数字 用其中任意 2 个数字都可以组合成 1 个 2 位的数字 要求所有可能组合出来的 2 位数字的和 例如给定 2 5 8 则可以组合出 25 28 52 58 82 85 它们的和为330 输入格式 输入在一行
  • Springboot3 + SpringSecurity + JWT + OpenApi3 实现认证授权

    Springboot3 SpringSecurity JWT OpenApi3 实现双token 目前全网最新的 Spring Security JWT 实现双 Token 的案例 收藏就对了 欢迎各位看友学习参考 此项目由作者个人创作 可