SpringSecurity学习(七)授权
授权
什么是权限管理
权限管理核心概念
SpringSecurity权限管理策略
基于URL地址的权限管理
基于方法的权限管理
一、权限管理

二、授权核心概念
在认证的过程成功之后会将当前用户登录信息保存到Authentication对象中,Authentication对象中有一个getAuthorities()方法,用来返回当前登录用户具备的权限信息。该方法返回值是Collections<extends GrantedAuthority>,当需要进行权限判断时,就根据集合返回权限信息调用对应方法进行判断。
public interface Authentication extends Principal, Serializable {Collection<? extends GrantedAuthority> getAuthorities();// 省略
}
那么针对返回值应该如何理解?是权限还是角色?
RBAC(Role/Resource Base Access Controll)
针对收取按可以是基于角色权限管理和基于资源权限管理,从设计层面来说:角色和权限是俩个不同的东西。权限是一些具体的操作,角色是一些权限的集合。eg:READ_BOOK和ROLE_ADMIN是完全不同的。因此至于返回值是什么应当取决于业务的设计。
- 基于角色权限设计:
用户<=>角色<=>资源三者关系,返回就是用户的角色。 - 基于资源权限设计:
用户<=>权限<=>资源三者关系,返回就是用户的权限。 - 基于角色和资源权限设计:
用户<=>角色<=>权限<=>资源的关系,返回统称为用户的权限。
这里统称为权限,是因为代码层面来说权限和角色没有太大的不同都是权限。其在SpringSecurity中处理方式也基本相同。唯一的区别是会自动给角色多加一个ROLE_前缀。
三、两种权限管理策略
SpringSecurity主要提供俩种权限管理策略:
可以访问系统中的那些资源(URL、Method)
- 基于过滤器(URL)的权限管理(FilterSecurityInterceptor)
基于过滤器的权限管理主要用来拦截HTTP请求,拦截下来后,根据http请求地址进行权限校验。 - 基于AOP(Method)的权限管理(MethodSecurityInterceptor)
基于AOP权限管理主要用来处理方法级别的权限问题。当需要调用某一方法时,通过aop将操作拦截,然后判断用户是否具备相关权限。
1、基于URL权限管理
1.1 案例
编写HiController
@RestController
public class HiController {@RequestMapping("/")public String home() {return "<h1>HI SPRING SECURITY</hi>";}@RequestMapping("/admin")public String admin() {return "<h1>HI SPRING ADMIN</hi>";}@RequestMapping("/user")public String user() {return "<h1>HI USER</hi>";}@RequestMapping("/getInfo")public Authentication getInfo() {return SecurityContextHolder.getContext().getAuthentication();}
}
编写SecurityConfig
@EnableWebSecurity
public class SecurityConfig {@Beanpublic UserDetailsService userDetailsService(){InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN","USER").build());manager.createUser(User.withUsername("whx").password("{noop}123").roles("USER").build());manager.createUser(User.withUsername("dy").password("{noop}123").authorities("READ_INFO").build());return manager;}@Beanpublic SecurityFilterChain configure(HttpSecurity http) throws Exception {http.authorizeHttpRequests(req -> {req.mvcMatchers("/admin").hasRole("ADMIN");req.mvcMatchers("/user").hasRole("USER");req.mvcMatchers("/getInfo").hasAuthority("READ_INFO");req.anyRequest().authenticated();});http.formLogin();http.csrf().disable();return http.build();}
}
1.2 权限表达式
public interface SecurityExpressionOperations {// 获取用户权限信息Authentication getAuthentication();// 当前用户是否具备指定权限boolean hasAuthority(String authority);// 当前用户是否具备指定权限中的任意一个boolean hasAnyAuthority(String... authorities);// 当前用户是否具备指定角色boolean hasRole(String role);// 当前用户是否具备指定角色任意一个boolean hasAnyRole(String... roles);// 放行所有请求boolean permitAll();// 拒绝所有请求boolean denyAll();// 当前用户是否匿名用户boolean isAnonymous();// 当前用户是否已经认证成功boolean isAuthenticated();// 当前用户是否通过RememberMe记住我自动登录boolean isRememberMe();// 当前用户是否既不是宁ing用户也不是通过rememberMe自动登录boolean isFullyAuthenticated();// 当前用户是否具备指定目标的指定权限信息boolean hasPermission(Object target, Object permission);// 当前用户是否具备指定目标的指定权限信息boolean hasPermission(Object targetId, String targetType, Object permission);
}
1.3 URL匹配规则:antMatchers、mvcMatchers、regexMatchers
antMatchers和mvcMatchers的区别,在于mvcMatchers更加强大通用,而regexMatchers的好处是支持正则表达式。

2. 基于方法的权限管理
基于方法的权限管理通过AOP来实现,SpringSecurity中通过MethodSecurityInterceptor来提供相关实现。不同在于FilterSecurityInterceptor只是在请求之前进行前置处理,MethodSecurityInterceptor除了前置处理之外还可以进行后置处理。前置处理就是在请求之前判断是否具有响应权限,而后置处理则是对方法执行结果进行二次过滤。前置处理和后置处理对应了不同的实现类。
@EnableGlobalMethodSecurity
@EnableGlobalMethodSecurity注解用来开启权限,用法如下:
@EnableWebSecurity
// 开启全局方法权限配置,仅可能的显示配置三个属性为true
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig2 {
prePostEnabled:开启SpringSecurity提供的四个权限注解@PostAuthorize、@PostFilter、@PreAuthorize、@PreFiltersecuredEnabled:开启SpringSecurity提供的@Secured注解支持,该注解不支持权限表达式jsr250Enabled:开启JSR-250提供的注解,主要是@DenyAll、@PermitAll、@RolesAll,同样的这些注解也不支持权限表达式。
| 注解 | 含义 |
|---|---|
@PostAuthorize | 在目标方法执行之后进行权限校验 |
@PostFilter | 在目标方法执行之后对返回结果进行过滤 |
@PreAuthorize | 在目标方法执行之前进行权限校验 |
@PreFilter | 在目标方法执行之前对方法参数进行过滤 |
| @Secured | 访问目标方法必须具备对应的角色 |
| @DenyAll | 拒绝所有访问 |
| @PermitAll | 允许所有访问 |
| @RolesAll | 访问目标方法必须具备对应的角色 |
这些基于方法的权限管理相关的注解,由于后四个不常用,一般来说只需要设置prePostEnabled =true即可
权限表达式:例子hasRole("admin")
案例:
编写SecurityConfig
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig2 {@Beanpublic UserDetailsService userDetailsService(){InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN","USER").build());manager.createUser(User.withUsername("whx").password("{noop}123").roles("USER").build());manager.createUser(User.withUsername("dy").password("{noop}123").authorities("READ_INFO").build());return manager;}@Beanpublic SecurityFilterChain configure(HttpSecurity http) throws Exception {http.authorizeHttpRequests(req -> {req.anyRequest().authenticated();});http.formLogin();http.csrf().disable();return http.build();}
}
编写T2Controller
@RestController
@RequestMapping("t2")
public class T2Controller {@PreAuthorize("hasRole('ADMIN') and authentication.name == 'root'")@RequestMapping("/")public String home() {return "<h1>HI SPRING SECURITY</hi>";}// http://localhost:8888/t2/name?name=root@PreAuthorize("authentication.name == #name")@RequestMapping("/name")public String admin(String name) {return "<h1>HI SPRING " + name + "</hi>";}// [ { "id":"1","username":"huathy" },
// { "id":"2","username":"dy" } ]@PreFilter(value = "filterObject.id%2 != 0", filterTarget = "users") //filterTarget参数必须是集合类型@RequestMapping("/add")public List<User> add(@RequestBody List<User> users) {List<User> result = new ArrayList<>();for (User user : users) {result.add(User.build(user.getId(), user.getUsername()));}return result;}// http://localhost:8888/t2/userId?id=1@PostAuthorize("returnObject.id == 1")@RequestMapping("/userId")public User userId(Integer id) {User user = User.build(id, "HUATHY");return user;}@PostFilter("filterObject.id%2 == 0")@RequestMapping("/lists")public List<User> getAllUser() {List<User> users = new ArrayList<>();for (int i = 0; i < 10; i++) {users.add(User.build(i, "嘻嘻" + i));}return users;}@PreAuthorize("hasAuthority('READ_INFO')")@RequestMapping("/getInfo")public Object getInfo() {return SecurityContextHolder.getContext().getAuthentication().getPrincipal();}/* === 以下是不常用的 只做演示 === */// 具备其中一个即可@Secured({"ROLE_ADMIN", "ROLE_USER"})@RequestMapping("/secured")public String secured() {return "<h1>HUATHY</h1>";}@PermitAll@RequestMapping("permitAll")public String permitAll() {return "<h1>permitAll</h1>";}@DenyAll@RequestMapping("DenyAll")public String DenyAll() {return "<h1>DenyAll</h1>";}@RolesAllowed({"ROLE_ADMIN", "ROLE_USER"})// 具备其中一个即可@RequestMapping("rolesAllowed")public String rolesAllowed() {return "<h1>rolesAllowed</h1>";}
}
四、授权的原理分析

ConfigAttribute在springsecurity中,用户请求一个资源(通常是一个接口或者Java方法)需要的角色会被封装成一个ConfigAttribute对象,在ConfigAttribute中只有一个getAttribute方法,该方法赶回一个String字符串(角色名称)。一般的角色名称都带有一个ROLE_前缀,投票器AccessDecisionVoter所做的事情,其实就是比较用户所具有的角色和请求某个资源所需要的ConfigAttribute之间的关系。AccessDecisionVoter和AccessDecisionManager都有众多实现类。在AccessDecisionManager中会挨个遍历AccessDecisionVoter,进而决定是否允许用户方法,因而AccessDecisionVoter和AccessDecisionManager俩者关系类似于AuthenticationProvicder和ProviderManager的关系。
授权实战—权限模型说明1
在前面的案例中,我们配置的URL拦截规则和URL所需要的权限都是通过代码配置的,这样过于死板。如果需要动态的管理权限规则,我们可以将URL拦截规则和访问URL所需的权限都保存到数据库中,这样在不修改代码的情况下只需要吸怪数据库即可对权限进行调整。
用户 < --用户角色表-- > 角色 < --角色菜单表-- > 菜单
库表设计
create table menu
(id int auto_incrementprimary key,pattern varchar(100) null
)comment '菜单表';create table menu_role
(id int auto_incrementprimary key,rid int not null,mid int not null
);create table role
(id int auto_incrementprimary key,name varchar(255) null,name_cn varchar(255) null
);create table user
(id int auto_incrementprimary key,username varchar(255) null,password varchar(255) null,accountNonExpired int(1) null,accountNunLocked int(1) null,credentialsNonExpired int(1) null,enable int(1) null
);create table user_role
(id int auto_incrementprimary key,uid int null,rid int null
);
数据
insert into role values (101,'superadmin','超级管理员');
insert into role values (102,'admin','管理员');
insert into role values (103,'user','普通用户');insert into user values (1001,'huathy','{noop}123',0,0,0,0);
insert into user values (1002,'whx','{noop}123',0,0,0,0);
insert into user values (1003,'dy','{noop}123',0,0,0,0);insert into user_role values (0,1001,101);
insert into user_role values (0,1002,102);
insert into user_role values (0,1003,103);insert into menu values (1,'/admin/**');
insert into menu values (2,'/user/**');
insert into menu values (3,'/guest/**');insert into menu_role values (0,101,1);
insert into menu_role values (0,102,2);
insert into menu_role values (0,103,3);
实现
本文只展示了核心代码,详细的参考附录1。
1. MyUserDetailsService
实现自定义UserDetailsService,从数据库获取用户信息。
@Component
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userMapper.getUserByUname(username);if (ObjectUtils.isEmpty(user)) {throw new UsernameNotFoundException("用户名不正确");}List<Role> roles = userMapper.getRolesByUid(user.getId());user.setRoles(roles);return user;}
}
2. 编写SecurityCfg配置,来自定义URL权限处理。
@EnableWebSecurity
public class SecurityCfg {@Autowiredprivate CustomerSecurityMetadataSource customerSecurityMetadataSource;@Beanpublic SecurityFilterChain configure(HttpSecurity http) throws Exception {// 1. 获取工厂对象ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);// 2. 设置自定义URL权限处理http.apply(new UrlAuthorizationConfigurer<>(applicationContext)).withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {@Overridepublic <O extends FilterSecurityInterceptor> O postProcess(O object) {object.setSecurityMetadataSource(customerSecurityMetadataSource);// 是否拒绝公共资源的访问object.setRejectPublicInvocations(false);return object;}});http.authorizeHttpRequests().anyRequest().authenticated();http.formLogin();http.csrf().disable();return http.build();}
}
3. 编写自定义权限元数据CustomerSecurityMetadataSource
需要注意的是此类中的SecurityConfig,是springSecurity官方的SecurityConfig。
@Component
public class CustomerSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {@AutowiredMenuService menuService;// 用来做路径比对AntPathMatcher antPathMatcher = new AntPathMatcher();/*** 自定义动态资源权限元数据信息** @param object* @return* @throws IllegalArgumentException*/@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {// 根据当前请求对象获取URIString requestURI = ((FilterInvocation) object).getRequest().getRequestURI();// 查询菜单对象List<Menu> allMenu = menuService.getList(null);for (Menu menu : allMenu) {if (antPathMatcher.match(menu.getPattern(), requestURI)) {String[] roles = new String[menu.getRoles().size()];for (int i = 0; i < menu.getRoles().size(); i++) {roles[i] = "ROLE_" + menu.getRoles().get(i).getName();}return SecurityConfig.createList(roles);}}return null;}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}@Overridepublic boolean supports(Class<?> clazz) {return FilterInvocation.class.isAssignableFrom(clazz);}
}
4. MenuService
@Service
public class MenuService {@Autowiredprivate MenuMapper menuMapper;@Autowiredprivate MenuRoleMapper menuRoleMapper;public List<Menu> getList(Object o) {List<Menu> menus = menuMapper.selectList(null);for (Menu menu : menus) {List<Role> roles = menuRoleMapper.getAllMenuRoles(menu.getId());if (!CollectionUtils.isEmpty(roles)) {menu.setRoles(roles);}}return menus;}
}
踩坑
这里有点坑的地方就是SpringSecurity会给角色的权限自动加上ROLE_,即便我加了前缀,他还是会自动加一次。这导致了这里equls的时候匹配失败。所以这里我们取消数据库中的前缀,这样查询出来的用户的角色是不带前缀的(eg:ADMIN)而我们在查询菜单的角色的构建CustomerSecurityMetadataSource元数据的时候给手动加上前缀ROLE_,就像这样子:roles[i] = "ROLE_" + menu.getRoles().get(i).getName();。

附录:
- 本文涉及代码部分https://gitee.com/huathy/study-all/tree/master/spring_security_study
相关文章:
SpringSecurity学习(七)授权
授权 什么是权限管理 权限管理核心概念 SpringSecurity权限管理策略 基于URL地址的权限管理 基于方法的权限管理 一、权限管理 二、授权核心概念 在认证的过程成功之后会将当前用户登录信息保存到Authentication对象中,Authentication对象中有一个getAuthorities…...
【Vue3】模板语法
🏆今日学习目标:模板语法 😃创作者:颜颜yan_ ✨个人格言:生如芥子,心藏须弥 ⏰本期期数:第三期 🎉专栏系列:Vue3 文章目录前言声明响应式状态插值文本Attributeÿ…...
Linux基础
环境搭建:linux安装、远程连接常用命令:文件、目录、拷贝、移动、打包、压缩、文本编辑安装软件:文件上传、jdk、tomcat、mysql项目部署:Java应用、Python应用、日志查看、系统管理、用户权限Linux是一套免费使用、自由传播的操作…...
Spark-序列化、依赖关系、持久化
序列化 闭包检查 序列化方法和属性 依赖关系 RDD 血缘关系 RDD 窄依赖 RDD 宽依赖 RDD 任务划分 RDD 持久化 RDD Cache 缓存 RDD CheckPoint 检查点 缓存和检查点区别 序列化 闭包检查 从计算的角度, 算子以外的代码都是在 Driver 端执行, 算子里面的代码都是在 E…...
蓝桥杯刷题冲刺 | 倒计时16天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.青蛙跳杯子1.青蛙跳杯子 题目 链接: 青蛙跳杯子 - 蓝桥云课 (lanqiao.cn) X 星球的…...
Java设计模式-12 、建造者模式
建造者模式 (将一个 复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。) 建造者模式是一种创建型的模式,有一些对象的创建过程new 是很繁杂的。 什么时候去使用建造者模式 由上文可以得出在一些对象创建…...
一款全新的基于GPT4的Python神器,关键还免费
chartgpt大火之后,随之而来的就是一大类衍生物了。 然后,今天要给大家介绍的是一款基于GPT4的新一代辅助编程神器——Cursor。 它最值得介绍的地方在于它免费,我们可以直接利用它来辅助我们编程,真正做到事半功倍。 注意&#…...
上岸整理:2023前端面试题-vue,小程序,js,css
前端: 今年疫情结束后,前端行情不好,竞争压力很大,现在整理下个人认为面试很频繁的前端问题。 正题:无分类,因为面试官的问题也是随机的 一、基础 1、浏览器常见的报错信息与含义 2、304与204的区别&am…...
Linux下LED设备驱动开发(LED灯实现闪烁)
文章目录一、配置连接说明二、更新设备树(1)将led灯引脚添加到pinctrl子系统(2)设备树中添加LDE灯的设备树节点(3)编译更新设备树三、驱动开发与测试(1)编写设备驱动代码(…...
JavaEE-多线程中wait和notify都有哪些区别?
更多内容请点击了解 本篇文章将详细讲述wait和notify的区别,请往下看 目录 更多内容请点击了解 文章目录 一、wait和notify概念 二、wait()方法详解 三、notify()方法详解 代码如下: 3.1notifyAll()详解 四、wait和sleep的对比 一、wait和notif…...
JavaScript实现列表分页(小白版)
组件用惯了,突然叫你用纯cssJavaScript写一个分页,顿时就慌了。久久没有接触js了,不知道咋写了。本文章也是借与参考做的一个demo案例,小白看了都会的那种。咱们就以ul列表为例进行分页: 首先模拟的数据列表是这样的&a…...
Python调用GPT3.5接口的最新方法
GPT3.5接口调用方法主要包括openai安装、api_requestor.py替换、接口调用、示例程序说明四个部分。 1 openai安装 Python openai库可直接通过pip install openai安装。如果已经安装openai,但是后续提示找不到ChatCompletion,那么请使用命令“pip instal…...
Java开发 - 拦截器初体验
目录 前言 拦截器 什么是拦截器 拦截器和过滤器 Spring MVC的拦截器 Mybatis的拦截器...
【数据仓库-7】-- 使用维度建模的一些缘由
维度建模是一种用于设计数据仓库和商业智能系统的方法。以下是选择维度建模的两类理由。 1.传统方法,有背书且可靠 易于理解和使用:维度建模使用直观的图形和术语,使得非技术人员也能够理解和使用数据仓库和商业智能系统。 快速开发和部署:维度建模是一种迭代开发方法,能…...
【开发实践】在线考试系统(一) 生成错题知识点的思维导图
一、需求分析设计 笔者开发了一个在线考试系统,导师提出一个需求:添加对考试错题相关知识点的总结。 在question表中关联知识点的编号,题目可能关联多个知识点。这里笔者的设计是,只关联一个知识点,便于维护。 下面是知…...
Java Web 实战 17 - 计算机网络之传输层协议(2)
大家好 , 这篇文章继续给大家讲解 TCP 协议当中的一些操作 , 比如 : 滑动窗口、流量控制、拥塞控制、延时应答、捎带应答、面向字节流这几个提升 TCP 效率的操作 . 我们还会给大家分析 TCP 连接出现异常的时候 , 该如何处理 . 最后会将 TCP 和 UDP 进行比较 上一篇文章的链接也…...
MyBatis<3>:动态SQL的使用<if><trim><where><set><foreach>
动态SQL是MyBatis的强大特性之一,能够完成不同条件下不同的sql拼接。参考官方文档:https://mybatis.org/mybatis-3/zh/dynamic-sql.html<if>标签看这个场景,有必填字段 和 非必填字段 ,当字段不确定是否传入的时候ÿ…...
【超好懂的比赛题解】暨南大学2023东软教育杯ACM校赛个人题解
title : 暨南大学2023东软教育杯ACM校赛 题解 tags : ACM,练习记录 date : 2023-3-26 author : Linno 文章目录暨南大学2023东软教育杯ACM校赛 题解A-小王的魔法B-苏神的遗憾C-神父的碟D-基站建设E-小王的数字F-Uziの真身G-电子围棋H-二分大法I-丁真的小马朋友们J-单车运营K-超…...
go-zero学习及使用中遇到的问题
go-zero学习及使用中遇到的问题1 go-zero入门--单体服务demo1.1 单体服务【官方示例】1.1.1 创建greet服务1.1.2 目录结构1.1.3 编写逻辑1.1.4 启动并访问服务1.2 修改GET入参1.2.1 去除options限制的入参值1.2.2 重启并访问服务1.3 添加post请求【新增方法】1.3.1 修改 greet/…...
CCF-CSP认证 202303 500分题解
202303-1 田地丈量(矩阵面积交) 矩阵面积交x轴线段交长度*y轴线段交长度 线段交长度,相交的时候是min右端点-max左端点,不相交的时候是0 #include<bits/stdc.h> using namespace std; int n,a,b,ans,x,y,x2,y2; int f(in…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果