Spring Boot集成Spring Security之HTTP请求授权

一、HTTP请求授权工作原理

​ 基于Spring Security最新的Http请求授权讲解,不再使用旧版的请求授权

  1. 授权过滤器AuthorizationFilter获取认证信息

  2. 调用RequestMatcherDelegatingAuthorizationManager的check方法验证该用户是否具有该请求的授权

  3. RequestMatcherDelegatingAuthorizationManager根据配置的请求和授权关系校验用户是否具有当前请求的授权并返回授权结果

  4. AuthorizationFilter处理授权结果,授权成功则继续调用过滤器链,否则抛出AccessDeniedException异常

  5. 认证失败时,ExceptionTranslationFilter处理AccessDeniedException异常,如果是当前认证是匿名认证或者RememberMe认证则调用AuthenticationEntryPoint的commence方法,否则调用AccessDeniedHandler的handler方法

  6. 工作原理图如下

authorizationfilter

二、HTTP请求授权配置

1、添加用户权限

package com.yu.demo.spring.impl;

import com.yu.demo.entity.UserDetailsImpl;
import org.springframework.security.core.GrantedAuthority;
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;
import java.util.UUID;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    //@Autowired
    //private UserService userService;
    // @Autowired
    //private UserRoleService userRoleService;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //TODO 通过username从数据库中获取用户,将用户转UserDetails
        //User user = userService.getByUsername(username);
        //TODO 从数据库实现查询权限并转化为List<GrantedAuthority>
        //List<String> roleIds = userRoleService.listRoleIdByUsername(username);
        //List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roleIds.size());
        //roleIds.forEach(roleId -> grantedAuthorities.add(new SimpleGrantedAuthority(roleId)));
        //return new User(username, user.getPassword(), user.getEnable(), user.getAccountNonExpired(), user.getCredentialsNonExpired(), user.getAccountNonLocked(), user.getAuthorities());
        //测试使用,指定权限
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        //与hasXxxRole匹配时添加ROLE_前缀
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        //与hasXxxAuthority匹配时原始值
        grantedAuthorities.add(new SimpleGrantedAuthority("OPERATE"));
        //{noop}不使用密码加密器,密码123的都可以验证成功
        UserDetailsImpl userDetails = new UserDetailsImpl(username, "{noop}123", true, true, true, true, grantedAuthorities);
        //userDetails中设置token,该token只是实现认证流程,未使用jwt
        userDetails.setToken(UUID.randomUUID().toString());
        return userDetails;
    }

}

2、配置ExceptionTranslationFilter自定义异常处理器

  • 因AuthorizationFilter授权失败时会抛出异常,该异常由ExceptionTranslationFilter处理,所以要配置自定义的异常处理器。

  • 自定义AccessDeniedHandler和AuthenticationEntryPoint异常处理器(可以用一个类实现认证授权相关的所有接口,也可以使用多个类分别实现)。

package com.yu.demo.spring.impl;


import com.yu.demo.entity.ApiResp;
import com.yu.demo.entity.UserDetailsImpl;
import com.yu.demo.util.SpringUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

@Component
public class LoginResultHandler implements AuthenticationSuccessHandler, LogoutSuccessHandler, AuthenticationEntryPoint, AuthenticationFailureHandler, AccessDeniedHandler {

    /**
     * 登录成功
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) authentication;
        UserDetailsImpl userDetailsImpl = (UserDetailsImpl) usernamePasswordAuthenticationToken.getPrincipal();
        //token返回到前端
        SpringUtil.respJson(response, ApiResp.success(userDetailsImpl.getToken()));
    }

    /**
     * 登录失败
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.loginFailure());
    }

    /**
     * 登出成功
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.success());
    }

    /**
     * 未登录调用需要登录的接口时
     */
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.notLogin());
    }

    /**
     * 已登录调用未授权的接口时
     */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.forbidden());
    }
}

  • 配置异常处理:

                //异常处理配置
                .exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer
                        //授权失败处理器(登录账号访问未授权的资源时)
                        .accessDeniedHandler(loginResultHandler)
                        //登录失败处理器(匿账号访问需要未授权的资源时)
                        .authenticationEntryPoint(loginResultHandler))

3、HTTP请求授权配置

  • 本文使用最新的authorizeHttpRequests(AuthorizationFilter+AuthorizationManager)配置,不在使用authorizeRequests(FilterSecurityInterceptor+AccessDecisionManager+AccessDecisionVoter)

  • 请求和授权配置成对出现,配置在前的优先级更高

  • 请求种类antMatchers:Ant风格的路径模式,?(匹配一个字符)、*(匹配零个或多个字符,但不包括目录分隔符)、**(匹配零个或多个目录)mvcMatchers:Spring MVC的路径模式,支持路径变量和请求参数regexMatchers:正则表达式路径模式requestMatchers:实现RequestMatcher自定义匹配逻辑anyRequest:未匹配的其他请求,只能有一个且只能放在最后

  • 授权种类permitAll:匿名或登录用户都允许访问denyAll:匿名和登录用户都不允许访问hasAuthority:有配置的权限允许访问,AuthorityAuthorizationManager校验hasRole:有配置的角色允许访问,ROLE_{配置角色}与用户权限匹配,AuthorityAuthorizationManager校验hasAnyAuthority:有配置的任意一个权限的允许访问,AuthorityAuthorizationManager校验hasAnyRole:有配置的任意一个角色允许访问,ROLE_{配置角色}与用户权限匹配,AuthorityAuthorizationManager校验authenticated:已认证(不包括匿名)的允许访问,AuthenticatedAuthorizationManager校验access:自定义授权处理

  • 因authorizeHttpRequests不支持使用anonymous()的方式配置匿名访问,未自定义匿名角色时可以通过hasRole("ANONYMOUS")或者hasAuthority("ROLE_ANONYMOUS")或其他类似的方式实现允许匿名请求的设置

  • http请求授权配置

                //http请求授权
                .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
                        //不允许访问
                        .antMatchers("/test/deny")
                        .denyAll()
                        //允许匿名访问
                        .antMatchers("/test/anonymous")
                        .hasRole("ANONYMOUS")
                        //允许访问
                        .antMatchers("/test/permit")
                        .permitAll()
                        //测试使用:拥有ADMIN角色
                        .antMatchers("/test/admin")
                        //拥有ROLE_ADMIN权限,配置的角色不能以ROLE_作为前缀
                        .hasRole("ADMIN")
                        //测试使用:拥有OPERATE权限
                        .antMatchers("/test/operate")
                        //拥有OPERATE权限
                        .hasAuthority("OPERATE")
                        //其他的任何请求
                        .anyRequest()
                        //需要认证,且不能是匿名
                        .authenticated())

  • 完整过滤器链配置

package com.yu.demo.config;

import com.yu.demo.spring.filter.RestfulLoginConfigurer;
import com.yu.demo.spring.filter.RestfulUsernamePasswordAuthenticationFilter;
import com.yu.demo.spring.impl.LoginResultHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.context.SecurityContextRepository;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    //登录参数用户名
    private static final String LOGIN_ARG_USERNAME = "username";
    //登录参数密码
    private static final String LOGIN_ARG_PASSWORD = "password";
    //登录请求类型
    private static final String LOGIN_HTTP_METHOD = HttpMethod.POST.name();
    //登录请求地址
    private static final String LOGIN_URL = "/login";
    //登出请求地址
    private static final String LOGOUT_URL = "/logout";

    @Autowired
    private LoginResultHandler loginResultHandler;
    @Autowired
    private SecurityContextRepository securityContextRepository;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                //禁用UsernamePasswordAuthenticationFilter、DefaultLoginPageGeneratingFilter、DefaultLogoutPageGeneratingFilter
                .formLogin(FormLoginConfigurer::disable)
                //禁用BasicAuthenticationFilter
                .httpBasic(HttpBasicConfigurer::disable)
                //禁用CsrfFilter
                .csrf(CsrfConfigurer::disable)
                //禁用SessionManagementFilter
                .sessionManagement(SessionManagementConfigurer::disable)
                //异常处理配置
                .exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer
                        //授权失败处理器(登录账号访问未授权的资源时)
                        .accessDeniedHandler(loginResultHandler)
                        //登录失败处理器(匿账号访问需要未授权的资源时)
                        .authenticationEntryPoint(loginResultHandler))
                //http请求授权
                .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
                        //不允许访问
                        .antMatchers("/test/deny")
                        .denyAll()
                        //允许匿名访问
                        .antMatchers("/test/anonymous")
                        .hasRole("ANONYMOUS")
                        //允许访问
                        .antMatchers("/test/permit")
                        .permitAll()
                        //测试使用:拥有ADMIN角色
                        .antMatchers("/test/admin")
                        //拥有ROLE_ADMIN权限,配置的角色不能以ROLE_作为前缀
                        .hasRole("ADMIN")
                        //测试使用:拥有OPERATE权限
                        .antMatchers("/test/operate")
                        //拥有OPERATE权限
                        .hasAuthority("OPERATE")
                        //其他的任何请求
                        .anyRequest()
                        //需要认证,且不能是匿名
                        .authenticated())
                //安全上下文配置
                .securityContext(securityContextCustomizer -> securityContextCustomizer
                        //设置自定义securityContext仓库
                        .securityContextRepository(securityContextRepository)
                        //显示保存SecurityContext,官方推荐
                        .requireExplicitSave(true))
                //登出配置
                .logout(logoutCustomizer -> logoutCustomizer
                        //登出地址
                        .logoutUrl(LOGOUT_URL)
                        //登出成功处理器
                        .logoutSuccessHandler(loginResultHandler)
                )
                //注册自定义登录过滤器的配置器:自动注册自定义登录过滤器;
                //需要重写FilterOrderRegistration的构造方法FilterOrderRegistration(){},在构造方法中添加自定义过滤器的序号,否则注册不成功
                .apply(new RestfulLoginConfigurer<>(new RestfulUsernamePasswordAuthenticationFilter(LOGIN_ARG_USERNAME, LOGIN_ARG_PASSWORD, LOGIN_URL, LOGIN_HTTP_METHOD), LOGIN_URL, LOGIN_HTTP_METHOD))
                //设置登录地址:未设置时系统默认生成登录页面,登录地址/login
                .loginPage(LOGIN_URL)
                //设置登录成功之后的处理器
                .successHandler(loginResultHandler)
                //设置登录失败之后的处理器
                .failureHandler(loginResultHandler);

        //创建过滤器链对象
        return httpSecurity.build();
    }

}

三、测试接口

1、测试类

package com.yu.demo.web;

import com.yu.demo.entity.ApiResp;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/hello")
    public ApiResp hello() {
        return ApiResp.success("hello");
    }

    /**
     * 匿名允许访问接口地址
     */
    @GetMapping("/anonymous")
    public ApiResp anonymous() {
        return ApiResp.success("anonymous");
    }

    /**
     * 禁止访问接口地址
     */
    @GetMapping("/deny")
    public ApiResp deny() {
        return ApiResp.success("deny");
    }

    /**
     * 允许访问接口地址
     */
    @GetMapping("/permit")
    public ApiResp permit() {
        return ApiResp.success("permit");
    }

    /**
     * 拥有ADMIN角色或ROLE_ADMIN权限允许访问接口地址
     */
    @GetMapping("/admin")
    public ApiResp admin() {
        return ApiResp.success("admin");
    }

    /**
     * 拥有OPERATE权限的允许访问接口地址
     */
    @GetMapping("/operate")
    public ApiResp operate() {
        return ApiResp.success("operate");
    }

}

2、测试

1、登录获取token

2、admin接口测试

3、其他接口不在一一测试,有疑问或问题评论或私聊

四、总结

  1. 授权是拿用户的权限和可以访问接口的权限进行匹配,匹配成功时授权成功,匹配失败时授权失败

  2. 用户的权限对象是SimpleGrantedAuthority,字符串属性role

  3. 接口的role权限会通过ROLE_{role}转化为SimpleGrantedAuthority及其字符串属性role

  4. 接口的authority权限会直接转化为SimpleGrantedAuthority及其字符串属性role

  5. 拥有ROLE_ANONYMOUS权限或者ANONYMOUS角色可以访问匿名接口

文章转载自:那你为何对我三笑留情

原文链接:十、Spring Boot集成Spring Security之HTTP请求授权 - 那你为何对我三笑留情 - 博客园

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

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

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

相关文章

Docker搭建基于Rust语言的云原生可观测平台OpenObserve

文章目录 前言1. 安装Docker2. 创建并启动OpenObserve容器3. 本地访问测试4. 公网访问本地部署的OpenObserve4.1 内网穿透工具安装4.2 创建公网地址 5. 配置固定公网地址 前言 嘿&#xff0c;朋友们&#xff0c;今天我们要聊聊一个能让你在云原生世界里大展身手的秘密武器——…

批量给 Word 添加或设置页眉页脚/页码

在 Word 文档中我们可以设置各种各样的页眉页脚信息&#xff0c;比如设置页码信息、在页眉页脚中插入公司的 logo 信息、联系方式信息等等。当我们有大量的文档需要设置或者修改页眉页脚的时候&#xff0c;今天介绍的方法就可以帮我们快速的完成。 使用场景 批量给 Word 文档设…

安卓 SpannableString的使用 给文字末尾几个小尾巴

效果一&#xff1a; 效果二&#xff1a; 其实我们知道如果想实现效果一很简单&#xff0c;两个textview横向布局一下就可以了&#xff0c;但是如果想要是实现效果二怎么办呢。据我所知对于前端开发来说其实效果二也很简单&#xff0c;前端甚至可以轻松实现富文本&#xff0c;但…

opencv:距离变换 cv2.distanceTransform

函数 cv2.distanceTransform() 用于计算图像中每一个非零点像素与其最近的零点像素之间的距离&#xff08;Distance Transform&#xff0c; DT算法&#xff09;,输出的是保存每一个非零点与最近零点的距离信息&#xff1b;图像上越亮的点&#xff0c;代表了离零点的距离越远。 …

ArcGIS Pro中打造精美高程渲染图的全面指南

一、引言 高程渲染图是地理信息系统&#xff08;GIS&#xff09;中用于展示地形地貌的重要工具。一张精美的高程渲染图&#xff0c;不仅能够清晰地呈现地形的起伏变化&#xff0c;还能增强视觉表现力&#xff0c;使得数据更加生动、直观。ArcGIS Pro作为一款强大的GIS软件&…

[Python学习日记-84] 进程理论

[Python学习日记-84] 进程理论 简介 进程的概念 并发与并行的区别 进程并发的实现 简介 进程理论是计算机科学中一种重要的概念&#xff0c;用来描述操作系统中执行的程序实例。在操作系统中&#xff0c;每个程序的执行被称为一个进程。进程理论研究进程的创建、调度、通信…

信息系统的安全防护

文章目录 引言**1. 物理安全****2. 网络安全****3. 数据安全****4. 身份认证与访问控制****5. 应用安全****6. 日志与监控****7. 人员与管理制度****8. 其他安全措施****9. 安全防护框架**引言 从技术、管理和人员三个方面综合考虑,构建多层次、多维度的安全防护体系。 信息…

分布式主键生成服务

目录 一、使用线程安全的类——AtomicInteger或者AtomicLong 二、主键生成最简单写法(不推荐) 三、主键生成方法一&#xff1a;Long型id生成——雪花算法 四、主键生成方法二&#xff1a;流水号 (一)流水号概述 (二)添加配置 1.pom.xml 2.application.properties 3.创…

Linux 环境“从零”部署 MongoDB 6.0:mongosh 安装与数据操作全攻略

前提 完成linux平台部署MongoDB【部署教程】且完成mongosh的安装 由于本人使用的是6.0版本的MongoDB&#xff0c;新版本 MongoDB&#xff08;尤其是 6.0 及以上版本&#xff09;已经不再默认捆绑传统的 mongo shell&#xff0c;而改用新的 MongoDB Shell&#xff08;mongosh&am…

使用Docker将ros1自定义消息通过rosjava_bootstrap生成jar包

文章目录 预准备环境rosjava_bootstrap坏消息好消息 环境安装docker安装rosjava_bootstrap仓库rosjava_center仓库修改rosjava_bootstrap代码拉取docker镜像放置自己的自定义消息 启动docker编译 预准备环境 rosjava_bootstrap rosjava_bootstrap是将自定义的ROS消息生成java…

RNN,LSTM,GRU三种循环网络的对比

1. 三种网络在准确率的对比 2. 三种网络在损失值的对比 3. 三种网络在计算时间的对比 4. RNN&#xff08;传统循环神经网络&#xff09; 主要特点&#xff1a; RNN 是最基础的循环神经网络&#xff0c;通过 递归 计算每个时间步的输出。在每个时间步&#xff0c;RNN 会将当前…

hackmyvm-hero

信息收集 ┌──(root㉿kali)-[/home/kali/Desktop/hackmyvm] └─# arp-scan -I eth1 192.168.56.0/24 Interface: eth1, type: EN10MB, MAC: 00:0c:29:34:da:f5, IPv4: 192.168.56.103 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192…

纷析云:赋能企业财务数字化转型的开源解决方案

在企业数字化转型的浪潮中&#xff0c;财务管理的高效与安全成为关键。纷析云凭借其开源、安全、灵活的财务软件解决方案&#xff0c;为企业提供了一条理想的转型路径。 一、开源的力量&#xff1a;自主、安全、高效 纷析云的核心优势在于其100%开源的财务软件源码。这意味着…

异常c/c++

目录 1.c语言传统处理错误方式 1、终止程序 2、返回错误码 2.c异常概念 3.异常的使用 3.1异常的抛出与捕获 3.2异常安全&#xff08;还有一些异常重新抛出&#xff09; 3.3异常规范 4.自定义异常体系 5.c标准库的异常体系 6.异常优缺点 1、优点 2、缺点 7、补充 1.…

SAP-ABAP:使用ST05(SQL Trace)追踪结构字段来源的步骤

ST05 是 SAP 提供的 SQL 跟踪工具&#xff0c;可以记录程序运行期间所有数据库操作&#xff08;如 SELECT、UPDATE、INSERT&#xff09;。通过分析跟踪结果&#xff0c;可以精准定位程序中结构字段对应的数据库表。 步骤1&#xff1a;激活ST05跟踪 事务码 ST05 → 点击 Activa…

sklearn中的决策树-分类树:剪枝参数

剪枝参数 在不加限制的情况下&#xff0c;一棵决策树会生长到衡量不纯度的指标最优&#xff0c;或者没有更多的特征可用为止。这样的决策树 往往会过拟合。为了让决策树有更好的泛化性&#xff0c;我们要对决策树进行剪枝。剪枝策略对决策树的影响巨大&#xff0c;正确的剪枝策…

安宝特科技 | Vuzix Z100智能眼镜+AugmentOS:重新定义AI可穿戴设备的未来——从操作系统到硬件生态,如何掀起无感智能革命?

一、AugmentOS&#xff1a;AI可穿戴的“操作系统革命” 2025年2月3日&#xff0c;Vuzix与AI人机交互团队Mentra联合推出的AugmentOS&#xff0c;被业内视为智能眼镜领域的“iOS时刻”。这款全球首个专为智能眼镜设计的通用操作系统&#xff0c;通过三大突破重新定义了AI可穿戴…

基于Rook的Ceph云原生存储部署与实践指南(上)

#作者&#xff1a;任少近 文章目录 1 Ceph环境准备2 rook部署ceph群集2.1 Rook 帮助地址2.2 安装ceph2.3 获取csi镜像2.4 Master参加到osd2.5 设置默认存储 3 Rook部署云原生RBD块存储3.1 部署storageclass资源3.2 部署WordPress使用RBD3.3 WordPress访问 4 Rook部署云原生RGW…

2月27(信息差)

&#x1f30d;雷军超钟睒睒登顶中国首富 身家近4400亿元 &#x1f384;全球AI大混战升温&#xff01;超越Sora的阿里万相大模型开源 家用显卡都能跑 ✨小米15 Ultra、小米SU7 Ultra定档2月27日 雷军宣布&#xff1a;向超高端进发 1.刚刚&#xff01;DeepSeek硬核发布&#xff…

【Linux】文件系统深度解析:从基础到高级应用

&#x1f3ac; 个人主页&#xff1a;努力可抵万难 &#x1f4d6; 个人专栏&#xff1a;《C语法》《Linux系列》《数据结构及算法》 ⛰️ 路虽远&#xff0c;行则将至 目录 &#x1f4da;一、引言&#xff1a;文件系统的核心作用与历史演进 &#x1f4d6;1.文件系统的定义与功…