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
、@PreFilter
securedEnabled
:开启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…...
板内盘中孔设计狂飙,细密间距线路中招
一博高速先生成员:王辉东大风起兮云飞扬,投板兮人心舒畅。赵理工打了哈欠,伸了个懒腰,看了看窗外,对林如烟说道:“春天虽美,但是容易让人沉醉。如烟,快女神节了,要不今晚…...
面试热点题:回溯算法 递增子序列与全排列 II
前言: 如果你一点也不了解什么叫做回溯算法,那么推荐你看看这一篇回溯入门,让你快速了解回溯算法的基本原理及框架 递增子序列 给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两…...
怎么找回回收站删除的文件
我们都知道,电脑文件都是放在桌面上的,单独存放或者一起存放在文件夹里。但总会有已用完或者是没用的文件,这让我们不得不对其进行清理。而清空回收站也是不可避免的。如果出现了清空文件中还有我们需要的文件,怎么找回回收站删除…...
dp-打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非…...
C++预处理连接
目录定义常量字符串前缀定义枚举类型Boost C库中常常使用预处理连接来定义宏和模板类Google开源的C单元测试框架gtest,使用预处理连接技术创建测试用例和测试方法C预处理连接(Preprocessor Concatenation)是一种宏定义技巧,用于将…...
3、DRF实战总结:基于类的视图APIView, GenericAPIView和GenericViewSet视图集(附源码)
前面介绍了什么是符合RESTful规范的API接口,以及使用了基于函数的视图(FBV)编写了对文章进行增删查改的API。在本篇文章将使用基于类的视图(Class-based View, CBV)重写之前的接口。 参考: 1、Django开发总结:Django MVT与MVC设计模式&…...
AutoSAR PduR -AutoSAR PDU常用的使用方式【发送,接收,网关】
总目录链接==>> AutoSAR入门和实战系列总目录 @学前问答: AutoSAR PDU在哪里全局定义的? AutoSAR PDU涉及到哪些模块? AutoSAR PDU网关怎么使用? 文章目录 1 AutoSAR PDU发送2 AutoSAR PDU接收3 AutoSAR PDU网关转发4 答疑解析AutoSAR PDU 怎么样通过PduR 实现与其…...
瑟瑟发抖吧~OpenAI刚刚推出王炸——引入ChatGPT插件,开启AI新生态
5分钟学会使用ChatGPT 插件(ChatGPT plugins)——ChatGPT生态建设的开端ChatGPT插件是什么OpenAI最新官方blog资料表示,已经在ChatGPT中实现了对插件的初步支持。插件是专门为以安全为核心原则的语言模型设计的工具,可帮助ChatGPT…...
脉诊(切脉、诊脉、按脉、持脉)之法——入门篇
认识脉诊何谓脉诊?脉诊的渊源脉诊重要吗?脉诊确有其事,还是故弄玄虚?中医科学吗?如何脉诊?寸口脉诊法何谓脉诊? 所谓脉诊,就是通过把脉来诊断身体健康状况的一种必要手段。 …...
【十二天学java】day09常用api介绍
1.API 1.1API概述 什么是API API (Application Programming Interface) :应用程序编程接口 java中的API 指的就是 JDK 中提供的各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这…...
东莞企业网站建设/网盘app下载
本文实例讲述了JS实现的简单四则运算计算器功能。分享给大家供大家参考,具体如下:先来看看运行效果:具体代码如下:www.jb51.net 计算器 Calculatorvar numresult;var str;function onclicknum(nums) {str document.getElementByI…...
引航博景网站做的很好吗/企业营销推广怎么做
当把一台服务器(包括Web服务器、FTP服务器或者流媒体服务器等等)放入网络中之后,随着客户端数量的不断增加,人们往往需要功能更强大、处理速度更快的服务器。为了解决这个问题,如果将原有的服务器替换成功能更强大、处理速度更快的服务器显然…...
怎么样才算大型网站开发/上海推广seo
年轻的Python横空出世闯入编程语言这个大家庭里面,新生的面孔,面对老大哥们,它该如何走下去呢?前景及应用在我看来,一切充满了未知数,但也代表着无限可能!先来看看被人熟知的这些前景及应用吧&a…...
wordpress 相关文章 图片/seo广告投放
今天,我们来谈谈一个新仓库应该如何规划!仓库先进先出规划1. 进出从不同的门进行,入库从进仓门进,出库从出仓门出。2. 联单制,每箱设两联单,一联贴箱上,一联放文件夹内,依据入仓时间…...
淘宝客网站怎么做的/百度关键词数据
安装photoshop cs6(虽然目前已经退出到cc 2015,不过因激活成熟度等,我还是偏向于使用cs6,够用!),默认安装adobe application manager。 不过如果不小心单独打开adobe application manager&#…...
北京网站设计公司兴田德润怎么样/百度推广点击一次多少钱
1.安装插件: 这里可以搜索到插件并安装。 2.修改快捷键或查找快捷键: 这里可以进行快捷键的查找和修改 3.进入引用文件: 点击f12,或者右击快捷键可以看到进入引用文件的快捷方法。 4.查看目录: 转载于:https://www.cnb…...