spring security OAuth2 搭建资源服务器以及授权服务器/jdbc/jwt两种方案

一、认证服务器基于jdbc方式

如果不懂请移步上一篇文章:Spring security OAuth2 授权服务器搭建-CSDN博客

    在上一篇文章中,TokenStore的默认实现为 InHenoryTokenStore 即内存存储,对于 CLient 信息,userDetaitsServce 接负责从存储库中读取数据,在上面的案例中默认使用的也是 InHemoryCLientDetalsService 实现类。如果要想使用政据库存储,只要提供这些接口的实现类即可,而框架已经为我们写好 dbekenStore 和 dbcclientDetatsService

1.1 数据库所需表

我们使用spring security 自带的sql 语句生成对应的表结构,我们关注一下 oauth_client_details  这个表,这个表用来储存用户密钥信息,相当于前面的这个地方

clients.inMemory() //基于内存,后期可以入库查出来
        .withClient("client-lq")
        .secret(passwordEncoder.encode("secret-lq"))

-- 写入客户端信息
INSERT INTO oauth_client_details
VALUES ('client-id', NULL,'$2a$10$2McX6ml8CVK3RUNpLkX1zeQeNkrEvLCPOJ2hhpG18XMeIMbJWIJnK', 'read', 'authorization_code,refresh_token','http://www.baidu.com',NULL, NULL, NULL, NULL,NULL);

client_secret 需要加密存进去,否则会报不成功

--  structure for clientdetails

DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE clientdetails (
  appId                  varchar(256) NOT NULL,
  resourceIds            varchar(256)  DEFAULT NULL,
  appSecret              varchar(256)  DEFAULT NULL,
  scope                  varchar(256)  DEFAULT NULL,
  grantTypes             varchar(256)  DEFAULT NULL,
  redirectUrl            varchar(256)  DEFAULT NULL,
  authorities            varchar(256)  DEFAULT NULL,
  access_token_validity  int(11)       DEFAULT NULL,
  refresh_token_validity int(11)       DEFAULT NULL,
  additionalInformation  varchar(4096) DEFAULT NULL,
  autoApproveScopes      varchar(256)  DEFAULT NULL,
  
  PRIMARY KEY (appId)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;



-- Table structure for oauth_access_token
DROP TABLE IF EXISTS oauth_access_token;
CREATE TABLE oauth_access_token  (
  token_id varchar(256) DEFAULT NULL,
  token blob,
  authentication_id varchar(256) NOT NULL,
  user_name     varchar(256) DEFAULT NULL,
  client_id     varchar(256) DEFAULT NULL,
  authentication blob ,
  refresh_token  varchar(256) DEFAULT NULL,
  PRIMARY KEY (authentication_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


--  Table structure for oauth_approvals

DROP TABLE IF EXISTS  oauth_approvals;
CREATE TABLE oauth_approvals (
  userId varchar(256) DEFAULT NULL,
  clientId varchar(256) DEFAULT NULL,
  scope varchar(256) DEFAULT NULL,
  status varchar(10) DEFAULT NULL,
  expiresAt timestamp NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
  lastModifiedAt date null
)ENGINE=InnODB DEFAULT CHARSET=utf8mb4;



-- Table structure for oauth_client_details
DROP TABLE IF EXISTS oauth_client_details;
CREATE TABLE oauth_client_details (
  client_id               varchar(256) NOT NULL,
  resource_ids            varchar(256)  DEFAULT NULL,
  client_secret           varchar(256)  DEFAULT NULL,
  scope                   varchar(256)  DEFAULT NULL,
  authorized_grant_types  varchar(256)  DEFAULT NULL,
  web_server_redirect_uri varchar(256)  DEFAULT NULL,
  authorities             varchar(256)  DEFAULT NULL,
  access_token_validity   int(11)       DEFAULT NULL,
  refresh_token_validity  int(11)       DEFAULT NULL,
  additional_information  varchar(4096) DEFAULT NULL,
  autoapprove             varchar(256)  DEFAULT NULL,
  PRIMARY KEY (client_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;





--  Table structure for oauth_clienttoken
DROP TABLE IF EXISTS  oauth_client_token;
CREATE TABLE oauth_client_token (
  token_id          varchar(256) DEFAULT NULL,
  token             blob,
  authentication_id varchar(256) NOT NULL,
  user_nam          varchar(256) DEFAULT NULL,
  client_id         varchar(256) DEFAULT NULL,
  PRIMARY KEY (authentication_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


--  Table structure for oauth_code
DROP TABLE IF EXISTS oauth_code;
CREATE TABLE oauth_code (
  code           varchar(256) DEFAULT NULL,
  authentication blob
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


-- Table structure for oauth_refresh_token
DROP TABLE IF EXISTS  oauth_refresh_token;
CREATE TABLE oauth_refresh_token (
token_id varchar(256) DEFAULT NULL,
token blob,
authentication blob
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


select * from  oauth_refresh_token;

-- 写入客户端信息
INSERT INTO oauth_client_details
VALUES ('client-id', NULL,'$2a$10$2McX6ml8CVK3RUNpLkX1zeQeNkrEvLCPOJ2hhpG18XMeIMbJWIJnK', 'read', 'authorization_code,refresh_token','http://www.baidu.com',NULL, NULL, NULL, NULL,NULL);

1.2 认证服务器配置

@Configuration
@EnableAuthorizationServer  //指定当前应用为授权服务器
public class JdbcAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManager authenticationManager;

    private final DataSource dataSource;

    @Autowired
    public JdbcAuthorizationServerConfig(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService, AuthenticationManager authenticationManager, DataSource dataSource) {
        this.passwordEncoder = passwordEncoder;
        this.userDetailsService = userDetailsService;
        this.authenticationManager = authenticationManager;
        this.dataSource = dataSource;
    }


    @Bean
    public ClientDetailsService clientDetails() {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        // 使用哪一种加密方式
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    // 用来配置授权服务器可以为哪些客户端授权   client_id,secret  redirect_url
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());// 使用jdbc存储
    }


    @Bean
    public TokenStore tokenStore() {
        JdbcTokenStore jdbcTokenStore = new JdbcTokenStore(dataSource);
        return jdbcTokenStore;
    }


    // 配置令牌存储

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
       endpoints.authenticationManager(authenticationManager);
       endpoints.tokenStore(tokenStore());// 配置令牌存储为数据库存储
        endpoints.userDetailsService(userDetailsService);

        // 配置 tokenServices 参数
        DefaultTokenServices tokenServices = new DefaultTokenServices();//修改默认令牌生成服务
        tokenServices.setTokenStore(endpoints.getTokenStore());// 基于数据库令牌生成
        tokenServices.setSupportRefreshToken(true);//是否支持刷新令牌
        tokenServices.setReuseRefreshToken(true); // 是否重复使用刷新令牌( 直到过期


        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());//设置客户端信息
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());//用来控制令牌存储增强策略
        // 访问令牌的默认有效期( 以秒为单位)。过期的令牌为零或负数。
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30天

        endpoints.tokenServices(tokenServices);// 使用配置令牌服务

    }

}

1.3 security 配置

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        inMemoryUserDetailsManager.createUser(User.withUsername("root").password(passwordEncoder().encode("123456")).roles("ADMIN").build());
        return inMemoryUserDetailsManager;
    }


    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()// 开启表单登录
                .and()
                .csrf().disable();
    }
}

1.4 数据库配置

@Configuration
@Slf4j
public class MysqlDsConfig {

    /**
     *  配置数据源
     * @return
     */
    @Primary
    @Bean
    public DataSource mysqlDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/oauth3");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("12345678");
        return dataSource;
    }

}

1.5 启动测试

当我们刷新token的时候 oauth_refresh_token 会有值进去,使用 apifox 测试令牌


二、资源服务器基于 jdbc 方式

需要连接数据库信息,跟认证服务器数据源一样,因为资源服务器最终也要去读取表信息

所需的依赖,资料参考 哔哩博主,不良人编程 

<properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-resource-server</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!-- 数据源支持 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

2.1  资源服务器配置

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {


    private final DataSource dataSource;

    @Autowired
    public ResourceServerConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore());
    }


    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }
}

2.2 测试controller

@RestController
public class HelloController {



    @RequestMapping("hello")
    public String hello() {
        System.out.println("hello resource server ");

        return "hello resource server";
    }
}

2.3 测试结果

当我们这个时候访问的时候就需要带上 Authorization:Bearer   参数,否则提示 Full authentication is required to access this resource 

curl -H "Authorization:Bearer token 对应的值" http://127.0.0.1:8083/hello


三、认证服务器基于 jwt 方式

jwt 分为三部分,用三个点隔开  ,第一部分标识 header 标识用那种加密方式;中间部分为subject 主体信息,可以是用户信息,也可以是一个json数据;最后一部分为sign 签名信息,这个信息需要设置足够复杂才能被破解;

3.1 认证服务器配置

可能在疑问,为什么还是要用数据库,这次我们只需要从库里面读取一次用户信息,只是使用一张表 oauth_client_details 其他信息都不会使用,jwt 有自动过期时间

@Configuration
@EnableAuthorizationServer  //指定当前应用为授权服务器
public class JwtAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManager authenticationManager;

    private final DataSource dataSource;

    @Autowired
    public JwtAuthorizationServerConfig(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService, AuthenticationManager authenticationManager, DataSource dataSource) {
        this.passwordEncoder = passwordEncoder;
        this.userDetailsService = userDetailsService;
        this.authenticationManager = authenticationManager;
        this.dataSource = dataSource;
    }


    @Bean
    public ClientDetailsService clientDetails() {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        // 使用哪一种加密方式
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    // 用来配置授权服务器可以为哪些客户端授权   client_id,secret  redirect_url
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());// 使用jdbc存储
    }



    // 使用同一个秘钥编码
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("12332111");
        return jwtAccessTokenConverter;
    }


    // jwt生成方式生成令牌
    @Bean
    public TokenStore tokenStore() {
        JwtTokenStore jdbcTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jdbcTokenStore;
    }


    // 配置令牌存储

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
       endpoints.authenticationManager(authenticationManager);
       endpoints.tokenStore(tokenStore())// 配置jwt令牌存储为数据库存储
         .accessTokenConverter(jwtAccessTokenConverter());
    }


    //http://127.0.0.1:8082/oauth/authorize?client_id=client&response_type=code&redirect_url=https://www.baidu.com

}

3.2 测试效果

1、页面授权效果有所不同,获取code

2、返回的令牌有所不同

api fox 效果

四、资源服务器基于 jwt 方式

    我们只需要跟认证服务器加密规则设置相同即可,因为jwt 自带加密算法,就类似于我们两个定义了一套rsa的秘钥,我们加密规则相同,约定一个签名戳,这个别人获取不到,你请求过来的时候我验证合法性,所以就不需要用到数据库了

4.1 资源服务器配置

@Configuration
@EnableResourceServer
public class JwtResourceServerConfig extends ResourceServerConfigurerAdapter {


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore());
    }

    // jwt生成方式生成令牌
    @Bean
    public TokenStore tokenStore() {
        JwtTokenStore jdbcTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jdbcTokenStore;
    }


    // 使用同一个秘钥编码
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("12332111");
        return jwtAccessTokenConverter;
    }
}

4.2 测试效果

curl -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjYzMTY1MTAsInVzZXJfbmFtZ
SI6InJvb3QiLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjEyYTgyMmNhLWQ3MTgtNDE1Yy1hYWQ3LTA5ZjIxODNjNzY0YiIsImNsaWVudF9pZCI6ImNsaWVudC1scSIsInNjb3BlI
jpbInJlYWQ6dXNlciJdfQ.myzFE0VJOhlFLOsddBknOeB6Y499RwZ1X2zTM4PxC00" http://127.0.0.1:8083/hello
 

Z1X2zTM4PxC00" http://127.0.0.1:8083/hello
hello resource server
D:\java-tool\idea-project\spring-security-study>
 

使用apifox 测试

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mqtt整体了解

整个系统的分布及功能 参考太极创客视频 整体分为三部分&#xff1a; 发布&#xff1a;实时发送到云平台&#xff1b;实现主体是传感器或被控对象 订阅&#xff1a;得到能够访问发布信息&#xff1b;主体是有查看和控制权限的对象 云平台&#xff1a;可以理解为有控制订阅者权…

Python 爬虫入门 - Request 静态页面数据获取

在现代 Web 开发中,HTTP 请求(Request)是与服务器进行通信的核心操作。无论是在前端还是后端开发中,数据的获取、传递以及处理都离不开请求的应用。特别是在静态页面的数据获取中,使用请求可以将页面变得更加动态和互动,从而大大提升用户体验,使得页面内容更加丰富和灵活…

MySQL_SQLYog简介、下载及安装(超详细)

课 程 推 荐我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448;入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448;虚 拟 环 境 搭 建 &#xff1a;&#x1…

行人动作行为识别系统源码分享

行人动作行为识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

Halo 开发者指南——项目运行、构建

准备工作 环境要求 OpenJDK 17 LTSNode.js 20 LTSpnpm 9IntelliJ IDEAGitDocker&#xff08;可选&#xff09; 名词解释 工作目录 指 Halo 所依赖的工作目录&#xff0c;在 Halo 运行的时候会在系统当前用户目录下产生一个 halo-next 的文件夹&#xff0c;绝对路径为 ~/ha…

网络高级项目( 基于webserver的工业数据采集和控制项目)

目录 一、项目要求&#xff1a; 二、演示效果&#xff1a; 设备端&#xff1a; Modbus用户控制端&#xff1a; 服务器端&#xff1a; 网页端&#xff1a; 三、 项目代码&#xff1a; Modbus用户控制端代码&#xff1a; 服务器端代码&#xff1a; 网页端代码&#xff1…

VirtualBox Install MacOS

环境搭建 git clone https://github.com/myspaghetti/macos-virtualbox 脚本配置 修改macos-guest-virtualbox.sh部分内容为 vm_name"macOS" # name of the VirtualBox virtual machine macOS_release_name"Catalina" # install &quo…

股指期货的详细玩法功能与应用解析

股指期货作为一种重要的金融衍生工具&#xff0c;为投资者提供了多样化的投资和风险管理手段。本文将详细探讨股指期货的三大主要功能&#xff1a;风险规避、价格发现和资产配置。 第一&#xff0c;风险规避功能 1.套期保值&#xff1a;股指期货的风险规避功能主要通过套期保值…

外观模式详解:如何为复杂系统构建简洁的接口

&#x1f3af; 设计模式专栏&#xff0c;持续更新中 欢迎订阅&#xff1a;JAVA实现设计模式 &#x1f6e0;️ 希望小伙伴们一键三连&#xff0c;有问题私信都会回复&#xff0c;或者在评论区直接发言 外观模式 外观模式&#xff08;Facade Pattern&#xff09;为子系统中的一组…

智能 Uber 发票 PDF 合并工具

在现代商务出行中&#xff0c;尤其是在跨国出差中&#xff0c;处理和整合大量 Uber 发票已成为一项不小的挑战。手动整理和合并这些发票不仅耗时&#xff0c;还容易出错。作为开发者&#xff0c;为什么不开发一个自动化工具&#xff0c;将这些任务交给代码来完成呢&#xff1f;…

成型的程序

加一个提示信息 加上python 常用的包 整个程序打包完 250M 安装 960MB matplot numpy pandas scapy pysearial 常用的包 (pyvisa)… … 啥都有 Python 解释器组件构建 要比 lua 容易的多 &#xff08;C/Rust 的组件库)

钢材表面缺陷数据集以coco格式做好了数据集的划分,1200张训练集,600张验证集,对应的json文件也在里面

钢材表面缺陷数据集 以coco格式做好了数据集的划分&#xff0c;1200张训练集&#xff0c;600张验证集&#xff0c;对应的json文件也在里面。 钢材表面缺陷检测数据集营销介绍 项目背景&#xff1a; 钢材作为工业生产的重要原材料之一&#xff0c;其表面质量直接影响到成品的性…

MySQL之安装与基础知识

目录 一&#xff1a;在centos7上安装MySQL数据库 1.卸载默认存在的环境 2.配置mysql的yum源 3. 安装MySQL 4.登录mysql 5.设置MySQL的配置文件 二&#xff1a;MySQL基础知识 1.什么是数据库 2.主流数据库 3.服务器&#xff0c;数据库&#xff0c;表关系及使用案例 4…

预训练发展

预训练发展 1.ELMo2.GPT3.Bert3.1Ernie-baidu3.2Ernie- Tsinghua 4.GPT25.UNILM6.Transformer-XL & XLNet6.1方案一6.2方案三 7.Roberta8.SpanBert8.1SBO简介&#xff1a; 9.ALBERT9.1方案一9.2方案二9.3方案三 10.T511.GPT312.从"续写"到"回答"12.1SF…

基于51单片机的直流数字电流表proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1adZbhgOBvvg0KsCO6_ZiAw 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

MySQL——数据类型(一)

目录 一、前言 二、数值类型 2.1 tinyint [unsigned] 2.1.1 插入合法数据 2.1.2 插入边界数据 2.1.3 插入不合法数据 2.1.4 结论 2.2 bit [n] 2.3 float [(m, d)] [unsigned] 2.3.1 float 特性 2.3.2 插入整数部分大于 m-d 的数字 2.3.3 插入小数部分大于 d 的数字…

【贪心算法】贪心算法

贪心算法简介 1.什么是贪心算法2.贪心算法的特点3.学习贪心的方向 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.什么是贪心算法 与其说是…

MYSQL数据库——MYSQL管理

MYSQL数据库安装完成后&#xff0c;自带四个数据库&#xff0c;具体作用如下&#xff1a; 常用工具 1.mysql 不是指mysql服务&#xff0c;而是指mysql的客户端工具 例如&#xff1a; 2.mysqladmin 这是一个执行管理操作的客户端程序&#xff0c;可以用它来检查服务器的配置和…

vs2022快捷键异常解决办法

安装了新版本的vs2022&#xff0c;安装成功后&#xff0c;发现快捷键发生异常&#xff0c;之前常用的快捷键要么发生改变&#xff0c;要么无法使用&#xff0c;比如原来注释代码的快捷键是ctrlec&#xff0c;最新安装版本变成了ctrlkc&#xff0c;以前编译代码的快捷键是F6或者…

算法入门-贪心1

第八部分&#xff1a;贪心 409.最长回文串&#xff08;简单&#xff09; 给定一个包含大写字母和小写字母的字符串 s &#xff0c;返回通过这些字母构造成的最长的回文串 的长度。 在构造过程中&#xff0c;请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串…