Spring Boot 3 集成 Spring Security + JWT

文章目录

  • Spring Boot 3 集成 Spring Security + JWT
    • 准备工作
      • 引入依赖
      • 设计表结构
      • 生成基本代码
      • 白名单配置
    • JWT配置
      • 配置参数jwt密码、过期时间等
      • 自定义未授权和未登录结果返回
      • 创建JWT过滤器
    • 改写SecurityConfig
    • 登录验证
      • 启动查看接口
      • 未登录
      • 登录
    • 总结

Spring Boot 3 集成 Spring Security + JWT

准备工作

概述: 在本文中,我们将一步步学习如何使用 Spring Boot 3 和 Spring Security 来保护我们的应用程序。我们将从简单的入门开始,然后逐渐引入数据库,并最终使用 JWT 实现前后端分离。

引入依赖

这里主要用到了Mybatis-plus、hutool 、knife4j ,其他依赖可以直接勾选

image-20241202162308177

 <properties>
        <java.version>17</java.version>

        <mybatisplus.version>3.5.9</mybatisplus.version>
        <knife4j.version>4.5.0</knife4j.version>
        <hutool.version>5.8.26</hutool.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <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.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- MyBatis-Plus https://baomidou.com-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-jsqlparser</artifactId>
        </dependency>

        <!--Knife4j https://doc.xiaominfo.com/-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>

        <!-- Java工具类库 https://doc.hutool.cn -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-bom</artifactId>
                <version>${mybatisplus.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

我这里使用的Spring boot版本为3.3.5 ,使用3.4.0整合JWT过滤器时,打开swagger会报错:jakarta.servlet.ServletException: Handler dispatch failed: java.lang.NoSuchMethodError: 'void org.springframework.web.method.ControllerAdviceBean.<init>(java.lang.Object) ,说是版本兼容问题。暂时没有找到很好的解决方案,所以给Spring boot版本降至3.3.5

设计表结构

关于表结构内容我这里不详细的说了,各个表字段内容,可以拉一下代码,获取表结构sql脚本。关注公众号:“Harry技术”,回复“jwt”,即可获取到整个项目源码以及表结构。

sys_config 系统配置表
sys_dept 部门表
sys_dict 字典表
sys_dict_data 字典数据表
sys_menu 菜单表
sys_role 角色表
sys_role_menu 角色菜单关系表
sys_user 用户表
sys_user_role 用户角色关系表

生成基本代码

image-20241202161738013

白名单配置

因为我们这里引入knife4j ,关于knife4j 的相关配置可以参考《Spring Boot 3 整合Knife4j(OpenAPI3规范)》,我们需要将以下接口加入到白名单

  # 白名单列表
  ignore-urls:
    - /v3/api-docs/**
    - /doc.html
    - /swagger-resources/**
    - /webjars/**
    - /swagger-ui/**
    - /swagger-ui.html

JWT配置

JWT(JSON Web Token)相关资料网络上非常多,可以自行搜索,简单点说JWT就是一种网络身份认证和信息交换格式。

  • Header 头部信息,主要声明了JWT的签名算法等信息
  • Payload 载荷信息,主要承载了各种声明并传递明文数据
  • Signature 签名,拥有该部分的JWT被称为JWS,也就是签了名的JWT,用于校验数据

整体结构是:

header.payload.signature

配置参数jwt密码、过期时间等

  • yml 配置
# 安全配置
security:
  jwt:
    # JWT 秘钥
    key: www.tech-harry.cn
    # JWT 有效期(单位:秒)
    ttl: 7200
  # 白名单列表
  ignore-urls:
    - /v3/api-docs/**
    - /doc.html
    - /swagger-resources/**
    - /webjars/**
    - /swagger-ui/**
    - /swagger-ui.html
    - /auth/login
  • 创建SecurityProperties
/**
 * Security Properties
 *
 * @author harry
 * @公众号 Harry技术
 */
@Data
@ConfigurationProperties(prefix = "security")
public class SecurityProperties {

    /**
     * 白名单 URL 集合
     */
    private List<String> ignoreUrls;

    /**
     * JWT 配置
     */
    private JwtProperty jwt;


    /**
     * JWT 配置
     */
    @Data
    public static class JwtProperty {

        /**
         * JWT 密钥
         */
        private String key;

        /**
         * JWT 过期时间
         */
        private Long ttl;

    }
}

自定义未授权和未登录结果返回

在之前的案例中没有自定义未授权和未登录,直接在页面上显示错误信息,这样对于前端来说不是很好处理,我们将所有接口按照一定的格式返回,会方便前端交互处理。

  • 未登录

    /**
     * 当未登录或者token失效访问接口时,自定义的返回结果
     *
     * @author harry
     * @公众号 Harry技术
     */
    @Component
    public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            response.getWriter().println(JSONUtil.toJsonStr(R.unauthorized(authException.getMessage())));
    
            response.getWriter().flush();
        }
    
    }
    
  • 未授权

    /**
     * 当访问接口没有权限时,自定义的返回结果
     *
     * @author harry
     * @公众号 Harry技术
     */
    @Component
    public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
    
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            response.getWriter().println(JSONUtil.toJsonStr(R.forbidden(e.getMessage())));
            response.getWriter().flush();
        }
    
    }
    

创建JWT过滤器

这里直接使用了Hutool-jwt提供的JWTUtil工具类,主要包括:JWT创建、JWT解析、JWT验证。

/**
 * JWT登录授权过滤器
 *
 * @author harry
 * @公众号 Harry技术
 */
@Slf4j
public class JwtValidationFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;

    // 密钥
    private final byte[] secretKey;

    public JwtValidationFilter(UserDetailsService userDetailsService, String secretKey) {
        this.userDetailsService = userDetailsService;
        this.secretKey = secretKey.getBytes();
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain chain) throws ServletException, IOException {
        // 获取请求token
        String token = request.getHeader(HttpHeaders.AUTHORIZATION);
        try {
            // 如果请求头中没有Authorization信息,或者Authorization以Bearer开头,则认为是匿名用户
            if (StrUtil.isBlank(token) || !token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) {
                chain.doFilter(request, response);
                return;
            }

            // 去除 Bearer 前缀
            token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
            // 解析 Token
            JWT jwt = JWTUtil.parseToken(token);

            // 检查 Token 是否有效(验签 + 是否过期)
            boolean isValidate = jwt.setKey(secretKey).validate(0);
            if (!isValidate) {
                log.error("JwtValidationFilter error: token is invalid");
                throw new ApiException(ResultCode.UNAUTHORIZED);
            }
            JSONObject payloads = jwt.getPayloads();
            String username = payloads.getStr(JWTPayload.SUBJECT);
            SysUserDetails userDetails = (SysUserDetails) this.userDetailsService.loadUserByUsername(username);

            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);

        } catch (Exception e) {
            log.error("JwtValidationFilter error: {}", e.getMessage());
            SecurityContextHolder.clearContext();
            throw new ApiException(ResultCode.UNAUTHORIZED);
        }
        // Token有效或无Token时继续执行过滤链
        chain.doFilter(request, response);
    }
}

改写SecurityConfig

关于Spring Boot 3 集成 Spring Security相关的知识点,可以参考文章:《Spring Boot 3 集成 Spring Security(1)认证》、《Spring Boot 3 集成 Spring Security(2)授权》、《Spring Boot 3 集成 Spring Security(3)数据管理》。

/**
 * Spring Security 权限配置
 *
 * @author harry
 * @公众号 Harry技术
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true) // 开启方法级别的权限控制
@RequiredArgsConstructor
public class SecurityConfig {

    private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    private final SecurityProperties securityProperties;
    private final UserDetailsService userDetailsService;

    @Bean
    protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // 忽略的路径
        http.authorizeHttpRequests(requestMatcherRegistry -> requestMatcherRegistry.requestMatchers(
                        securityProperties.getIgnoreUrls().toArray(new String[0])).permitAll()
                .anyRequest().authenticated()
        );

        http
                // 由于使用的是JWT,我们这里不需要csrf
                .csrf(AbstractHttpConfigurer::disable)
                // 禁用session
                .sessionManagement(configurer ->
                        configurer
                                .sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        // 添加自定义未授权和未登录结果返回
        http.exceptionHandling(customizer ->
                customizer
                        // 处理未授权
                        .accessDeniedHandler(restfulAccessDeniedHandler)
                        // 处理未登录
                        .authenticationEntryPoint(restAuthenticationEntryPoint));
        // JWT 校验过滤器
        http.addFilterBefore(new JwtValidationFilter(userDetailsService, securityProperties.getJwt().getKey()), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    /**
     * AuthenticationManager 手动注入
     *
     * @param authenticationConfiguration 认证配置
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    /**
     * 强散列哈希加密实现
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

这里主要做了以下几点配置:

  • 将不需要认证鉴权的接口加入白名单

  • 由于使用的是JWT,我们这里不需要csrf、禁用session

  • 添加自定义未授权和未登录结果返回

  • 配置 JWT 校验过滤器

我们根据数据库中的用户信息加载用户,并将角色转换为 Spring Security 能识别的格式。我们写一个SysUserDetails类来实现自定义Spring Security 用户对象。

/**
 * 用户详情服务
 *
 * @author harry
 * @公众号 Harry技术
 */
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private final SysUserMapper sysUserMapper;
    private final SysMenuMapper sysMenuMapper;
    private final SysUserRoleMapper sysUserRoleMapper;

    @Override
    @Cacheable(value = CacheConstants.USER_DETAILS, key = "#username", unless = "#result == null ")
    public SysUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 获取登录用户信息
        SysUser user = sysUserMapper.selectByUsername(username);

        // 用户不存在
        if (BeanUtil.isEmpty(user)) {
            throw new ApiException(SysExceptionEnum.USER_NOT_EXIST);
        }
        Long userId = user.getUserId();

        // 用户停用
        if (StatusEnums.DISABLE.getKey().equals(user.getStatus())) {
            throw new ApiException(SysExceptionEnum.USER_DISABLED);
        }

        // 获取角色
        Set<String> roles = sysUserRoleMapper.listRoleKeyByUserId(userId);

        // 获取数据范围标识
        Integer dataScope = sysUserRoleMapper.getMaximumDataScope(roles);

        Set<String> permissions = new HashSet<>();
        // 如果 roles 包含 root 则拥有所有权限
        if (roles.contains(CommonConstant.SUPER_ADMIN_ROOT)) {
            permissions.add(CommonConstant.ALL_PERMISSION);
        } else {
            // 获取菜单权限标识
            permissions = sysMenuMapper.getMenuPermission(userId);
            // 过滤空字符串
            permissions.remove("");
        }

        return new SysUserDetails(user, permissions, roles, username, dataScope);
    }

}

这里使用了@Cacheable结合redis做的缓存处理,关于缓存相关配置,可以参考文章《Spring Boot 3 整合Redis(1) 基础功能》、《Spring Boot 3 整合Redis(2)注解驱动缓存》。

登录验证

  • 写一个登录接口/auth/login,返回 token、tokenType等信息
/**
 * 登录相关
 *
 * @author harry
 * @公众号 Harry技术
 */
@Slf4j
@RestController
@RequiredArgsConstructor
@Tag(name = "认证中心")
@RequestMapping("/auth")
public class LoginController {

    private final SysUserService sysUserService;

    @Operation(summary = "login 登录")
    @PostMapping(value = "/login")
    public R<LoginResult> login(@RequestBody SysUserLoginParam sysUserLoginParam) {

        return R.success(sysUserService.login(sysUserLoginParam.getUsername(), sysUserLoginParam.getPassword()));
    }

    @Operation(summary = "info 获取当前用户信息")
    @GetMapping(value = "/info")
    public R<UserInfoResult> getInfo() {
        UserInfoResult result = sysUserService.getInfo();
        return R.success(result);
    }

    @Operation(summary = "logout 注销")
    @PostMapping(value = "/logout")
    public R logout(HttpServletRequest request) {
        // 需要 将当前用户token 设置无效
        SecurityContextHolder.clearContext();
        return R.success();
    }

}
  • LoginResult 对象

    /**
     *
     * @author harry
     * @公众号 Harry技术
     */
    @Data
    public class LoginResult {
    
        @Schema(description = "token")
        private String token;
    
        @Schema(description = "token 类型", example = "Bearer")
        private String tokenType;
    
        @Schema(description = "过期时间(单位:秒)", example = "604800")
        private Long expiration;
    
        @Schema(description = "刷新token")
        private String refreshToken;
    
    }
    

启动查看接口

访问http://localhost:8080/swagger-ui/index.html或者http://localhost:8080/doc.html

image-20241202171619913

image-20241202171646132

未登录

当我们处于未登录状态时访问/auth/info接口,直接返回了我们自定义的异常信息

image-20241202171939442

登录

这里我们登录用户 harry/123456,设定用户角色TEST,菜单权限不给字典相关的操作。

image-20241202172623935

看到接口成功返回token等信息,我们将token信息填写到 Authorize,作为全局配置。

image-20241202172804760

这时,我们访问/auth/info,可以看到当前登录的用户信息

image-20241202172855574

我们访问字典相关的接口,如:/sys_dict/page,返回了没有相关权限的信息

image-20241202172938760

访问其他接口,如:/sys_dept/page,可以看到数据正常返回。

image-20241202173105784

总结

到这里,我们已经掌握了Spring Boot 3 整合 Security 的全过程。我们将从简单的入门开始,然后学习如何整合数据库,并最终使用 JWT 实现前后端分离。这些知识将帮助我们构建更安全、更可靠的应用程序。后续我们会深入了解在项目中用到的一些其他框架、工具。让我们一起开始吧!

示例源码:关注公众号“Harry技术”,回复 jwt 获取源码地址。

文章推荐

  • SpringBoot3整合Hutool-captcha实现图形验证码
  • Spring Boot 3 集成 Spring Security(1)认证
  • Spring Boot 3 集成 Spring Security(2)授权
  • Spring Boot 3 集成 Spring Security(3)数据管理
  • Spring Boot 3 整合Knife4j(OpenAPI3规范)
  • Spring Boot 3 整合Redis(1) 基础功能
  • Spring Boot 3 整合Redis(2)注解驱动缓存
  • Spring Boot 3 集成 Spring Security + JWT

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

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

相关文章

tauri使用github action打包编译多个平台arm架构和inter架构包踩坑记录

这些error的坑&#xff0c;肯定是很多人不想看到的&#xff0c;我的开源软件PakePlus是使用tauri开发的&#xff0c;PakePlus是一个界面化将任何网站打包为轻量级跨平台软件的程序&#xff0c;利用Tauri轻松构建轻量级多端桌面应用和多端手机应用&#xff0c;为了实现发布的时候…

【娱乐项目】竖式算术器

Demo介绍 一个加减法随机数生成器&#xff0c;它能够生成随机的加减法题目&#xff0c;并且支持用户输入答案。系统会根据用户输入的答案判断是否正确&#xff0c;统计正确和错误的次数&#xff0c;并显示历史记录和错题记录。该工具适合用于数学练习&#xff0c;尤其适合练习基…

D86【python 接口自动化学习】- pytest基础用法

day86 pytest配置testpaths 学习日期&#xff1a;20241202 学习目标&#xff1a;pytest基础用法 -- pytest配置testpaths 学习笔记&#xff1a; pytest配置项 主目录创建pytest.ini文件 [pytest] testpaths./testRule 然后Terminal里直接命令&#xff1a;pytest&#xff…

LeetCode1556. 千位分隔数 LeetCode9. 回文数

题目一、1556. 千位分隔数 给你一个整数 n&#xff0c;请你每隔三位添加点&#xff08;即 "." 符号&#xff09;作为千位分隔符&#xff0c;并将结果以字符串格式返回。 示例 1&#xff1a; 输入&#xff1a;n 987 输出&#xff1a;"987"示例 2&#xff…

网络编程(UDP\TCP回显服务器)

目录 套接字socket TCP和UDP特点比较 特点 比较 UDP回显服务器/客户端的编写 UDP的socket api 回显服务器 客户端 TCP回显服务器/客户端的编写 TCP的socket api 回显服务器 客户端 优化服务器 1.关闭服务器创建的socket对象 2.引入线程池&#xff0c;为多个客户…

基于python的某音乐网站热门歌曲的采集与分析,包括聚类和Lda主题分析

一项目背景 在当前竞争激烈的市场环境下&#xff0c;分析酷狗音乐上的热门歌曲及其用户行为趋势&#xff0c;对平台运营、歌曲推荐和音乐创作具有重要意义。尤其是通过对酷狗音乐平台热门歌曲的数据采集与分析&#xff0c;可以深入理解用户偏好、歌曲流行的规律以及市场需求的…

论文阅读之方法: Single-cell transcriptomics of 20 mouse organs creates a Tabula Muris

The Tabula Muris Consortium., Overall coordination., Logistical coordination. et al. Single-cell transcriptomics of 20 mouse organs creates a Tabula Muris. Nature 562, 367–372 (2018). 论文地址&#xff1a;https://doi.org/10.1038/s41586-018-0590-4 代码地址…

国产多点低压差分(M-LVDS)线路驱动器和接收器,可替代DS91D176/SN65MLVD201

MS2111 是多点低压差分 (M-LVDS) 线路驱动器和接收器。经过优化&#xff0c;可运行在高达 200Mbps 的信号速率下。所有部件均符合 MLVDS 标准 TIA / EIA-899 。该驱动器的输出支持负载低至 30Ω 的多点总线。 MS2111 的接收器属于 Type-2 &#xff0c; 可在 -1V 至 …

【推荐算法】推荐系统的评估

这篇文章是笔者阅读《深度学习推荐系统》第五章推荐系统的评估的学习笔记&#xff0c;在原文的基础上增加了自己的理解以及内容的补充&#xff0c;在未来的日子里会不断完善这篇文章的相关工作。 文章目录 离线评估划分数据集方法客观评价指标P-R曲线ROC/AUCmAPNDCG A/B 测试分…

专题1:AI大模型在汽车行业的应用现状与展望

前言&#xff1a; 本文是《大模型在汽车行业的典型应用实践》100w字长文预告的一篇&#xff0c;本文没有泛泛的去做行业应用现状的介绍&#xff0c;而是通过引入行业调研报告的方式&#xff0c;进行详解&#xff0c;用事事和数据来说话&#xff0c;具有很好的参考意义。 一、…

开源ISP介绍(1)——开源ISP的Vivado框架搭建

开源github链接&#xff1a;bxinquan/zynq_cam_isp_demo: 基于verilog实现了ISP图像处理IP 国内Gitee链接&#xff1a;zynq_cam_isp: 开源ISP项目 基于以上开源链接移植项目到正点原子领航者Zynq7020开发板&#xff0c;并对该项目的Vivddo工程进行架构详解&#xff0c;后续会…

【Java基础面试题009】Java的I/O流是什么?

相关知识补充&#xff1a;黑马-字符集、IO流&#xff08;一&#xff09;.pdf Autism_Btkrsr/Blog_md_to_pdf - 码云 - 开源中国 (gitee.com) 黑马-IO流&#xff08;二&#xff09;.pdf Autism_Btkrsr/Blog_md_to_pdf - 码云 - 开源中国 (gitee.com) 回答重点 Java的I/O&…

Python Tkinter 模块

Python 支持多种图形界面的第三方库&#xff0c;包括 TkQtwxWidgets… Python 提供的 Tkinter 模块&#xff0c;就是 Tk GUI 工具包的接口。 Tkinter Tk 是图形库&#xff0c;支持多种操作系统&#xff0c;使用 Tcl 语言开发&#xff1b; Tk 会调用操作系统提供的本地 GUI …

详细介绍下oracle建库过程中核心脚本dbcore.bsq

在我们搭建oracle 11g数据库过程中&#xff0c;当我们设置好安装参数后&#xff0c;最后一步进行数据库安装时&#xff0c;oracle安装进程实际上调用的是$ORACLE_HOME/rdbms/admin/sql.bsq脚本进行建库。今天我们将详细介绍下其中的核心脚本dbcore.bsq。 一、建库脚本 我们先看…

解决`-bash: ./configure:/bin/sh^M:解释器错误: 没有那个文件或目录`的问题

解决`-bash: ./configure:/bin/sh^M:解释器错误: 没有那个文件或目录`的问题 一、错误原因分析二、解决方法方法一:使用`dos2unix`工具方法二:使用`sed`命令方法三:使用`tr`命令方法四:在文本编辑器中转换方法五:在Windows系统中使用适当的工具三、预防措施四、总结在使…

linux安全-firewalld防火墙-基础讲解

目录 一、 防火墙技术分类 二、 firewalld 三、 firewalld支持的类型的NAT 四、 富语言 五、 firewalld配置方式 六、 firewall-cmd命令 七、 小实验 这篇文章将对 firewalld 防火墙的基础知识进行介绍 firewalld简介&#xff1a;firewalld的作用是为包过滤机制提供匹配…

图的概念即存储结构(C++实现图【1】)

目录 1. 图的基本概念 2. 图的存储结构 2.1 邻接矩阵 2.1.1私有成员变量 2.1.2类模板的声明 2.1.3构造函数 2.1.4获取顶点下标 2.1.5添加边的信息 2.1.6打印图 2.1.7测试用例 2.2邻接表 2.2.1私有成员变量 2.2.2Edge类 2.2.3类模板的声明 2.2.4构造函数 2.2.5获取顶点下标 2.2.…

使用Tauri创建桌面应用

当前是在 Windows 环境下 1.准备 系统依赖项 Microsoft C 构建工具WebView2 (Windows10 v1803 以上版本不用下载&#xff0c;已经默认安装了) 下载安装 Rust下载安装 Rust 需要重启终端或者系统 重新打开cmd&#xff0c;键入rustc --version&#xff0c;出现 rust 版本号&…

实验13 使用预训练resnet18实现CIFAR-10分类

1.数据预处理 首先利用函数transforms.Compose定义了一个预处理函数transform&#xff0c;里面定义了两种操作&#xff0c;一个是将图像转换为Tensor&#xff0c;一个是对图像进行标准化。然后利用函数torchvision.datasets.CIFAR10下载数据集&#xff0c;这个函数有四个常见的…

Wwise SoundBanks内存优化

1.更换音频格式为Vorbis 2.停用多余的音频&#xff0c;如Random Container的随机脚步声数量降为2个 3.背景音乐勾选“Stream”。这样就让音频从硬盘流送到Wwise&#xff0c;而不是保存在内存当中&#xff0c;也就节省了内存 4.设置最大发声数Max Voice Instances 5.设置音频…