一步步入门搭建SpringSecurity OAuth2(密码模式)

2023-05-16

什么是OAuth2?

开放授权的一个标准,旨在让用户允许第三方应用去访问改用户在某服务器中的特定私有资源,而可以不提供其在某服务器的账号密码给到第三方应用

大概意思就是比如如果我们的系统的资源是受保护的,其他第三方应用想访问这些受保护的资源就要走OAuth2协议,通常是用在一些授权登录的场景,例如在其他网站上使用QQ,微信登录等。

OAuth2中的几个角色

用户:使用第三方应用(或者是我们自己的应用)的用户。

客户端:用户是用的第三方应用。

认证(授权)服务器:负责发放token的服务,以及想要访问受保护资源的客户端都需要向认证服务器注册信息。

资源服务器:受保护的API资源。

 可以通过一张图来说明这4个角色之间的关系:

 使用SpringSecurity OAuth2进行搭建认证服务器以及资源服务器

1.搭建认证服务器

现在的很多项目都是微服务架构的项目了,对于每一个提供API资源的微服务来说就是一个个的资源服务器,而认证服务器就是客户端访问每一个微服务的时候需要先去认证服务器中拿到token才能去访问。

每个微服务添加依赖:

        <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>

其中一个服务作为认证服务器,设置关于认证的配置:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 配置authenticationManager用于认证的过程
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager);
    }

    /**
     * 重写此方法用于声明认证服务器能认证的客户端信息
     * 相当于在认证服务器中注册哪些客户端(包括资源服务器)能访问
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("orderApp")  //声明访问认证服务器的客户端
                .secret(passwordEncoder.encode("123456"))  //客户端访问认证服务器需要带上的密码
                .scopes("read","write")  //获取token包含的哪些权限
                .accessTokenValiditySeconds(3600)  //token过期时间
                .resourceIds("order-service")  //指明请求的资源服务器
                .authorizedGrantTypes("password")  //密码模式
                .and()
                //资源服务器拿到了客户端请求过来的token之后会请求认证服务器去判断此token是否正确或者过期
                //所以此时的资源服务器对于认证服务器来说也充当了客户端的角色
                .withClient("order-service")
                .secret(passwordEncoder.encode("123456"))
                .scopes("read")
                .accessTokenValiditySeconds(3600)
                .resourceIds("order-service")
                .authorizedGrantTypes("password");
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("isAuthenticated()");
    }
}

首先加上@EnableAuthorizationServer注解声明这是一个认证服务器,然后在第二个重写的configure方法中去设置客户端的信息,相当于只有注册进来的客户端才能有权限访问认证服务器,客户端带着这些信息以及用户账号密码等信息向认证服务器发起请求,由于我们这里是密码模式(密码模式一般都是用在保护自己API安全的场景),当然要验证username和password了,而这些验证的过程就需要AuthenticationManager了(第一个重写的configure方法),而AuthenticationManager从哪里来的我们也是要设置的,新建一个配置类,该配置类是与SpringSecurity验证用户信息有关的:

@Configuration
@EnableWebSecurity
public class OAuth2AuthWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

UserDetailServiceImpl:

@Component
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    //认证的过程,由AuthenticationManager去调,从数据库中查找用户信息
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return User.withUsername(username)
                .password(passwordEncoder.encode("123456"))
                .authorities("ROLE_ADMIN")
                .build();
    }
}

启动应用,用postman访问http://localhost:9000/auth/token

username和password就是我们登陆的用户名和密码,如果和数据库中保存的用户名密码不一致就会认证不成功(UserDetailService里从数据库中根据传过来的username查找对应的密码与传过来的密码进行比对),grant_type为password,表明当前是密码模式,scope是申请的权限。

得到的响应,其中access_token就是我们认证成功后从认证服务器返回的token,expires_in就是过期剩余时间。每次请求资源服务器都要带上token,然后资源服务器再去请求认证服务器验证token是否正确,即在这个过程中,资源服务器的资源能否访问就取决于资源服务器信任的是哪个认证服务器。

1.2 把客户端信息与token保存在数据库中

此时认证服务器就完成了,不过上面我们客户端的信息和token是保存在内存中的,这明显不太适合在真实的使用中,我们可以将其保存在数据库中。

在数据库中创建对应的表:

create table oauth_client_details (
  client_id VARCHAR(256) PRIMARY KEY,
  resource_ids VARCHAR(256),
  client_secret VARCHAR(256),
  scope VARCHAR(256),
  authorized_grant_types VARCHAR(256),
  web_server_redirect_uri VARCHAR(256),
  authorities VARCHAR(256),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additional_information VARCHAR(4096),
  autoapprove VARCHAR(256)
);

create table oauth_access_token (
  token_id VARCHAR(256),
  token BLOB,
  authentication_id VARCHAR(256) PRIMARY KEY,
  user_name VARCHAR(256),
  client_id VARCHAR(256),
  authentication BLOB,
  refresh_token VARCHAR(256)
);

把客户端的信息添加到oauth_client_details这个表中:

添加数据库依赖:

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

修改认证服务器配置:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private DataSource dataSource;

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

    /**
     * 配置authenticationManager用于认证的过程
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                //设置tokenStore,生成token时会向数据库中保存
                .tokenStore(tokenStore())
                .authenticationManager(authenticationManager);
    }

    /**
     * 重写此方法用于声明认证服务器能认证的客户端信息
     * 相当于在认证服务器中注册哪些客户端(包括资源服务器)能访问
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource);
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("isAuthenticated()");
    }
}

此时再启动认证服务器的话当客户端发起令牌请求的时候就会把生成的令牌存储到数据库中,当认证服务器重启的时候同一个客户端再次请求就会把数据库中的token拿出来判断token是否已经超时,如果没有就直接返回给客户端,否则就重新生成token并且更新数据库中旧的token。

2.搭建资源服务器

新建一个maven项目,添加上OAuth2的依赖,加上关于资源服务器的配置:

@Configuration
@EnableResourceServer  //声明该服务是一个资源服务器
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        //声明该资源服务器的id,当请求过来是会首先判断token是否有访问该资源服务器的权限
        resources.resourceId("order-service");
    }

    /**
     * 设置访问权限需要重写该方法
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //访问post请求的接口必须要有write权限
                .antMatchers(HttpMethod.POST).access("#oauth2.hasScope('write')")
                //访问get请求的接口必须要有read权限
                .antMatchers(HttpMethod.GET).access("#oauth2.hasScope('read')");
    }
}

@Configuration
//资源服务器拿到token之后需要向认证服务器发出请求判断该token是否正确,开启SpringSecurity认证的过程
@EnableWebSecurity
public class OAuth2ResourceWebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Bean
    public ResourceServerTokenServices tokenServices() {
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        //认证时资源服务器就相当于客户端,需要向认证服务器声明自己的信息是否匹配
        remoteTokenServices.setClientId("order-service");
        remoteTokenServices.setClientSecret("123456");
        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9000/oauth/check_token");
        return remoteTokenServices;
    }


    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
        oAuth2AuthenticationManager.setTokenServices(tokenServices());
        return oAuth2AuthenticationManager;
    }
}

2.1 获取认证之后用户的信息

我们在资源服务器中的一些接口中可能会使用到认证用户的信息,而SpringSecurity OAuth2中也提供了相应的方法来获取,我们可以在接口参数中加上@AuthenticationPricipal注解,例如:

@PostMapping("/create")
    public OrderInfo order(@RequestBody OrderInfo orderInfo, @AuthenticationPrincipal String username){
        PriceInfo priceInfo = restTemplate.getForObject("http://localhost:9002/price/get", PriceInfo.class);
        orderInfo.setPrice(priceInfo.getPrice());
        System.out.println("order() username is ===================" + username);
        return orderInfo;
    }

因为token中包含username,默认是只能获取username的,而如果我们想要获取整个user对象的信息的话,则可能利用UserDetailService去数据库中根据username查询,修改下配置类:

@Configuration
//资源服务器拿到token之后需要向认证服务器发出请求判断该token是否正确,开启SpringSecurity认证的过程
@EnableWebSecurity
public class OAuth2ResourceWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public ResourceServerTokenServices tokenServices() {
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        //认证时资源服务器就相当于客户端,需要向认证服务器声明自己的信息是否匹配
        remoteTokenServices.setClientId("order-service");
        remoteTokenServices.setClientSecret("123456");
        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9000/oauth/check_token");
        remoteTokenServices.setAccessTokenConverter(getAccessTokenConverter());
        return remoteTokenServices;
    }

    //设置了AccessTokenConverter就能在认证之后通过token里面的username去调用UserDetailsService去数据库查找出完整的用户信息了
    private AccessTokenConverter getAccessTokenConverter() {
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
        userTokenConverter.setUserDetailsService(userDetailsService);
        accessTokenConverter.setUserTokenConverter(userTokenConverter);
        return accessTokenConverter;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
        oAuth2AuthenticationManager.setTokenServices(tokenServices());
        return oAuth2AuthenticationManager;
    }
}

添加UserDetailService之后,资源服务器在向认证服务器发送令牌认证成功之后会调用里面的loadUserByUsername方法并且返回user对象,接口方法参数此时就能获取到user对象了:

@PostMapping("/create")
    public OrderInfo order(@RequestBody OrderInfo orderInfo, @AuthenticationPrincipal User user){
        PriceInfo priceInfo = restTemplate.getForObject("http://localhost:9002/price/get", PriceInfo.class);
        orderInfo.setPrice(priceInfo.getPrice());
        System.out.println("order() user is ===================" + user);
        return orderInfo;
    }

如果我们想要这个对象中的某个属性也是可以的,在注解上使用el表达式即可:

@PostMapping("/create")
    public OrderInfo order(@RequestBody OrderInfo orderInfo, @AuthenticationPrincipal(expression = "#this.id") Long id){
        PriceInfo priceInfo = restTemplate.getForObject("http://localhost:9002/price/get", PriceInfo.class);
        orderInfo.setPrice(priceInfo.getPrice());
        System.out.println("order() useId is ===================" + id);
        return orderInfo;
    }

自此,一个简单的利用SpringSecurity OAuth2搭建的认证服务器和资源服务器就完成了。

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

一步步入门搭建SpringSecurity OAuth2(密码模式) 的相关文章

  • 深度学习 - 模型调优经历(1)

    模型调优经历 xff08 1 xff09 背景遇到问题思路解决办法 背景 样本规模及划分 bullet 二分类问题 xff0c 正负样本 1 1 xff0c 特征数 xff1a 20
  • Android SDK下载安装及配置教程

    让大家很方便的不用翻墙不用代理地就完成Android开发环境的搭建 总结起来 xff0c Android开发环境搭建可以分为以下四步 xff1a 第一步 安装JDK xff1b 略 第二步 安装Eclipse xff1b 略 第三步 下载并
  • (DFS)深度优先搜索算法详解

    背景 DFS 英文全称为 xff08 Depth First Search xff09 xff0c 中文简称深度优先搜索算法 xff0c 其过程为沿着每一个可能的路径向下进行搜索 xff0c 直到不能再深入为止 xff0c 并且每一个节点只
  • ovs-ofctl

    文章目录 64 toc ovs ofctl语法COMMANDSOpenFlow Switch Management Commands OpenFlow Switch Flow Table CommandsGroup Table Comman
  • 【牛客网华为机试】HJ46 截取字符串

    题目 描述 输入一个字符串和一个整数k xff0c 截取字符串的前k个字符并输出 本题输入含有多组数据 输入描述 xff1a 1 输入待截取的字符串 2 输入一个正整数k xff0c 代表截取的长度 输出描述 xff1a 截取后的字符串 示
  • 【牛客网华为机试】HJ50 四则运算

    题目 描述 输入一个表达式 xff08 用字符串表示 xff09 xff0c 求这个表达式的值 保证字符串中的有效字符包括 0 9 43 xff0c 且表达式一定合法 输入描述 xff1a 输入一个算术表达式 输出描述 xff1a 得到计算
  • 【机器学习实战 Task1】 (KNN)k近邻算法的应用

    1 背景 1 1 k近邻算法的概述 xff08 1 xff09 k近邻算法的简介 k 近邻算法是属于一个非常有效且易于掌握的机器学习算法 xff0c 简单的说就是采用测量不同特征值之间距离的方法对数据进行分类的一个算法 xff08 2 xf
  • 【Python量化分析100例】Day1-使用Tushare获取数据

    1 背景 Tushare平台是目前使用python学习量化投资比较好用的而且是免费的一个数据获取平台 主要实现对金融数据从数据采集 清洗加工 到 数据存储的过程 xff0c 能够为金融分析人员提供快速 整洁 和多样的便于分析的数据 xff0
  • python报错:TypeError: Cannot interpret ‘<attribute ‘dtype‘ of ‘numpy.generic‘objects>‘as a data type

    原因 xff1a pandas和matplotlib版本不匹配 方案1 conda更新一下所有包 conda update n base conda 方案2 pip 更新最新版pandas pip install pandas upgrad
  • 【阿里云天池算法挑战赛】零基础入门NLP - 新闻文本分类-Day2-数据读取与数据分析

    一 赛题解析 阿里云天池算法挑战赛 零基础入门NLP 新闻文本分类 Day1 赛题理解 202xxx的博客 CSDN博客 二 数据读取 下载完成数据后推荐使用anaconda xff0c python3 8进行数据读取与模型训练 首先安装需
  • 【阿里云天池算法挑战赛】零基础入门NLP - 新闻文本分类-Day3-基于机器学习的文本分类

    一 赛题解析 阿里云天池算法挑战赛 零基础入门NLP 新闻文本分类 Day1 赛题理解 202xxx的博客 CSDN博客 二 数据读取与数据分析 阿里云天池算法挑战赛 零基础入门NLP 新闻文本分类 Day2 数据读取与数据分析 202xx
  • 【清华大学】2021元宇宙研究报告

    关于元宇宙的详细介绍 xff1b 来源 xff1a 软件定义世界 xff08 SDX xff09
  • MAC OS M1 安装MySQL

    xff08 1 xff09 进入mysql官网下载mysql http www mysql com downloads xff08 2 xff09 选择对应下载版本 xff08 3 xff09 选择直接下载 xff08 4 xff09 下载
  • PX4开源飞控位置控制解析

    位置控制的总体思路为串级PID控制算法 xff0c 内环为对速度的PID控制 xff0c 外环为对位置的比例控制 xff0c 被控对象为四旋翼无人机 xff0c 被控量为四旋翼无人机输出的推力矢量和 xff0c 将测量到的位置与速度进行反馈
  • ONOS意图框架

    1 意图基本概念 Intent是用于描述应用需求的不可变模型对象 xff0c ONOS核心根据其改变网络行为 在最低级别上 xff0c 可以用以下方式描述意图 即意图的组成 xff1a 1 Network Resource xff1a 一组
  • prometheus-4种metric类型说明

    前提了解 prometheus中存储的数据都是时序型 xff0c 其数据模型都是如下 xff1a metric name label 61 value value timestamp 下文中说的数据类型只是客户端的封装 prometheus
  • ROS2 中常用坐标系

    ROS2 中常用坐标系 frame id xff1a 用来告诉你 xff0c 发布的数据是来自哪一个坐标系的 在使用ROS进行定位导航等操作时 xff0c 我们经常会遇到各种坐标系 每种坐标系都有明确的含义 理论上坐标系的名称可以是随意的
  • 国密SM2算法

    目录 1 前言 2 基础参数 3 密钥对生成 4 签名算法 4 1 预处理1 4 2 预处理2 4 3 生成签名 4 4 签名验证 4 5 签名验证原理 5 参考资料 1 前言 比原链的智能合约支持国密算法的函数 SM2是国密标准的椭圆曲线
  • STM32——ARM与STM32之间的联系

    ARM与STM32之间的联系 stm32是基于ARM内核的一种控制器 xff0c 是包含与被包含的关系 ARM xff08 STM32 xff09
  • VTK坐标转换

    VTK坐标转换

随机推荐

  • 软件工程师校招面试救急包

    LeetCode牛人总结 xff08 手撕代码前看看 xff0c 抱佛脚 xff09 https github com labuladong fucking algorithm blob master README md 剑指offer x
  • unix环境高级编程——UNIX体系架构

    本期主题 xff1a unix环境高级编程 UNIX体系架构 文件IO 0 初始UNIX1 系统调用2 库函数2 1 C语言的运行库 3 shell 0 初始UNIX 这里略过unix的历史不讲 xff0c 网上有比较详细的资料 我们可以将
  • CVPR2019:无监督深度追踪

    本文提出了一种无监督的视觉跟踪方法 与使用大量带注释数据进行监督学习的现有方法不同 xff0c 本文的CNN模型是在无监督的大规模无标签视频上进行训练的 动机是 xff0c 强大的跟踪器在向前和向后预测中均应有效 xff08 即 xff0c
  • 各种智能优化算法比较与实现(matlab版)

    各种智能优化算法比较与实现 xff08 matlab版 xff09 一 方法介绍 1免疫算法 xff08 Immune Algorithm xff0c IA xff09 1 1算法基本思想 免疫算法是受生物免疫系统的启发而推出的一种新型的智
  • 我与人工智能的故事

    本文作者 xff1a 诸葛越 前 言 人工智能的三次浪潮 2018年年初 xff0c 招聘季正如火如荼地进行 xff0c 而 数据科学家 和 算法工程师 绝对算得上热门职业 人工智能 机器学习 深度学习 建模 卷积神经网络 等关键词 xff
  • ovn-architecture

    参考 文章目录 1 Name2 Description2 1 Information Flow in OVN OVN中的信息流向 2 2 Chassis Setup2 3 Logical Networks2 4 Life Cycle of
  • IDEA创建maven项目添加jar包依赖出错

    Problem1 xff1a 由于网络原因无法下载jar包 解决方法 xff1a 在maven的settings xml文件的 lt mirrors gt 标签中配置阿里镜像 lt mirror gt lt id gt nexus aliy
  • 判断一个字符串是否为回文字符串

    题目 输入一个字符串 xff0c 判断该字符串中除去空格之后的字符串是否为回文字符串 要求 xff1a 不可使用 Java 已实现的方法替换空格 xff0c 不可消耗额外的空间 代码实现 此处为判断方法的实现 public static b
  • Spring中的 JdbcTemplate和声明式事务控制

    Spring中的 JdbcTemplate和声明式事务控制 JdbcTemplate概述 JdbcTemplate的作用 xff1a 他就是用于和数据库交互的 xff0c 实现CRUD操作 如何创建该对象 在dao的实现类中定义并用set方
  • SpringMVC(一)

    SpringMVC xff08 一 xff09 SpringMVC的基本概念 三层架构 表现层业务层持久层 MVC模型 Model xff08 模型 xff09 xff1a 通常就是指我们的数据模型 xff0c 一般情况下用于封装数据 Vi
  • SpringMVC(二)

    SpringMVC xff08 二 xff09 响应数据和结果视图 返回值分类 xff1a 字符串voidModelAndView 对象 xff1a 是 spring 提供的一个对象 xff0c 可以用来调整具体的 JSP 视图 span
  • SpringMvc(三)

    SpringMvc xff08 三 xff09 SSM 整合可以使用多种方式 xff0c 一般会选择 XML 43 注解 的方式 整合的思路 xff1a 搭建整合环境先把spring 的配置搭建完成再使用 spring 整合SpringMV
  • 工厂方法模式(Factory Method)--多态工厂的实现

    工厂方法模式 xff08 Factory Method xff09 多态工厂的实现 定义 xff1a 定义一个用于创建对象的接口 xff0c 让子类决定实例化哪一个类 xff0c 工厂方法使一个类的实例化延迟到其子类 类图 xff1a 外链
  • 无人机自主定位导航避障VINS+fast_planner实测~

    厦大研一研究的一个项目 xff0c 将项目开发用到的技术和难点在这记录一下 常更新 xff0c 先把框架写好 xff0c 有空的时候就过来更新 xff0c 要是有漏的或者有错误的地方 xff0c 请大佬指点 因为采用的是TX2 xff0c
  • rs_D455相机内外参标定+imu联合标定

    IMU标定 lt launch gt lt node pkg 61 34 imu utils 34 type 61 34 imu an 34 name 61 34 imu an 34 output 61 34 screen 34 gt lt
  • GVINS论文简明解读

    VIN与GNSS融合的必要性 VIN系统只工作在局部坐标系下 x y z yaw不可观 里程计存在不可避免的漂移 而GNSS系统可提供无漂移的全局定位 VIN与GNSS融合的难点 不同于cmaera与imu此类的外参标定 GNSS坐标与VI
  • onos2.5.2编译安装

    onos编译安装 Ubuntu18 04 1 前置下载安装 1 1 前置包安装 参考docker file sudo apt get install y ca certificates zip python python3 git bzip
  • C++和C的区别(汇总)

    1 C是面向过程的语言 xff0c 而C 43 43 是面向对象的语言 2 C和C 43 43 动态管理内存的方法不一样 xff0c C是使用malloc free函数 xff0c 而C 43 43 除此之外还有new delete关键字
  • PX4学习笔记—通过串口发送自定义数据

    最近因为项目需要实现一个通过pixhawk串口收发自定义数据的功能 搜索发现 xff0c 博客上大神们FantasyJXF FreeApe的博文已经详细介绍了通过pixhawk串口读取自定义数据 xff0c 这部分功能实现后就可以将自己开发
  • 一步步入门搭建SpringSecurity OAuth2(密码模式)

    什么是OAuth2 xff1f 是开放授权的一个标准 xff0c 旨在让用户允许第三方应用去访问改用户在某服务器中的特定私有资源 xff0c 而可以不提供其在某服务器的账号密码给到第三方应用 大概意思就是比如如果我们的系统的资源是受保护的