SpringBoot整合SpringScurity权限控制(菜单权限,按钮权限)以及加上SSH实现安全传输

文章目录

    • 项目地址:
  • 一、md5 与 先进的哈希算法的区别
    • 1.1. 安全性问题
    • 1.2. 设计目的
    • 1.3. 功能特性
    • 1.4. 适用性
    • 1.5. 总结
  • 二、数据传输安全和数据加密实现:
    • 2.1 生成证书:
    • 2.2、在springboot中进行集成
      • 2.2.1 配置证书:
      • 2.2.2. 强制使用 HTTPS
        • 2.2.2.1 实现过程
      • 2.2.3 使用bcrypt 进行密码加密
        • 2.2.3.1实现过程
        • 2.2.3.2 BCryptPasswordEncoder 的实现逻辑
  • 三、权限控制:
    • 3.1、菜单权限控制
      • 3.1.1具体步骤:
    • 3.2、按钮权限:
      • 3.2.1 具体步骤:
      • 3.2.2 流程图
  • 别的
    • 在Springboot中使用Argon2进行密码加密 不依靠 SpringScurity
      • 1. 添加 Argon2 库的依赖
      • 2. 散列密码
      • 3. 验证密码
      • 4. 使用例子
      • 验证逻辑
      • 小结

项目地址:

https://github.com/yszhdhy/ItemSet/tree/master/java/secureAndSpringScurity

项目结构:
在这里插入图片描述

一、md5 与 先进的哈希算法的区别

先进的hash算法有( bcrypt、PBKDF2 或 Argon2)

实际上,使用 bcrypt、PBKDF2 或 Argon2 进行密码散列与使用 MD5 有着显著的区别,主要在于安全性和适用性。虽然 MD5 曾广泛用于密码散列,但它现在被认为是不安全的选择,主要原因如下:

1.1. 安全性问题

  • 快速计算:MD5 设计用于快速计算文件或数据的散列值,这使得它在抵抗暴力破解攻击方面表现不佳。攻击者可以使用现代的硬件在极短的时间内尝试数十亿个密码组合。
  • 碰撞弱点:MD5 已经被证明存在碰撞弱点,即可以找到两个不同的输入产生相同的输出。这种特性使其不适合进行密码存储或任何需要高安全性的场景。

1.2. 设计目的

  • MD5:最初设计为快速计算数据的散列,用于检查文件完整性,而非密码存储。
  • bcrypt/PBKDF2/Argon2:特别为密码存储设计,包括对抗散列速度过快的攻击手段,如密钥拉伸和盐值使用,这些都是为了增加密码破解的难度。

1.3. 功能特性

  • 密钥拉伸:bcrypt、PBKDF2 和 Argon2 实现了密钥拉伸功能,可以通过增加计算的时间和资源需求来有效抵抗暴力破解攻击。
  • 盐值使用:这些算法在散列密码时使用盐值,可以有效防止彩虹表攻击,即使是两个相同的密码,在加了不同的盐之后,产生的散列值也会不同。

1.4. 适用性

  • 广泛适用:虽然你提到“大多数项目都是使用 MD5 进行加密”,但实际上,在安全性意识提高和数据泄露事件频发的当下,许多现代应用和安全标准推荐使用 bcrypt、PBKDF2 或 Argon2 作为密码存储的首选方法。

1.5. 总结

如果你在设计一个需要存储用户密码的系统,强烈建议使用 bcrypt、PBKDF2 或 Argon2 而不是 MD5。这些现代算法提供了更高级别的安全保障,可以帮助你的系统抵御当前的威胁,如暴力破解和彩虹表攻击。选择正确的密码存储策略是保护用户数据安全的关键一步。

二、数据传输安全和数据加密实现:

2.1 生成证书:

  1. 执行命令

    keytool -genkeypair -alias mydomain -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore mykeystore.p12 -validity 3650 -storepass yourpassword
    

    命令详解:

    • keytool: 这是 Java 提供的一个用于管理密钥和证书的命令行工具。
    • -genkeypair: 这个参数告诉 keytool 生成一个密钥对,包括一个公钥和一个相应的私钥。
    • -alias mydomain: 这个参数指定了密钥对的别名。在之后的操作中,可以使用这个别名来引用这个密钥对。
    • -keyalg RSA: 这个参数指定了要使用的密钥算法。在这种情况下,RSA 算法被选用。
    • -keysize 2048: 这个参数指定了生成的密钥的位长度。在这个例子中,密钥的长度为 2048 位。
    • -storetype PKCS12: 这个参数指定了密钥库的类型。在这里,选择了 PKCS#12 格式。
    • -keystore mykeystore.p12: 这个参数指定了要生成的密钥库的文件名。在这个例子中,生成的密钥库将被命名为 mykeystore.p12
    • -validity 3650: 这个参数指定了生成的证书的有效期限。在这里,证书的有效期限被设置为 3650 天(大约 10 年)。
    • -storepass yourpassword: 这个参数指定了密钥库的密码。在这个例子中,密码被设置为 yourpassword。请确保将 yourpassword 替换为你自己的密码。
    • 在这里插入图片描述

2.2、在springboot中进行集成

2.2.1 配置证书:

  1. 将生成的证书放置在resource目录下在这里插入图片描述

  2. 配置yaml

    spring:
      application:
        name: demo
    
    server:
      ssl:
        key-store: "src/main/resources/mykeystore.p12"
        key-store-password: xu123456
        keyStoreType: PKCS12   # 存储加密信息的标准文件格式
        keyAlias: mydomain # 别名用于从密钥库中引用具体的密钥
      port: 8840
    
  3. 启动项目并使用cmd 进行测试在这里插入图片描述

总结:

​ 完成以上操作后我们的所有的请求 都需要使用https进行访问。

2.2.2. 强制使用 HTTPS

2.2.2.1 实现过程
  1. 首先导入本次项目所需要的所有依赖

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</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-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.1</version>
            </dependency>
    
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-boot-starter</artifactId>
                <version>3.0.3</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>2.0.21</version>
            </dependency>
    
        </dependencies>
    
  2. 配置yaml文件

    spring:
      application:
        name: demo
    
      datasource:
        password: '123456'
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/ceshi?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
        username: root
      mvc:
        format:
          date: yyyy-MM-dd
          date-time: yyyy-MM-dd HH:mm:ss
    
    server:
      port: 8840 # HTTP端口
      ssl:
        enabled: true
        port: 8843 # HTTPS端口
        key-store: "src/main/resources/mykeystore.p12"
        key-store-password: xu123456
        keyStoreType: PKCS12
        keyAlias: mydomain
    
  3. 搭建security配置文件 WebSecurityConfig

    1. 考虑在你的应用中强制使用 HTTPS,以避免数据通过不安全的 HTTP 发送。在 Spring Security 中,可以通过配置安全性策略来实现:

      @EnableWebSecurity
      public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .requiresChannel() //指定了需要使用的通道类型
                  .anyRequest() // 指定了所有的请求
                  .requiresSecure(); // 指定了需要使用安全的HTTPS通道
          }
      }
      

这样配置后,所有请求都会被重定向到 HTTPS (即使用户输入http)。

2.2.3 使用bcrypt 进行密码加密

2.2.3.1实现过程
  1. 创建密码加密配置文件 SecurityConfig 在添加用户业务中可以使用此 BCryptPasswordEncoder.encode() 方法进行加密用户密码并存储在数据库中

    package com.example.demo.config.passwordEncoder;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author CaptureOrNew
     * @description //进行密码加密配置
     * @date 19:29:12 2024/4/19
     * @return null
     **/
    @Configuration
    public class SecurityConfig {
    
        // 进行编码配置 创建一个BCryptPasswordEncoder的实例
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        // 配置其他安全措施...
    }
    
    
  2. 创建一个密码编辑器

    package com.example.demo.security.custom;
    
    
    import com.example.demo.config.passwordEncoder.SecurityConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Component;
    
    /**
     * @author CaptureOrNew
     * @description //创建一个bcrypt 加密器
     * @date 19:28:21 2024/4/19
     * @Param
     * @return null
     **/
    @Component
    public class CustomBcryptPasswordEncoder implements PasswordEncoder {
    
        @Autowired
        SecurityConfig securityConfig;
    
        // 进行密码加密操作
        public String encode(CharSequence rawPassword) {
            return securityConfig.passwordEncoder().encode(rawPassword.toString());
        }
    
        // 进行密码比对
        public boolean matches(CharSequence rawPassword, String encodedPassword) {
            // security 会自动进行密码的比对
            return securityConfig.passwordEncoder().matches(rawPassword, encodedPassword);
        }
    }
    
    
  3. 创建一个 UserDetailsService 接口 以及下面的实现类进行 数据库用户查询和一些鉴权判断

    package com.example.demo.security.custom;
    
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Component;
    
    @Component
    public interface UserDetailsService extends org.springframework.security.core.userdetails.UserDetailsService {
    
        /**
         * 根据用户名获取用户对象(获取不到直接抛异常)
         */
        @Override
        UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
    }
    
  4. 实现类

    package com.example.demo.service.impl;
    
    
    import com.example.demo.model.model.SysUser;
    import com.example.demo.security.custom.CustomUser;
    import com.example.demo.service.SysUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private SysUserService sysUserService;
    
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //根据用户名进行查询
            SysUser sysUser = sysUserService.getUserByUserName(username);
            if(null == sysUser) {
                throw new UsernameNotFoundException("用户名不存在!");
            }
    
            if(sysUser.getStatus().intValue() == 0) {
                throw new RuntimeException("账号已停用");
            }
    
            List<SimpleGrantedAuthority> authList = new ArrayList<>();
            return new CustomUser(sysUser, authList);
        }
    }
    
    
2.2.3.2 BCryptPasswordEncoder 的实现逻辑
  1. 加密 (encode) 过程
    • 用户提供一个明文密码。
    • 生成一个随机盐值(通常是每次加密时生成的)。
    • 使用明文密码和生成的盐值,通过 BCrypt 算法进行多轮散列处理。
    • 将盐值和散列结果合并成一个字符串(通常在字符串中包含盐值、散列值和用于 BCrypt 的成本参数)。
    • 返回这个合并后的字符串作为最终的存储散列。
  2. 验证 (matches) 过程
    • 用户尝试登录时提供一个明文密码。
    • 取出数据库中存储的散列密码,这个散列包含了用于该特定密码的盐值。
    • 使用存储的盐值和用户提供的明文密码再次执行相同的 BCrypt 散列过程。
    • 比较新生成的散列结果与存储的散列值是否一致。
    • 如果两者一致,说明密码匹配,用户验证成功;如果不一致,则验证失败。
  3. 流程图:
                    +-----------------------+
                    |       用户注册        |
                    +-----------------------+
                    | 输入明文密码          |
                    +-----------------------++-----------------------+
                    | 生成随机盐值          |
                    +-----------------------++-----------------------+
                    | 使用 BCrypt 加密      |
                    | (密码 + 盐值)         |
                    +-----------------------++-----------------------+
                    | 存储散列密码到数据库  |
                    +-----------------------++-----------------------+
                    |       用户登录        |
                    +-----------------------+
                    | 输入明文密码          |
                    +-----------------------++-----------------------+
                    | 读取存储的散列密码    |
                    | (包含盐值)            |
                    +-----------------------++-----------------------+
                    | 使用读取的盐值重新    |
                    | 加密输入的明文密码    |
                    +-----------------------++-----------------------+
                    | 比较两个散列值是否相同|
                    +-----------------------++-----------------------+
                    | 相同: 验证成功        |
                    | 不同: 验证失败        |
                    +-----------------------+

三、权限控制:

以下操作基于RBAC (角色访问控制) 权限管理模型

给用户分配角色 ——> 给角色分配权限(可对项目进行的操作)

3.1、菜单权限控制

在用户每次登录系统获取信息时,后端通过用户名和密码在数据库中进行查找此用户,如果查询到此用户就根据用户所处的角色及角色所能操作的菜单查询出来将查询到的菜单组装为前端的路由形式进行返回给前端。这样就实现了根据用户的身份角色不同,客户端就会展示相应的菜单等。

3.1.1具体步骤:

  1. 用户登录后紧接着获取菜单信息
  2. 从请求头获取用户信息即token
  3. 从数据在token中解析出登录用户的ID
  4. 根据ID查询用户信息
  5. 根据用户信息查询用户可操作的菜单
  6. 组装菜单并返回
  7. 前端通过动态路由的形式进行插入到项目中。

3.2、按钮权限:

根据用户可以操作的按钮进行来控制那些后端方法也就是接口是可以被用户所访问的(CURD)。

3.2.1 具体步骤:

  1. 前端中会根据用户登录后获取信息的接口获取到按钮权限。在前端中使用获取到的按钮权限信息对相应的按钮进行禁用或其他操作

  2. 我们后端中 security 配置类 TokenAuthenticationFilter 中进行配置获取用户按钮权限的操作并存入到security 身上

    @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response,
                                        FilterChain chain) throws ServletException, IOException {
            //如果是登录接口,直接放行
            if ("/admin/system/index/login".equals(request.getRequestURI())) {
                chain.doFilter(request, response);
                return;
            }
            // 获取校验信息
            UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
            if (null != authentication) {
                SecurityContextHolder.getContext().setAuthentication(authentication);
                chain.doFilter(request, response);
            } else {
                ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_ERROR));
            }
        }
    
        private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
    
            //请求头是否有token
            String username = request.getHeader("token");
    
            if (!StringUtils.isEmpty(username)) {
                String authListString = request.getHeader("authorities");
                if (!StringUtils.isEmpty(authListString)) {
                    System.out.println(authListString+"================>");
                    String ccc = authListString;
                    //把redis获取字符串权限数据转换要求集合类型 List<SimpleGrantedAuthority>
                    List<Map> maplist = JSON.parseArray(ccc, Map.class);
                    System.out.println(maplist);
                    List<SimpleGrantedAuthority> authList = new ArrayList<>();
                    for (Map map : maplist) {
                        String authority = (String) map.get("authority");
                        authList.add(new SimpleGrantedAuthority(authority));
                    }
                    return new UsernamePasswordAuthenticationToken(username, null, authList);
                } else {
                    return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
                }
            }
            return null;
        }
    
  3. 这样我们就可以直接调用security中的一个注解进行 校验:

    @PreAuthorize("hasAuthority('bnt.sysUser.list')")    
    
    
    
    /**
         * @author CaptureOrNew
         * @description //查询用户列表
         * @date 09:46:22 2024/4/20
         * @PreAuthorize 标注在方法上,表示访问方法的用户需要有对应的权限
         * @return com.example.demo.result.Result<java.util.List < com.example.demo.model.model.SysUser>>
         **/
        @PreAuthorize("hasAuthority('bnt.sysUser.list')")
        @GetMapping("getUserList")
        public Result<List<SysUser>> getUserList() {
            List<SysUser> list = sysUserService.list();
            return list != null ? Result.ok(list) : Result.build(null, LOGIN_ERROR);
        }
    
  4. 测试验证数据 这里我们是将权限放在header中了 ,没有使用redis

    const axios = require('axios');
    
    let config = {
      method: 'get',
      maxBodyLength: Infinity,
      url: 'https://localhost:8840/admin/getUserList',
      headers: { 
        'token': 'admin', 
        'authorities': '[{"authority":"bnt.sysUser.remove"},{"authority":"bnt.sysUser.add"},{"authority":"bnt.sysUser.update"}]'
      }
    };
    
    axios.request(config)
    .then((response) => {
      console.log(JSON.stringify(response.data));
    })
    .catch((error) => {
      console.log(error);
    });
    
    

3.2.2 流程图

  1. 用户登录在这里插入图片描述

  2. 用户添加在这里插入图片描述

别的

在Springboot中使用Argon2进行密码加密 不依靠 SpringScurity

1. 添加 Argon2 库的依赖

如果你的项目中使用 Java,你可以添加如下依赖(例如使用 argon2-jvm 库):

xmlCopy code<!-- Maven 依赖 -->
<dependency>
    <groupId>de.mkammerer</groupId>
    <artifactId>argon2-jvm</artifactId>
    <version>2.8</version>
</dependency>

2. 散列密码

创建一个服务类,实现密码的散列功能。这里是一个简单的例子,展示如何使用 Argon2 进行密码散列:

javaCopy codeimport de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;

public class PasswordService {
    private Argon2 argon2 = Argon2Factory.create();

    public String hashPassword(String password) {
        // 参数解释: 迭代次数, 内存使用量, 并行度表示算法同时使用的线程数或CPU核心数
        return argon2.hash(2, 65536, 1, password.toCharArray());
    }
}

3. 验证密码

验证过程中,你需要比较用户输入的密码与数据库中存储的散列值是否匹配。这可以通过 Argon2 的验证功能来实现:

javaCopy codepublic boolean verifyPassword(String inputPassword, String storedHash) {
    return argon2.verify(storedHash, inputPassword.toCharArray());
}

4. 使用例子

在你的业务逻辑中,可以使用这个服务来处理用户的密码。例如,在用户注册时散列密码,用户登录时验证密码:

javaCopy codepublic class UserService {

    private PasswordService passwordService = new PasswordService();

    // 用户注册
    public void registerUser(String username, String password) {
        String hashedPassword = passwordService.hashPassword(password);
        // 存储 username 和 hashedPassword 到数据库
    }

    // 用户登录
    public boolean loginUser(String username, String inputPassword) {
        // 从数据库获取存储的散列密码
        String storedHash = ...;
        return passwordService.verifyPassword(inputPassword, storedHash);
    }
}

验证逻辑

Argon2 的验证逻辑内部会处理与散列值相关的盐值和参数配置,所以你不需要手动处理盐值。当你调用 argon2.verify 方法时,它会解析存储的散列值,自动提取盐值和散列参数,然后使用这些信息来验证输入密码。

小结

使用 Argon2 手动处理密码散列和验证确实比直接使用 Spring Security 稍复杂一些,但这提供了更高的灵活性和控制,适合不使用 Spring Security 的项目。确保在实际部署中正确管理密码相关的安全细节,如使用安全的参数配置和及时更新依赖库。

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

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

相关文章

Pytorch 与 Tensorflow:深度学习的主要区别(1)

引言 目前&#xff0c;Python 深度学习领域已经涌现出多个由科技界巨头如 Google、Facebook 和 Uber 等公司公开发布的框架&#xff0c;这些框架旨在帮助开发者构建先进的计算架构。对于刚接触这一领域的你来说&#xff0c;深度学习是计算机科学中的一个分支&#xff0c;它通过…

springboot306基于Java的民宿管理系统(源码+包运行+配套LW+技术指导)

项目描述 临近学期结束&#xff0c;开始毕业设计制作&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉的困难吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。今天给大家介绍一篇基于Java的民宿管理…

初识指针(5)<C语言>

前言 在前几篇文章中&#xff0c;已经介绍了指针一些基本概念、用途和一些不同类型的指针&#xff0c;下文将介绍某些指针类型的运用。本文主要介绍函数指针数组、转移表&#xff08;函数指针的用途&#xff09;、回调函数、qsort使用举例等。 函数指针数组 函数指针数组即每个…

24年做抖音小店,你还停留在数据?别人都已经开始注重利润了

大家好&#xff0c;我是电商笨笨熊 一件事情持续做&#xff0c;一个项目持续深耕&#xff0c;意义到底是什么&#xff1f; 这句话我常常说&#xff0c;但很多人似乎走偏了实际意义&#xff1b; 尤其对于新手来说&#xff0c;做抖音小店总是向往某某老玩家多么牛的数据&#…

如何把握人力RPO的蓝海机遇?实战策略分享!

随着企业间竞争的日益激烈&#xff0c;人力资源管理的重要性愈发凸显。在众多人力资源管理策略中&#xff0c;招聘流程外包(RPO)作为一种新兴的服务模式&#xff0c;逐渐受到业界的关注。那么&#xff0c;人力RPO是否是蓝海项目?我们又该如何实施RPO呢? 一、人力RPO&#xff…

C++干货--引用

前言&#xff1a; C的引用&#xff0c;是学习C的重点之一&#xff0c;它与指针的作用有重叠的部分&#xff0c;但是它绝不是完全取代指针(后面我们也会简单的分析)。 引用的概念&#xff1a; 引用 不是新定义一个变量 &#xff0c;而 是给已存在变量取了一个别名 &#xf…

经典文献阅读之--D-Map(无需射线投射的高分辨率激光雷达传感器的占据栅格地图)

0. 简介 占用地图是机器人系统中推理环境未知和已知区域的基本组成部分。《Occupancy Grid Mapping without Ray-Casting for High-resolution LiDAR Sensors》介绍了一种高分辨率LiDAR传感器的高效占用地图框架&#xff0c;称为D-Map。该框架引入了三个主要创新来解决占用地图…

附录2 创建flask镜像

目录 1 python镜像 2 安装flask 3 把项目文件扔进去 3.1 创建git仓库 3.2 上传文件 3.3 获取git链接 3.4 在容器中git clone 4 启动flask服务 5 将容器保存为镜像 6 映射端口运行镜像 7 遇到的问题 8 Dockerfile创建镜像 1 python镜像 首先找一下fla…

Android 老年模式功能 放大字体

1 配置属性 <attr name"text_size_16" format"dimension"/><attr name"text_size_18" format"dimension"/><attr name"text_size_14" format"dimension"/><attr name"text_size_12&quo…

MySQL中的索引失效问题

索引失效的情况 这是正常查询情况&#xff0c;满足最左前缀&#xff0c;先查有先度高的索引。 1. 注意这里最后一种情况&#xff0c;这里和上面只查询 name 小米科技 的命中情况一样。说明索引部分丢失&#xff01; 2. 这里第二条sql中的&#xff0c;status > 1 就是范围查…

咸鱼之王游戏攻略:平民怎么起号?

在《咸鱼之王》这款游戏中&#xff0c;即使是平民玩家&#xff0c;也有着许多可以优化的操作&#xff0c;以最大程度地提高收益。本攻略将针对平民玩家的日常操作进行详细解读&#xff0c;包括黑市购买、资源管理等方面的建议&#xff0c;希望对广大玩家有所帮助。 一、黑市购买…

Vue的学习 —— <vue的开发环境> “6000字超详细”

目录 前言 学习目标 正篇开始 —— 部署vue开发环境 一、Visual Studio Code编辑器 1、简介 2、下载和安装VSCode编辑器 3、安装中文插件 4、安装Volar插件 5、使用VCode编辑器 二、Node.js环境 简介 下载和安装Node.js环境 三、包管理工具 1、简介 2、配置npm …

公司申请增加公众号数量

一般可以申请多少个公众号&#xff1f;众所周知&#xff0c;在2013年前后&#xff0c;公众号申请是不限制数量的&#xff0c;后来企业开始限制申请50个&#xff0c;直到2018年的11月tx又发布&#xff0c;其中个人主体可申请公众号由2个调整为1个&#xff0c;企业主体由50个调整…

信息量、熵、KL散度、交叉熵概念理解

信息量、熵、KL散度、交叉熵概念理解 (1) 信息量 信息量是对事件的不确定性的度量。 假设我们听到了两件事&#xff0c;分别如下&#xff1a;事件A&#xff1a;巴西队进入了世界杯决赛圈。 事件B&#xff1a;中国队进入了世界杯决赛圈。仅凭直觉来说&#xff0c;显而易见事件…

2024年蓝桥杯——复盘

1、握手问题 知识点&#xff1a;模拟 这道题很简单。但是不知道考试的时候有没有写错。一开始的43个人握手&#xff0c;仅需要两两握手&#xff0c;也就是从42个握手开始&#xff0c;而非43.很可惜。这道题没有拿稳这5分。也很有可能是这5分导致没有进决赛。 总结&#xff1a…

我的 OpenLiteSpeed 从开始到放弃之经历

昨晚下定决心放弃 OpenLiteSpeed 了&#xff0c;从开始到放弃历时七天。总结下来放弃 OpenLiteSpeed 主要是实在不适合明月当前的需要&#xff0c;用起来不是锦上添花而是个累赘了都&#xff0c;今天明月就给大家总结分享一下这次 OpenLiteSpeed 从开始到放弃的经历。 一、Ngin…

为什么大家都说it行业要死了?

年后开始找工作开始还不敢投简历怕准备不充分。怕错过了心仪的公司。 花了2周自己死磕了一下自我介绍&#xff0c;和工作经历。然后信心满满就开始投了&#xff0c;结果发现一堆人打招呼一阵欣喜的。可是一查全是外包公司。于是开始了自我怀疑。难道是我能力不行&#xff1f;难…

反转链表(C语言)———链表经典算法题

题目描述​​​​​​206. 反转链表 - 力扣&#xff08;LeetCode&#xff09;&#xff1a; 答案展示: 迭代&#xff1a; 递归&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* rev…

等保测评技术方案(五)

&#xff08;八&#xff09;漏洞扫描方案 1.参与人员 乙方工程师&#xff1a;谭 然、张 剑等。 范围经过双方确认&#xff0c;此次评估的对象包括&#xff1a; 2.网络设备 IP 地址 设备型号 备注 / / / / / / 以现场测评实际数据为准 3.应用系统 地址 …

Python从0到POC编写-魔法方法

name __name__ 是系统定义的内部函数&#xff0c; 它的作用是识别模块。 通常我们看到这样一句话&#xff1a; if __name__ __main____name__ 的值有两种情况&#xff0c;那么挨个来说下。 如果模块是被直接执行的 &#xff0c;那么 __name__ 的值 为 __main__ 例如&…