当前位置: 首页 > news >正文

RBAC 权限模型介绍

RBAC 权限:

一、关系:

在这里插入图片描述

这基于角色的访问控制的结构就叫RBAC结构。

二、RBAC 重要对象:

  • 用户(Employee):角色施加的主体;用户通过拥有某个或多个角色以得到对应的权限。
  • 角色(Role):表示一组权限的集合。
  • 权限(Permission):一个资源代表一个权限,是否能访问该资源,就是看是否有该权限。

三、权限认证方式:

--其实就是控制用户访问我们的controller层中的方法。--- 可以自定义一个注解,在需要权限的方法上面贴一个注解,在不需要权限的方法上就不贴注解。

例子:

 @ RequiredPermission (name="权限名称",expression="权限表达式") //--expression:是我们判断的依据。
public User list(){//查询所有学生}

步骤:

1.自定义注解;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredPermission {String name();//----权限名称String expression();//----权限表达式
}

2.将自定义注解贴在需要校验权限请求方法上面;

@RequstMapping("/list")
@RequiredPermission (name="员工新增或修改",expression="department:list")
public void saveOrUpdate(Role role) {if (role.getId() != null) { // 修改roleService.update(role);} else { // 新增roleService.save(role);}}

注意:

在权限系统开发中,一般权限系统有一个权限加载的功能。如果没有权限加载的功能的话,要把系统中所有的权限信息保存到数据库中。假如有 10个Controller接口,每个接口中有5个方法贴了自定义注解 @RequiredPermission 这样的话,得我们自己手动新增50次,而如果通过权限加载这个功能的话,就只需要点击这个功能按钮。这个按钮就可以把我们系统中所有贴了该注解的权限信息保存到数据库中。

权限加载的功能实现:

实现权限加载步骤:0. 先把数据库中所有的权限表达式查询出来
1. 先拿到所有Controller --> 通过Spring容器去拿
2. 通过contoller去拿到每一个controller方法
3. 通过方法去拿方法上的注解@RequirdPermission
4. 判断如果注解不为空并且注解中权限表达式不在数据库中,拿到注解中name,expression,封装到Permission对象中
5. 把permission对象保存到数据库权限信息表中

方式一:

@Service
@Slf4j
public class PermissionServiceImpl implements IPermissionService {@Autowiredprivate PermissionMapper permissionMapper;@Autowiredprivate ApplicationContext context; //获取Spring Ioc 容器@overridepublic void reload() {//0 先把数据库中所有的权限表达式查询出来List<String> expression =  permissionMapper.selectExpression();// 1 从spring容器中去拿到所有的controLLerMap<String, Obiect> beansithAnnotation = context.getBeanswithAnnotation(Controller.class);// map key 把controller类名小写字符串 ,value才是我们想要的controllerCollection<Object> controllers = beanswithAnnotation.values();// 2 根据controller 拿到每一个方法for (Object controller : controllers) {//拿到controller对象,通过反射,去获取字节码对象,在获取自身所有的方法Method[] methods = controller.getClass().getDeclaredMethods();for (Method method : methods){// 3 根据方法拿到方法上注解 @RequiredPermissionRequiredPermission annotation = method.getAnnotation(RequiredPermission.class);//判断注解是否为空,将在数据库中查询的权限表达式(第0步),与注解上表达式比对,获取list中不包含的权限表达式if(annotation!=null && !expression.contatins(annotation.expression())){// 4 判断注解是否为空 ,如果不为空数据封装到Permission对象中Permission p = new Permission();String name = annotation.name():String expression = annotation.expression();p.setExpression(expression);p.setName(name);// 5.把数据保存到数据库中permissionMapper.insert(p);}}}  
}

方式二:

@Service
@Slf4j
public class PermissionServiceImpl implements IPermissionService {@Autowiredprivate PermissionMapper permissionMapper;@Autowired//SpringMVC处理器映射器 ;通过处理器映射器获取Controller注解类中的方法信息,封装到HandlerMethod中private RequestMappingHandlerMapping requestMappingHandlerMapping;//SpringMVC处理器映射器 @overridepublic void reload() {//0 先把数据库中所有的权限表达式查询出来List<String> expression =  permissionMapper.selectExpression();// 在启动的时候requestMappingHandlerMapping 会把controller中所有的方法封装HandlerMethod 中Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();Collection<HandlerMethod> handlerMethods = map.values();for (HandlerMethod handlerMethod : handlerMethods) {// 到方法Method method = handlerMethod.getMethod();// 方法上的注解//3 根据方法拿到方法上注@RequiredPermissionRequiredPermission annotation = method.getAnnotation(RequiredPermission.class);if(annotation!=null 8& !expressions.contains(annotation.expression())){// 4 断注解是否为空 ,如果不为空把权限表达式数据封装到Permission对象中Permission p = new Permission();String name = annotation.name();String expression = annotation.expression();p.setName(name);p.setExpression(expression);// 5 把数据保存到数据库中permissionMapper.insert(p);}  }    }

权限新增删除:

               角色(Role)                    角色中间表(role_permission)            权限(permission)
即:其通过对角色:新增或删除权限:通过中间表来关联,并在中间表上新增和删除。在中间表上对外键 role_id与permission_id 之间的关系来增加或删除。用户(Employee)                 用户中间表(Employee_role)              角色(role)删除的时候,不仅删除用户或角色表中的信息,还要删除中间表中的信息。 

四、登录校验:

@Data
public class JsonResult {private boolean success;private String msg;public JsonResult(boolean success, String msg) {this.success = success;this.msg = msg;}
}

1.controller层:

@Controller
public class LoginController {@Autowiredprivate IEmployeeService employeeService;@Autowiredprivate IPermissionService permissionService;@RequestMapping("/login")@ResponseBody// {"success":true,"msg":"登录成功"}// {"success":false,"msg":"登录失败"}public JsonResult login(String username, String password) {try{Employee employee = employeeService.login(username, password);  return new JsonResult(true, "操作成功");}catch{e.printStackTrace();return new JsonResult(false, e.getMessage);}}

2.service层:

@Service
public class EmployeeServiceImpl implements IEmployeeService {@Overridepublic Employee login(String username, String password) {//校验参数非空判断if(StringUtils.isEmpty(username) || StringUtil.isEmpty(password)){throw new RuntimeException("账号或者密码不能为空");}//根据账号和密码上数据库中进行查询Employee employee = employeeMapper.selectByUsernameAndPassword(username, password);//如果为空抛出异常if(employee == null){throw new RuntimeException("账号或者密码错误");}return employee;}
}

3.前端:

$(".submitBtn").click(function () (// 发送ajax请求
$.post("/login",$("#loginForm").serialize(),function (data) {// data -> JsonResultif(data.success)(Location.href="/department/list";}else(Swal.fire({text: data.msg})}})
})

4.***注意这样的话还是不行,用户可以直接输入网址进入页面;所以要对url进行拦截,进行登录校验;

在这里插入图片描述

步骤:
第一步: 用户开始时登录的时候,如果用户登录成功,则把用户信息放到ssesion中。
第二步: 定义一个拦截器,对每一次用户请求的资源进行拦截。
第三步: 对拦截器进行配置。
(1)要拦截哪些资源?
(2)排除哪些资源路径?
第一步:用户信息放到ssesion中
@Controller
public class LoginController {@Autowiredprivate IEmployeeService employeeService;@Autowiredprivate IPermissionService permissionService;@RequestMapping("/login")@ResponseBody// {"success":true,"msg":"登录成功"}// {"success":false,"msg":"登录失败"}public JsonResult login(HtttpSession session, String username, String password) {try{Employee employee = employeeService.login(username, password);  //验证用户身份成功之后,将用户的信息保存到session中session.setAttribute("USER_IN_SESSION", employee);return new JsonResult(true, "操作成功");}catch{e.printStackTrace();return new JsonResult(false, e.getMessage);}}
第二步: 定义一个拦截器
public class CheckLoginInterceptor implements HandlerInterceptor{public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 0bject handler) throws{// 查询session 中保存用户信息Employee employee = (Employee) request.getSession().getAttribute( "USER_IN_SESSION");// 判断如果存在就放行,如果不存在跳转到登灵界面中if(employee!=nul1){return true;}response.sendRedirect( s:"/static/login.html"); //重定向到登录界面return false;}
第三步: 对拦截器进行配置:
@Configuration
public class WebConfig implements WebMvcConfigurer (@Beanpublic CheckLoginInterceptor checkLoginInterceptor(){return new CheckLoginInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry){       registry.addInterceptor(checkLoginInterceptor()). addPathPatterns("/**").//将拦截器注入到容器中excludePathPatterns("/static/**","/login","/logout");//放行那些资源}   

五、退出登录:

就是把session中登录的信息删除掉

@RequestMapping("/logout")
public String logout(HttpSession session){session.removeAttribute("USER_IN_SESSION");return "redirect:/static/login.html";//重定向
}

六、权限拦截:

分析:

在这里插入图片描述

步骤:

1 上session去获取当前登录用户信息
2 判断用户是否是超级管理员->如果是直接放行
3 拿到当前访问方法
4 获取方法上的注解@RequiredPermission
5 判断注解是否为空 ->直接放行
6 要把当前登录的用户所拥有的权限表达式给查询出来
7 如果注解中权限表示式是在从数据库中查询出来权限表达式集合中,说明用户拥有这个权限的 ->放行
8 没有这个权限跳转到没有权限界面中,进行拦截

1.定义一个拦截器

***拓展:
拦截器中 preHandle 方法的参数列表中有一个参数叫:Object handler 的参数:
通过反射 handler.getClass打印输出为:class org.springframework.web.method.HanderMethod(真实类型)
在 SpringMvc 中: 有一个处理器映射器。处理器映射器会把我们所有贴有Controller注解的类中的方法的信息,封装到HanderMethod中。所以在拦截器中我们可以通过 HanderMethod 这个去获得贴有Controller注解的类中的方法的信息
/**
*权限校验拦截器
**/
public class CheckPermissionIntercetor implements HandlerInterceptor{@Autowird   private IPermissionService permissionService;public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){// 1.从session中去拿当前用户登录信息Employee employee = (Employee) request.getSession().getAtribute("USER_IN_SESSION");// 2.判断是否是超级超级管理员if(employee.isAdmin()){return true;}// 3.获取到当前访问方法 不明白看拓展;HandlerMethod handlerMethod = (Handlerethod)handlerMethod method = handlerMethod.getMethod();// 4.通过方法拿方法上的注解RequiredPermission annotation = method.getAnnotation(RequiredPermission.class);// 5.判断注解如果为空表示访问方法时不需要权限if(annotation == null){return true;} // 6.把用户所拥有的所有权限表达式集合给查询出来List<String> list = permissionService.getExpressionByEmpId(employee.getId());// 7.判断注解中的权限表达式,是否在集合中if(list.contatins(annotation.expression())){return true;}// 8. 如果不包含则表示没有权限 跳转到没有权限的页面中response.sendRedirect("/permission/nopermission");//url从定向}

2.编写url从定向接口

@RequestMapping("/nopermission")
public String nopermission(){return "自己定义的页面路径";
}

3.将拦截器注入到容器中

@Configuration
public class WebConfig implements WebMvcConfigurer (//登录拦截器@Beanpublic CheckLoginInterceptor checkLoginInterceptor(){return new CheckLoginInterceptor();}//权限拦截器@Beanpublic CheckPermissionIntercetor checkPermissionIntercetor(){return new CheckPermissionIntercetor();}//登录拦截器注入@Overridepublic void addInterceptors(InterceptorRegistry registry){       registry.addInterceptor(checkLoginInterceptor()). addPathPatterns("/**").//将拦截器注入到容器中excludePathPatterns("/static/**","/login","/logout");//放行那些资源} //登录拦截器注入@Overridepublic void addInterceptors(InterceptorRegistry registry){       registry.addInterceptor(CheckPermissionIntercetor()). addPathPatterns("/**").//将拦截器注入到容器中excludePathPatterns("/static/**","/login","/logout");//放行那些资源} }    

七、统一异常处理

如何解决

  • 手动 try

    • 弊端是到处是重复代码,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。
  • 利用 Spring MVC 的方式

    • Spring MVC 为 Controller 处理方法执行出现异常提供了全局统一处理,可以使用 @ExceptionHandler 配合 @ControllerAdvice 注解实现异常处理,可减少代码量,提高拓展性和可维护性。
    • 添加处理控制器异常处理类,确保 Spring 配置中要能扫描到这个类。
    • 针对不同异常进行不同处理,针对不同处理方法响应的内容,需要进行不同处理,比如原来方法响应 HTML 依然响应 HTML,若原来方法响应 JSON 依然响应 JSON。
/**
* 对控制器进行增强处理
*/
@ControllerAdvice
public class RuntimeExceptionHandler {/*** 该方法是用于捕获并处理某种异常* e:现在出现的异常对象* method:现在出现异常的那个处理方法*/@ExceptionHandler(RuntimeException.class)public String exceptionHandler(RuntimeException e, HandlerMethod method, HttpServletResponse response) {e.printStackTrace(); // 方便开发的时候找 bug// 若原本控制器的方法是返回 JSON,现在出异常也应该返回 JSON// 获取当前出现异常的方法,判断是否有 ResponseBody 注解,有就代表需要返回 JSONif(method.hasMethodAnnotation(ResponseBody.class)){try {response.setContentType("application/json;charset=UTF-8");response.getWriter().print(JSON.toJSONString(new JsonResult(false, "系统异常,请联系管理员")));} catch (IOException e1) {e1.printStackTrace();}return null;}// 若原本控制器的方法是返回 HTML,现在也应该返回 HTMLreturn "common/error";}
}

2、自定义异常

在开发中还可以根据自己业务的异常情况来自定义业务逻辑异常类,一般继承于 java.lang.RuntimeException

public class LogicException extends RuntimeException {public LogicException(String errorMsg){super(errorMsg);}
}

比如虽然登录出错,也响应 JSON 数据,但其需要提示的消息更详细,所以通过自定义异常,再利用 Spring MVC 全局处理异常方式,针对这个异常进行专门处理。可参考另一篇博客SpringBoot统一异常处理

相关文章:

RBAC 权限模型介绍

RBAC 权限&#xff1a; 一、关系&#xff1a; 这基于角色的访问控制的结构就叫RBAC结构。 二、RBAC 重要对象&#xff1a; 用户&#xff08;Employee&#xff09;&#xff1a;角色施加的主体&#xff1b;用户通过拥有某个或多个角色以得到对应的权限。角色&#xff08;Role&…...

西电面向对象程序设计核心考点汇总(期末真题)

文章目录前言一、往年真题与答案1.1 改错题1.2 读程题1.3 面向对象程序设计二、易错知识点2.1 构造函数2.2 静态成员变量和静态成员函数2.3 权限2.4 继承2.5 多态总结前言 主要针对西安电子科技大学《面向对象程序设计》的核心考点进行汇总&#xff0c;包含总共8章的核心简答。…...

判断一个用字符串表达的数字是否可以被整除

一.问题引出 当一个数字很大的时候,我们常用字符串进行表达,(超过了int和long等数据类型可以存储的最大范围),但是这个时候我们该如何判断他是否可以被另一个数整除呢? 这个时候我们不妨这样来考虑问题,每次将前边求模之后的数保存下来,然后乘以10和这一位的数字进行相加的操…...

这是一款值得开发人员认真研究的软件,数据库优化,应用服务器安全优化...

1.查询数据库死锁相关信息2.查看数据库的链接情况3.当前实例上的所有用户4.创建数据库独立密码5.查看数据库使用的端口号6.当前数据库设置的最大连接数7.当前数据库最大的理论可连接数8.当前数据库实例的连接数9.当前数据库连接数10.当前数据库连接超时设置11.当前sqlserver 超…...

栈与队列小结

一、理论基础1.队列是先进先出&#xff0c;栈是先进后出2.栈和队列是STL&#xff08;C标准库&#xff09;里面的两个数据结构。栈提供push和pop等等接口&#xff0c;所有元素必须符合先进后出规则&#xff0c;所以栈不提供走访功能&#xff0c;也不提供迭代器。3.栈是以底层容器…...

SpringBoot整合(五)HikariCP、Druid数据库连接池—多数据源配置

在项目中&#xff0c;数据库连接池基本是必不可少的组件。在目前数据库连接池的选型中&#xff0c;主要是 Druid &#xff0c;为监控而生的数据库连接池。HikariCP &#xff0c;号称性能最好的数据库连接池。 在Spring Boot 2.X 版本&#xff0c;默认采用 HikariCP 连接池。而…...

ShardingSphere水平、垂直分库、分表和公共表

目录一、ShardingSphere简介二、ShardingSphere-分库分表1、垂直拆分&#xff08;1&#xff09;垂直分库&#xff08;2&#xff09;垂直分表2、水平拆分&#xff08;1&#xff09;水平分库&#xff08;2&#xff09;水平分表三、水平分库操作1、创建数据库和表2、配置分片的规则…...

《分布式技术原理与算法解析》学习笔记Day24

分布式缓存 在计算机领域&#xff0c;缓存是一个非常重要的、用来提升性能的技术。 什么是分布式缓存&#xff1f; 缓存技术是指用一个更快的存储设备存储一些经常用到的数据&#xff0c;供用户快速访问。 分布式缓存是指在分布式环境或者系统下&#xff0c;把一些热门数据…...

强化学习RL 02: Value-based Reinforcement Learning

DQN和TD更新算法。 目录 Review 1. Deep Q-Network(DQN) 1.1 Approximate the Q*(s,a) Function 1.2 Apply DQN to Play Game 1.3 Temporal Difference(TD) Learning 1.4 TD Learning for DQN 1.4.1 TD使用条件 condition 1.4.2 Train DQN using TD learning 1.5 summ…...

08_MySQL聚合函数

1. 聚合函数介绍什么是聚合函数聚合函数作用于一组数据&#xff0c;并对一组数据返回一个值。聚合函数类型AVG()SUM()MAX()MIN()COUNT()注意&#xff1a;聚合函数不能嵌套调用。比如不能出现类似“AVG(SUM(字段名称))”形式的调用。1.1 AVG和SUM函数可以对数值型数据使用AVG 和…...

「TCG 规范解读」词汇表

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…...

第三阶段-03MyBatis 中使用XML映射文件详解

MyBatis 中使用XML映射文件 什么是XML映射 使用注解的映射SQL的问题&#xff1a; 长SQL需要折行&#xff0c;不方便维护动态SQL查询拼接复杂源代码中的SQL&#xff0c;不方便与DBA协作 MyBatis建议使用XML文件映射SQL才能最大化发挥MySQL的功能 统一管理SQL&#xff0c; 方…...

从0开始学python -41

Python3 命名空间和作用域 命名空间 先看看官方文档的一段话&#xff1a; A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。 命名空间(Namespace)是从名称到对象的映射&#xff0c;大部分的命名空间都是…...

如何将Google浏览器安装到D盘(内含教学视频)

如何将Google浏览器安装到D盘&#xff08;内含教学视频&#xff09; 教学视频下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87503968 目录如何将Google浏览器安装到D盘&#xff08;内含教学视频&#xff09;教学视频下载链接地址&#xff1a;…...

三战阿里测试岗,成功上岸,面试才是测试员涨薪真正的拦路虎...

第一次面试阿里记得是挂在技术面上&#xff0c;当时也是技术不扎实&#xff0c;准备的不充分&#xff0c;面试官出的面试题确实把我问的一头雾水&#xff0c;还没结束我就已经知道我挂了这次面试。 第二次面试&#xff0c;我准备的特别充分&#xff0c;提前刷了半个月的面试题…...

Java代码弱点与修复之——ORM persistence error(对象关系映射持久错误)

弱点描述 ORM persistence error, ORM 持久化错误 。表示 ORM 工具在尝试将对象保存到数据库中时出现了问题。可能的原因包括: 数据库连接错误:ORM 工具无法连接到数据库,或者连接到数据库的权限不足。数据库表结构错误:ORM 工具无法正确映射对象和数据库表之间的关系,可…...

原始GAN-pytorch-生成MNIST数据集(原理)

文章目录1. GAN 《Generative Adversarial Nets》1.1 相关概念1.2 公式理解1.3 图片理解1.4 熵、交叉熵、KL散度、JS散度1.5 其他相关&#xff08;正在补充&#xff01;&#xff09;1. GAN 《Generative Adversarial Nets》 Ian J. Goodfellow, Jean Pouget-Abadie, Yoshua Be…...

Vue下载安装步骤的详细教程(亲测有效) 1

目录 一、【准备工作】nodejs下载安装(npm环境) 1 下载安装nodejs 2 查看环境变量是否添加成功 3、验证是否安装成功 4、修改模块下载位置 &#xff08;1&#xff09;查看npm默认存放位置 &#xff08;2&#xff09;在 nodejs 安装目录下&#xff0c;创建 “node_global…...

[Android Studio] Android Studio生成数字证书,为应用签名

&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Android Debug&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Topic 发布安卓学习过程中遇到问题解决过程&#xff0c;希望我的解决方案可以对小伙伴们有帮助。 &#x1f4cb;笔记目…...

应用IC 卡继续教育网络管理系统前后影响因素比较

3.1 实现了继续护理教育网络化管理近年来&#xff0c;随着一些医院继续护理教育管理信息系统的建立&#xff0c;有效改进了学分档案管理模式和教学模式&#xff0c;但这些继续护理教育管理信息系统一般为局域网&#xff0c;仅能达到满足自身管理的基本需求&#xff0c;而系统如…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践

01技术背景与业务挑战 某短视频点播企业深耕国内用户市场&#xff0c;但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大&#xff0c;传统架构已较难满足当前企业发展的需求&#xff0c;企业面临着三重挑战&#xff1a; ① 业务&#xff1a;国内用户访问海外服…...

【51单片机】4. 模块化编程与LCD1602Debug

1. 什么是模块化编程 传统编程会将所有函数放在main.c中&#xff0c;如果使用的模块多&#xff0c;一个文件内会有很多代码&#xff0c;不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里&#xff0c;在.h文件里提供外部可调用函数声明&#xff0c;其他.c文…...

GeoServer发布PostgreSQL图层后WFS查询无主键字段

在使用 GeoServer&#xff08;版本 2.22.2&#xff09; 发布 PostgreSQL&#xff08;PostGIS&#xff09;中的表为地图服务时&#xff0c;常常会遇到一个小问题&#xff1a; WFS 查询中&#xff0c;主键字段&#xff08;如 id&#xff09;莫名其妙地消失了&#xff01; 即使你在…...