Spring Security :一【权限管理概述、Spring Security 认证与授权】

news2024/12/29 9:01:21

文章目录

  • Spring Security
    • 一、权限管理概述
      • 1.1.什么是认证
      • 1.2 什么是授权
      • 1.3 授权的数据模型RBAC
        • 1.3.1 基于角色的访问控制
        • 1.3.2 基于资源的访问控制
      • 1.4 权限管理框架
        • 1.4.1 Apache Shiro
        • 1.4.2 Spring Security
        • 1.4.3 Shiro 和 Spring Security 比较
    • 二、Spring Security 认证与授权
      • 2.1 环境准备
      • 2.2 认证
        • 2.2.1 安全配置
        • 2.2.2 自定义登录界面
        • 2.2.3 403 问题解决
        • 2.2.4 修改登录参数名
        • 2.2.5 登出配置
        • 2.2.6 自定义登录成功处理器
        • 2.2.7 自定义登录失败处理器
      • 2.3 授权
        • 2.3.1 配置方式授权
        • 2.3.2 常见配置
        • 2.3.3 注解授权


Spring Security

学习目标

  • 了解权限管理概念
  • 掌握SpringSecurity可以解决什么问题, 为什么要学习他
  • 掌握 Spring Security 如何做认证
  • 掌握Spring Security 如何做授权
  • 了解认证和授权的底层原理
  • 掌握在项目中集成认证和授权
  • 掌握 JWT

一、权限管理概述

权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源。权限管理几乎出现在任何系统里面,只要有用户和密码的系统。 很多人常将“用户身份认证”、“密码加密”、“系统管理”等概念与权限管理概念混淆。

1.1.什么是认证

进入移动互联网时代,大家每天都在刷手机,常用的软件有微信、支付宝、头条等,下边拿微信来举例子说明认证相关的基本概念,在初次使用微信前需要注册成为微信用户,然后输入账号和密码即可登录微信,输入账号和密码登录微信的过程就是认证。

系统为什么要认证?

认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。

1.2 什么是授权

在一个平台中有很多资源, 不同的用户能够操作的资源是不同的, 需要赋予不同的权限, 只有赋予了才可以操作, 这就是授权.

为什么要授权?

认证是为了保证用户身份的合法性,授权则是为了更细粒度的对隐私数据进行划分,授权是在认证通过后发生的,控制不同的用户能够访问不同的资源。

1.3 授权的数据模型RBAC

在权限管理中使用最多的还是功能权限管理中的基于角色访问控制(RBAC,Role Based Access Control)。

在这里插入图片描述

当项目中需要使用权限管理的时候,我们可以选择自己去实现(前面的课程中所实现的 RBAC 系统),也可以选择使用第三方实现好的框架去实现,他们孰优孰劣这就需要看大家在项目中具体的需求了。

实现权限管理系统必备的功能:

1.权限管理(自定义权限注解/加载权限)
2.角色管理(新增/编辑/删除/关联权限)
3.用户管理(新增/编辑/删除/关联用户)
4.登录功能(定义登录拦截器/登录逻辑实现/登出功能)
5.权限拦截(定义权限拦截器/拦截逻辑实现)
1.3.1 基于角色的访问控制

RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

在这里插入图片描述

根据上图中的判断逻辑,授权代码可表示如下:

if(主体.hasRole("总经理角色id")){
查询工资
}

如果上图中查询工资所需要的角色变化为总经理和部门经理,此时就需要修改判断逻辑为“判断用户的角色是否是总经理或部门经理”,修改代码如下:

if(主体.hasRole("总经理角色id") || 主体.hasRole("部门经理角色id")){
查询工资
}

根据上边的例子发现,当需要修改角色的权限时就需要修改授权的相关代码,系统可扩展性差。

1.3.2 基于资源的访问控制

RBAC基于资源的访问控制(Resource-Based Access Control)是按资源(或权限)进行授权,比如:用户必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下:

在这里插入图片描述

根据上图中的判断,授权代码可以表示为:

if(主体.hasPermission("查询工资权限标识")){
查询工资
}

优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也不需要修改授权代码,系统可扩展性强。

1.4 权限管理框架

框架能帮我们解决权限管理系统中的哪些问题呢?

功能权限框架能做的事情
权限管理×
角色管理×
用户管理×
登录功能√ (密码加密、验证码、记住我)
权限拦截√(内置很多的拦截器、提供标签/注解/编程方式进行权限认证)

这里我们介绍两种常用的权限管理框架:

1.4.1 Apache Shiro

Apache Shiro 是一个强大且易用的 Java 安全框架,使用 Apache Shiro 的人越来越多,它可实现身份验证、授权、密码和会话管理等功能。

1.4.2 Spring Security

Spring Security 也是目前较为流行的一个安全权限管理框架,它与 Spring 紧密结合在一起。

1.4.3 Shiro 和 Spring Security 比较

Shiro与spring security最大的区别是shiro为一个独立的模块,使用灵活,但会造成侵入式的设计;而spring security依赖于spring的过滤器代理机制,与spring绑定,不够灵活,但不是侵入式的。此外,shiro与spring security都支持密码加密,shiro支持会话管理,spring security提供对常见漏洞的保护(例如CSRF)

二、Spring Security 认证与授权

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。由于它是Spring生态系统中的一员,因此它伴随着整个Spring生态系统不断修正、升级,在Spring boot项目中加入Springsecurity更是十分简单,使用Spring Security 减少了为企业系统安全控制编写大量重复代码的工作。

2.1 环境准备

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <!--spring security 组件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--web 组件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- test 组件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

创建 application.properties和启动类

启动类

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

创建 Controller

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello(String name){
        return "操作成功";
    }
}

启动项目:
观察控制台会生成一个密码,默认账号是 user

Using generated security password: a6c875c9-9b2e-4df9-a517-56c571258b01

在浏览器访问资源: http://localhost:8080/hello?name=zhangsan, 会发现访问资源被拦截了,springSecurity默认提供认证页面,不需要额外开发。

2.2 认证

2.2.1 安全配置

Spring security提供了用户名密码登录、退出、会话管理等认证功能,只需要配置即可使用。
在config包下定义WebSecurityConfig,安全配置的内容包括:用户信息、密码编码器、安全拦截机制。

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
//配置用户信息服务
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    //配置安全拦截机制
    protected void configure(HttpSecurity http) throws Exception {
        http
                //配置权限
                .authorizeRequests()
                .antMatchers("/hello").permitAll()
                .anyRequest().authenticated()
         http.formLogin()
                //登录成功后调整页面
                successForwardUrl("/main");
    }
}

controller:

@RequestMapping("/main")
public String main(String name){
    return "redirect:/main.html";
}

main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
我是主界面
</body>
</html>

在userDetailsService()方法中,我们返回了一个UserDetailsService给spring容器,Spring Security会使用它来获取用户信息。我们暂时使用InMemoryUserDetailsManager实现类,并在其中分别创建了zhangsan、lisi两个用
户,并设置密码和权限。

而在configure()中,我们通过HttpSecurity设置了安全拦截规则,其中包含了以下内容:
(1)url匹配/hello的资源,放行。
(2)其他url都需要认证。
(3)开启表单提交认证,登录成功会跳转到/main路径。

2.2.2 自定义登录界面

在 static 中创建一个登录界面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org/" lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="/login" method="post">
    用户名:<input type="text" name="username"> <br>
    密码:<input type="text" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

在配置类中 加入:

 protected void configure(HttpSecurity http) throws Exception {
        http
                //配置权限
                .authorizeRequests()
                .antMatchers("/hello").authenticated()
                .anyRequest().permitAll()
           .and()
                //配置表单登录
                .formLogin().
                    loginPage("/login.html"). 
                    loginProcessingUrl("/login").
                    successForwardUrl("/main");
    }

而在configure()中,我们通过HttpSecurity设置了安全拦截规则,其中包含了以下内容:
(1) 登录界面为 static下的 login.html
(2)制定表单登录的 url 路径

在访问的时候就可以看到如下界面:

在这里插入图片描述

2.2.3 403 问题解决

输入账号和密码,点击登录,报错:

在这里插入图片描述

问题:
Spring security为防止CSRF(Cross-site request forgery跨站请求伪造)的发生,限制了除了get以外的大多数方法。

在这里插入图片描述

解决
屏蔽CSRF控制,即spring security不再限制CSRF。
配置WebSecurityConfig

@Override
protected void configure(HttpSecurity http) throws Exception {
  //屏蔽CSRF控制,即spring security不再限制CSRF      
  http.csrf().disable() 
...
}
2.2.4 修改登录参数名

默认的账号和密码的参数名为: username,password , 必须对应上后台才可以进行正常登录, 当然在 SpringSecurity 中也是提供修改参数名字的

配置:

  .formLogin().
   							//自定义登录页面
                loginPage("/loginPage").
               //当发现/login时认为是登录,必须和表单提交的地址一样。
                loginProcessingUrl("/login").
                //登录成功后调整页面
                successForwardUrl("/main").
                //登录失败后跳转页面
                .failureForwardUrl("/toError");
                //自定义账号密码参数名
                usernameParameter("uname").
                passwordParameter("passwd");

测试方式: 在界面中修改参数名,以后看是否能登录,登录不了以后加上配置重启在进行登录,登录成功则验证配置生效。

2.2.5 登出配置

Spring security默认实现了logout退出,访问/logout,果然不出所料,退出功能Spring也替我们做好了。

在 Spring Security 中也可以自定义登录操作
配置如下:

.and() 
.logout() 
.logoutUrl("/logout")
.logoutSuccessUrl("/logoutSuccess");

controller 代码

@RequestMapping("/logoutSuccess")
public String logout1(){
    return "logout";
}

界面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
登出界面
</body>
</html>
2.2.6 自定义登录成功处理器

登录成功的successForwardUrl底层做的是请求转发的动作,在前后端分离的项目距中是没有办法使用的。

代码:

public class MyAuthenticationSuccessHandler
        implements   {
    private String url;
    public MyAuthenticationSuccessHandler(String url) {
        this.url = url;
    }
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication)
            throws IOException, ServletException {
        User user = (User)authentication.getPrincipal();
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getAuthorities());
        response.sendRedirect(this.url);
    }
}

配置:

   http .formLogin().
                    loginPage("/login.html").
                    loginProcessingUrl("/login").
                    successHandler(new SuccessAuthenticationSuccessHandler("http://www.baidu.com")).

successHandler:当登录成功以后交给我们自己定义的处理器进行处理

2.2.7 自定义登录失败处理器

代码:

public class FailAuthenticationFailureHandler implements AuthenticationFailureHandler {
    private String url;

    public FailAuthenticationFailureHandler(String url) {
        this.url = url;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.sendRedirect(url);

    }
}

配置:

http.formLogin().
                    loginPage("/login.html").
                    loginProcessingUrl("/login").
                    successHandler(new SuccessAuthenticationSuccessHandler("http://www.baidu.com")).
                    failureHandler(new FailAuthenticationFailureHandler("http://www.baidu.com")).

failureHandler: 当登录失败交给我们自己定义的处理器处理

2.3 授权

2.3.1 配置方式授权

实现授权需要对用户的访问进行拦截校验,校验用户的权限是否可以操作指定的资源,Spring Security默认提供授权实现方法。

在LoginController添加/r/r1或/r/r2

   @GetMapping("/r/r1")
    public String r1() {
        return " 访问资源1";
    }

    @GetMapping("/r/r2")
    public String r2() {
        return " 访问资源2";
    }

在安全配置类WebSecurityConfifig.java中配置授权规则:

.antMatchers("/r/r1").hasAuthority("p1") .antMatchers("/r/r2").hasAuthority("p2")

配置解释:

  • .antMatchers(“/r/r1”).hasAuthority(“p1”)表示:访问/r/r1资源的 url需要拥有p1权限。
  • .antMatchers(“/r/r2”).hasAuthority(“p2”)表示:访问/r/r2资源的 url需要拥有p2

测试:

1、登录成功

2、访问/r/r1和/r/r2,有权限时则正常访问,否则返回403(拒绝访问)

注: 这种方式需要在 SpringSecurityConfig 中进行配置是有一定缺陷的, 如果后台有几百个接口每一个都需要在这一个方法中进行配置,会特别混乱,SpringSecurity中还提供了另外一种方式为注解方式这种用的会更多一些,当然这里说用的多一些不代表出去做的项目就一定会用注解方式。

注意:规则的顺序是重要的更具体的规则应该先写。

比如:

.antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/admin/login").permitAll()

这样写/admin/login 也会被拦截.

应该顺序调转

.antMatchers("/admin/login").permitAll() .antMatchers("/admin/**").hasRole("ADMIN")
2.3.2 常见配置

保护URL常用的方法有:

authenticated() 保护URL,需要用户登录

permitAll() 指定URL无需保护,一般应用与静态资源文件

hasRole(String role) 限制单个角色访问,角色将被增加 “ROLE_” .所以”ADMIN” 将和 “ROLE_ADMIN”进行比较.

hasAuthority(String authority) 限制单个权限访问

**hasAnyRole(String… roles)**允许多个角色访问.

hasAnyAuthority(String… authorities) 允许多个权限访问.

access(String attribute) 该方法使用 SpEL表达式, 所以可以创建复杂的限制.

hasIpAddress(String ipaddressExpression) 限制IP地址或子网

2.3.3 注解授权

现在我们已经掌握了使用如何使用 http.authorizeRequests() 对web资源进行授权保护,从Spring Security2.0版本开始,它支持服务层方法的安全性的支持。本节学习@PreAuthorize,@PostAuthorize, @Secured三类注解。

我们可以在任何 @Configuration 实例上使用 @EnableGlobalMethodSecurity 注释来启用基于注解的安全性。

以下内容将启用Spring Security的 @Secured 注释。

配置:

@EnableGlobalMethodSecurity(securedEnabled = true)

响应代码:

@GetMapping("/r/r1")
@Secured("ROLE_HR")
@ResponseBody
public String r1() {
    return " 访问资源1";
}

@GetMapping("/r/r2")
@ResponseBody
@Secured("ROLE_ADMIN")
public String r2() {
    return " 访问资源2";
}

注: @Secured 一般是基于角色访问控制才用到。

以下内容将启用Spring Security的 @PreAuthorize 注解。

配置:

@EnableGlobalMethodSecurity(prePostEnabled = true)

相应 java 代码:

    @PreAuthorize("hasAnyAuthority('p1')")
    @GetMapping(value = "/r/r1")
    public String r1() {
        return " 访问资源1";
    }

    @GetMapping(value = "/r/r2")
    @PreAuthorize("hasAnyAuthority('p2')")
    public String r2() {
        return " 访问资源2";
    }

注:@PreAuthorize 一般是基于权限访问控制,用的也是比较多的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1034699.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Simple Factory 简单工厂模式简介与 C# 示例【创建型3.1】【设计模式来了_3.1】

〇、简介 1、什么是简单工厂模式&#xff1f; 一句话解释&#xff1a; 客户类和工厂类严格分工&#xff0c;客户类只需知道怎么用&#xff0c;处理逻辑交给工厂类。 简单工厂模式&#xff08;Simple Factory Pattern&#xff09;是日常开发中常用的设计模式。其是一种简单的创…

【WSN】基于蚁群算法的WSN路由协议(最短路径)消耗节点能量研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

postgresql-存储过程

postgresql-存储过程 简述PL/pgSQL 代码块结构示例嵌套子块 声明与赋值控制结构IF 语句CASE 语句简单case语句搜索 CASE 语句 循环语句continuewhilefor语句遍历查询结果 foreach 游标游标传参 错误处理报告错误和信息检查断言 捕获异常自定义函数重载VARIADIC 存储过程示例事务…

Opencv-python去图标与水印方案实践

RGB色彩模式是工业界的一种颜色标准&#xff0c;是通过对红&#xff08;R&#xff09;、绿&#xff08;G&#xff09;、蓝&#xff08;B&#xff09;三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的&#xff0c;RGB即是代表红、绿、蓝三个通道的颜色&#xff…

【虚拟化】虚拟机vcpu绑核物理机

文章目录 一、NUMA二、虚拟机xml配置解析 参考文章 第一篇&#xff1a;KVM虚拟化CPU技术总结 第二篇&#xff1a;虚机cpu和mem的配置&#xff08;cputune和numatune&#xff09; 第三篇&#xff1a;libvirt 中cpu, numa 的配置 第四篇&#xff1a;如何提高虚拟机性能&#xff1…

最新研究综述——探索基础模型中的“幻觉”现象

深度学习自然语言处理 原创作者&#xff1a;Winnie “幻觉”问题即模型生成的内容可能包含虚构的信息。它不仅在大语言模型(LLMs)中存在&#xff0c;也存在于图像、视频和音频等其他一系列基础模型中。 针对这一问题&#xff0c;一篇最近的综述论文对目前所有基础模型的“幻觉”…

树、二叉树、堆及其应用(堆排序、top-k问题)

目录 树的概念与结构 概念&#xff1a; 与树相关的概念: 树的表示&#xff1a; 二叉树 概念&#xff1a; 特殊的二叉树&#xff1a; 二叉树性质&#xff1a; 二叉树的存储结构&#xff1a; 堆 堆的概念: 堆的实现&#xff1a; 堆的创建&#xff1a; 堆的插入&#xff1a; 堆的删…

linux、windows的pip一键永久换源[清华源、中科大、豆瓣、阿里云]

前言 本文概述&#xff1a;linux、windows操作系统一键将pip下载源永久设置为国内下载源&#xff0c;避免了使用临时源需要到处找镜像地址的麻烦。 作者介绍&#xff1a;作者本人是一名人工智能炼丹师&#xff0c;目前在实验室主要研究的方向为生成式模型&#xff0c;对其它方向…

stm32_标准库_中断_按键点灯|蜂鸣器

配置流程 需要对AFIO、EXTI、NVIC、GPIOB进行配置形成通路将中断连接至CPU APB2总线连接的寄存器 LED灯代码 #include "stm32f10x.h" // Device header #include "Delay.h"GPIO_InitTypeDef GIPO_InitStruct;//结构体配置GPIO EXTI_InitTypeDef EXTI_…

海普纯化产品-多肽固相合成载体

#海普纯化产品-多肽固相合成载体 多肽固相合成法被广泛应用于多肽和蛋白质的研究领域&#xff0c;特别是短多肽的合成&#xff0c;随着全新化学结构的新药开发难度加大&#xff0c;投入费用逐年增高&#xff0c;多肽类药物的开发逐渐成为医药领域的新热点。 多肽类药物合成技术…

【Redis】专栏合集,从入门到高级业务场景实战

作者简介 目录 1.概述 2.下载安装 3.基础操作 4.集群 5.实战场景 1.概述 诸如数mysql、Oracle之类的关系型数据库或者NTFS、HDFS之类的文件存储系统&#xff0c;其本质上数据都是存在磁盘上的。这是现代计算机体系架构的架构所决定的&#xff0c;要持久化存储的数据都会落…

一文带你实现从PDF到Word文件的相互转换

一文带你实现从PDF到Word文件的相互转换 01. 前期准备 模块安装 pip install pdf2docx or使用国内镜像源进行安装&#xff08;清华的镜像源&#xff09; pip install pdf2docx -i https://pypi.tuna.tsinghua.edu.cn/simple 02. 模块介绍 pdf2docx是一个Python模块&#xff0…

SpringBoot源码解析: 从手写一个简易版SpringBoot开始

IDE&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 Maven版本&#xff1a;apache-maven-3.6.3 文章目录 真正的SpringBoot手写一个简易版的springboot一. 准备1.1 准备自定义的springboot模块1.2 准备用户模块User 二. 运行测试2.1 第一次运…

如何快速上传批量剪辑的素材?附素材水印、字幕去除教程

想要快速上传网络素材&#xff0c;大家可以使用超级编导批量剪辑软件。 首先进入超级编导官网&#xff0c;免费下载超级编导超级编导软件&#xff0c;可以在官网中添加产品顾问&#xff0c;获取试用账号&#xff0c;登录即可开始使用。 超级编导支持批量上传URL链接到后台&…

Manifest merger failed

编译报错&#xff1a;Manifest merger failed with multiple errors 定位编译错误&#xff1a;java.lang.RuntimeException: Manifest merger failed with multiple errors 近日&#xff0c;项目中需要引入一个module。在成功导入后&#xff0c;添加依赖到主模块上&#xff0c…

【RocketMQ】(六)顺序消息实现原理

全局有序 在RocketMQ中&#xff0c;如果使消息全局有序&#xff0c;可以为Topic设置一个消息队列&#xff0c;使用一个生产者单线程发送数据&#xff0c;消费者端也使用单线程进行消费&#xff0c;从而保证消息的全局有序&#xff0c;但是这种方式效率低&#xff0c;一般不使用…

955. 删列造序 II;1838. 最高频元素的频数;1392. 最长快乐前缀

955. 删列造序 II 核心思想&#xff1a;我们可以按照一行一行排列strs&#xff0c;删除索引序列就代表删除某一列&#xff0c;那么我们如何判断一列是否应该删除呢&#xff0c;我们可以从反方向思考&#xff0c;应该保留那些列呢&#xff1f;从第一列开始&#xff0c;如果它不…

WebGIS面试题(浙江中海达)

1、Cesium中有几种拾取坐标的方式&#xff0c;分别介绍 Cesium是一个用于创建3D地球和地理空间应用的JavaScript库。在Cesium中&#xff0c;你可以使用不同的方式来拾取坐标&#xff0c;以便与地球或地图上的对象进行交互。以下是Cesium中几种常见的拾取坐标的方式&#xff1a…

jdk21(最新版) download 配置(linux window mac)

download 直达链接 jdk21,17 # wget https://download.oracle.com/java/20/latest/jdk-21_linux-x64_bin.deb # 选择你需要的包类似格式替换包的名称就可以实现终端下载jdk下载登录/oracle账号 下载jdk有可能存在要求登录帐号的情况 # 好心人的帐号 账号&#xff1a; 599…

C语言内存函数的使用、剖析及模拟实现

目录 一、内存拷贝函数——memcpy 1.函数声明&#xff1a; 注意&#xff1a; 2.函数使用用例&#xff1a; 3.memcpy函数的模拟实现&#xff1a; 二、内存拷贝函数2——memmove 1.函数声明&#xff1a; 2.memmove函数的模拟实现 三、内存比较函数——memcmp 1.函数声明…