管理系统权限总结

news/2024/5/5 22:06:35/文章来源:https://blog.csdn.net/weixin_43549578/article/details/127208035

概念 

功能权限数据权限

  • 功能权限:用户是否能打开某一个网页,是否能点击编辑按钮等。
  • 数据权限:用户可以使用的数据范围。

用户

        应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

角色

        为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

        为了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、权限信息。比如:用户群,一个群可以有多个用户,一个用户也可以加入多个群。每个群具有自己的权限信息。例如查看群共享。QQ群也可以具有自己的角色信息,例如普通群、高级群等。

权限

        页面权限:用户可以看到那些页面;

        操作权限:用户可以在页面内进行那些操作,增删改查等;

        数据权限:用户可以看到那些数据或内容如:本部门的数据库,全部数据,本身数据;

权限模型

1.ACL:访问控制列表

说明 

 用户表 权限表 用户权限关系表。

2.RBAC: 基于角色的权限控制

        RBAC0RBAC1RBAC2RBAC3 四个阶段,一般公司使用 RBAC0 的模型就可以。另外,RBAC0 相当于底层逻辑,后三者都是在 RBAC0 模型上的拔高。 

3.ABAC:基于属性的权限控制

        通过动态计算一个或一组属性来是否满足某种条件来进行授权判断(可以编写简单的逻辑)。属性通常来说分为四类:用户属性(如用户年龄),环境属性(如当前时间),操作属性(如读取)和对象属性(如一篇文章,又称资源属性),理论上能够实现非常灵活的权限控制,几乎能满足所有类型的需求。

        典型的 ABAC 场景描述如下图,当 subject 需要去读取某一条记录时,我们的访问控制机制在请求发起后遍开始运作,该机制需要计算,来自 policy 中记录的规则,subject 的 attribute,object 的 attribute 以及 environment conditions,而最后会产生一个是否允许读取的结果:

 细粒度权限控制,实现起来非常负责,维护成本高。

4.PBAC:基于策略的权限控制

         PBAC支持运行时授权,因此它是动态的,并具有允许实时进行更改的能力,不能直观看出用户和资源的访问关系,需要实时计算,较多规则会有性能问题。

        PBAC是一种将角色和属性与逻辑结合以创建灵活的动态控制策略的方法。与ABAC一样,它使用许多属性来确定访问权限,因此它还提供了“细粒度”访问控制。PBAC旨在支持各种方式的访问设备,通常被认为是最灵活的授权解决方案。

文献 

数据权限

用户表 角色表 权限表 机构表 。

思想:

        角色表新增数据范围字段: 0:全部数据  1:本部门及子部门数据  2:本部门数据  3:本人数据  4:自定义数据

        用户登录缓存数据用户信息和机构信息。查询时候根据用户信息和机构信息获取用户数据权限范围。

如下仅供思维参考。

第一种:基于mybatis-plus插件

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version>
</dependency>
    @Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();// 数据权限mybatisPlusInterceptor.addInnerInterceptor(new DataScopeInnerInterceptor());return mybatisPlusInterceptor;}
/*** 数据范围*/
@Data
@AllArgsConstructor
public class DataScope {private String sqlFilter;
}

数据拦截器

package com.lean.auth.interceptor;import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;import java.util.Map;/*** 数据权限*/
public class DataScopeInnerInterceptor implements InnerInterceptor {@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {DataScope scope = getDataScope(parameter);// 不进行数据过滤if (scope == null || StrUtil.isBlank(scope.getSqlFilter())) {return;}// 拼接新SQLString buildSql = getSelect(boundSql.getSql(), scope);// 重写SQLPluginUtils.mpBoundSql(boundSql).sql(buildSql);}private DataScope getDataScope(Object parameter) {if (parameter == null) {return null;}// 判断参数里是否有DataScope对象if (parameter instanceof Map) {Map<?, ?> parameterMap = (Map<?, ?>) parameter;for (Map.Entry entry : parameterMap.entrySet()) {if (entry.getValue() != null && entry.getValue() instanceof DataScope) {return (DataScope) entry.getValue();}}} else if (parameter instanceof DataScope) {return (DataScope) parameter;}return null;}private String getSelect(String buildSql, DataScope scope) {try {//解析sqlSelect select = (Select) CCJSqlParserUtil.parse(buildSql);PlainSelect plainSelect = (PlainSelect) select.getSelectBody();Expression expression = plainSelect.getWhere();if (expression == null) {plainSelect.setWhere(new StringValue(scope.getSqlFilter()));} else {AndExpression andExpression = new AndExpression(expression, new StringValue(scope.getSqlFilter()));plainSelect.setWhere(andExpression);}return select.toString().replaceAll("'", "");} catch (JSQLParserException e) {return buildSql;}}
}

业务组装数据权限sql:

   /*** 原生SQL 数据权限* @param tableAlias 表别名,多表关联时,需要填写表别名* @param orgIdAlias 机构ID别名,null:表示org_id* @return 返回数据权限*/protected DataScope getDataScope(String tableAlias, String orgIdAlias)  {UserDetail user = SecurityUser.getUser();// 如果是超级管理员,则不进行数据过滤if(user.getSuperAdmin().equals("1")) {return null;}// 如果为null,则设置成空字符串if(tableAlias == null){tableAlias = "";}// 获取表的别名if(StringUtils.isNotBlank(tableAlias)){tableAlias +=  ".";}StringBuilder sqlFilter = new StringBuilder();sqlFilter.append(" (");// 数据权限范围List<Long> dataScopeList = getDataScopeList(user.getId(),user.getOrgId());// 全部数据权限if (dataScopeList == null){return null;}// 数据过滤if(dataScopeList.size() > 0){if(StringUtils.isBlank(orgIdAlias)){orgIdAlias = "org_id";}sqlFilter.append(tableAlias).append(orgIdAlias);sqlFilter.append(" in(").append(StrUtil.join(",", dataScopeList)).append(")");sqlFilter.append(" or ");}// 查询本人数据sqlFilter.append(tableAlias).append("create_id").append("=").append(user.getId());sqlFilter.append(")");return new DataScope(sqlFilter.toString());}/*** 获取数据权限部门id* @param userId 用户id* @param orgId 部门id* @return*/private List<Long> getDataScopeList(Long userId,Long orgId) {//获取用户最大的数据范围//select max(t1.data_scope) from sys_role t1, sys_user_role t2 where t1.id = t2.role_id and t2.user_id = #{userId} and t1.is_del = 0Integer dataScope = sysRoleDao.getDataScopeByUserId(userId);if (dataScope == null) {return new ArrayList<>();}//数据范围  0:全部数据  1:本部门及子部门数据  2:本部门数据  3:本人数据  4:自定义数据if (dataScope.equals("0")) {// 全部数据权限,则返回nullreturn null;} else if (dataScope.equals("1")) {// 本部门及子部门数据List<Long> dataScopeList = sysOrgService.getSubOrgIdList(orgId);return dataScopeList;} else if (dataScope.equals("2")) {// 本部门数据List<Long> dataScopeList = new ArrayList<>();dataScopeList.add(orgId);return dataScopeList;} else if (dataScope.equals("4")) {// 自定义数据权限范围//select t2.org_id from sys_user_role t1, sys_role_data_scope t2 where t1.user_id = #{userId} and t1.role_id = t2.role_id and t1.is_del = 0return sysRoleDataScopeDao.getDataScopeList(orgId);}return new ArrayList<>();}/*** MyBatis-Plus 数据权限请求参数填充sql*/protected void dataScopeWrapper(LambdaQueryWrapper<T> queryWrapper)  {DataScope dataScope = getDataScope(null, null);if (dataScope != null){queryWrapper.apply(dataScope.getSqlFilter());}}

案例:

@GetMapping("page")
@Operation(summary = "分页")
public Result<PageResult> page(@Valid SysRoleQuery query){PageResult page = sysRoleService.page(query);return Result.ok(page);
}@Override
public PageResult page(SysRoleQuery query) {IPage page = baseMapper.selectPage(getPage(query), getWrapper(query));return new PageResult<>(page);
}private Wrapper getWrapper(SysRoleQuery query){LambdaQueryWrapper<SysRoleEntity> wrapper = new LambdaQueryWrapper<>();wrapper.like(StrUtil.isNotBlank(query.getName()), SysRoleEntity::getName, query.getName());// 数据权限 请求参数填充权限sql dataScope,供拦截器拦截处理dataScopeWrapper(wrapper);return wrapper;}

第二种:基于mybatis插件

mybatis插件原理

        <dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.4</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.22</version><scope>compile</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.1</version></dependency>

 

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用来判断是否需要进行数据权限*/
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataAuthSelect {
}
import lombok.AllArgsConstructor;
import lombok.Data;/*** 数据范围***/
@Data
@AllArgsConstructor
public class DataScope {private String sqlFilter;
}
import cn.hutool.extra.spring.SpringUtil;
import com.lean.mybatis.service.MenuService;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;@Intercepts({ @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class,Integer.class }) })
public class DataScopeInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler handler = (StatementHandler)invocation.getTarget();//由于mappedStatement中有我们需要的方法id,但却是protected的,所以要通过反射获取MetaObject statementHandler = SystemMetaObject.forObject(handler);MappedStatement mappedStatement = (MappedStatement) statementHandler.getValue("delegate.mappedStatement");//没自定义注解直接按通过算DataAuthSelect dataAuth = getDataAuth(mappedStatement);if (dataAuth == null) {return invocation.proceed();}//判断是否登录 TODO //获取sql SELECT * FROM t_role WHERE id = ? and is_valid = 0BoundSql boundSql = handler.getBoundSql();String sql = boundSql.getSql();//获得方法类型 (如select,update)SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();if ("SELECT".equalsIgnoreCase(sqlCommandType.toString())) {//增强sql代码块  这里可通过判断用户权限为不通类型的用户拼接不同的sql//根据用户id和机构id 获取数据权限范围MenuService menuService = SpringUtil.getBean(MenuService.class);DataScope dataScope = menuService.getDataScope(null, null);// 拼接新SQL// SELECT * FROM t_role WHERE id = ? AND is_valid = 0 AND  (create_id=10)// SELECT * FROM t_role WHERE id = ? AND is_valid = 0 AND  (org_id in(2,21) or create_id=10)sql=getSelect(sql,dataScope);//将增强后的sql放回statementHandler.setValue("delegate.boundSql.sql",sql);}return invocation.proceed();}@Overridepublic Object plugin(Object o) {//生成代理对象return Plugin.wrap(o, this);}@Overridepublic void setProperties(Properties properties) {}/*** 通过反射获取mapper方法是否加了自定义注解*/private DataAuthSelect getDataAuth(MappedStatement mappedStatement) throws ClassNotFoundException {DataAuthSelect dataAuth = null;String id = mappedStatement.getId();String className = id.substring(0, id.lastIndexOf("."));String methodName = id.substring(id.lastIndexOf(".") + 1);final Class<?> cls = Class.forName(className);final Method[] methods = cls.getMethods();for (Method method : methods) {if (method.getName().equals(methodName) && method.isAnnotationPresent(DataAuthSelect.class)) {dataAuth = method.getAnnotation(DataAuthSelect.class);break;}}return dataAuth;}private String getSelect(String buildSql, DataScope scope){try {Select select = (Select) CCJSqlParserUtil.parse(buildSql);PlainSelect plainSelect = (PlainSelect) select.getSelectBody();Expression expression = plainSelect.getWhere();if(expression == null){plainSelect.setWhere(new StringValue(scope.getSqlFilter()));}else{AndExpression andExpression =  new AndExpression(expression, new StringValue(scope.getSqlFilter()));plainSelect.setWhere(andExpression);}return select.toString().replaceAll("'", "");}catch (JSQLParserException e){return buildSql;}}
}

import java.util.Date;
@Data
public class Role {private Long id;private String roleName;private String roleRemark;private Date createDate;private Date updateDate;private Integer isValid;
}
import lombok.AllArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructor
public class UserDetail {private Long id;private Long orgId;private String superAdmin;
}
import cn.hutool.core.util.StrUtil;
import com.lean.mybatis.datascope.DataScope;
import com.lean.mybatis.entity.UserDetail;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.List;@Service
public class DataScopService {/*** 获取数据权限部门id** @param userId 用户id* @param orgId  部门id* @return*/private List<Long> getDataScopeList(Long userId, Long orgId) {//根据用户ID,获取用户最大的数据范围//select max(t1.data_scope) from sys_role t1, sys_user_role t2 where t1.id = t2.role_id and t2.user_id = #{userId} and t1.is_del = 0Integer dataScope = 1;if (dataScope == null) {return new ArrayList<>();}//数据范围  0:全部数据  1:本部门及子部门数据  2:本部门数据  3:本人数据  4:自定义数据if (dataScope == 0) {// 全部数据权限,则返回nullreturn null;} else if (dataScope == 1) {// 本部门及子部门数据List<Long> dataScopeList = new ArrayList<>();dataScopeList.add(2L);dataScopeList.add(21L);return dataScopeList;} else if (dataScope == 2) {// 本部门数据List<Long> dataScopeList = new ArrayList<>();dataScopeList.add(orgId);return dataScopeList;} else if (dataScope == 4) {// 自定义数据权限范围//select t2.org_id from sys_user_role t1, sys_role_data_scope t2 where t1.user_id = #{userId} and t1.role_id = t2.role_id and t1.is_del = 0List<Long> dataScopeList = new ArrayList<>();dataScopeList.add(23L);return dataScopeList;}return new ArrayList<>();}/*** 原生SQL 数据权限** @param tableAlias 表别名,多表关联时,需要填写表别名* @param orgIdAlias 机构ID别名,null:表示org_id* @return 返回数据权限*/public DataScope getDataScope(String tableAlias, String orgIdAlias) {//模拟当前用户 TODOUserDetail user = new UserDetail(10L, 2L, "2");// 如果是超级管理员,则不进行数据过滤if (user.getSuperAdmin().equals("1")) {return null;}// 如果为null,则设置成空字符串if (tableAlias == null) {tableAlias = "";}// 获取表的别名if (!StringUtils.isEmpty(tableAlias)) {tableAlias += ".";}StringBuilder sqlFilter = new StringBuilder();sqlFilter.append(" (");// 数据权限范围List<Long> dataScopeList = getDataScopeList(user.getId(), user.getOrgId());// 全部数据权限if (dataScopeList == null) {return null;}// 数据过滤if (dataScopeList.size() > 0) {if (StringUtils.isEmpty(orgIdAlias)) {orgIdAlias = "org_id";}sqlFilter.append(tableAlias).append(orgIdAlias);sqlFilter.append(" in(").append(StrUtil.join(",", dataScopeList)).append(")");sqlFilter.append(" or ");}// 查询本人数据sqlFilter.append(tableAlias).append("create_id").append("=").append(user.getId());sqlFilter.append(")");return new DataScope(sqlFilter.toString());}}

xml配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><plugins><plugin interceptor="com.lean.mybatis.datascope.DataScopeInterceptor"></plugin></plugins>
</configuration>

import com.lean.mybatis.datascope.DataAuthSelect;
import com.lean.mybatis.entity.Role;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.io.Serializable;@Mapper
public interface RoleMapper {@Select(value = "SELECT * FROM t_role WHERE id = #{id} and is_valid = 0")@DataAuthSelectRole selectByIdAuth(Serializable id);
}

测试

import com.lean.mybatis.dao.RoleMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class SpringLeanMybatisApplicationTests {@Autowiredprivate RoleMapper roleMapper;@Testvoid contextLoads() {}@Testvoid testAuth() {roleMapper.selectByIdAuth(1);}
}

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

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

相关文章

使用VSCode连接远程服务器

1 效果展示 最近在使用云服务器开项目&#xff0c;发现VSCode的remote插件能远程连接服务器进行开发&#xff0c;这样就非常方便了。效果如下&#xff1a; 可以看到&#xff0c;这样操作&#xff0c;使得云端开发和本地开发几乎没什么不同&#xff0c;如果是云服务器就更方便了…

Vue脚手架报错:‘v-model‘ directives require no argument 解决方案

1、报错&#xff1a; v-model directives require no argument 截图 2、原因&#xff1a; ESLint对vetur进行了eslint检查 3、解决方法 ① 修改模板中使用v-show 将 v-model:show"show" 改为 v-model"show" ② vetur插件的作者给出了解决办法 我们可…

20201306吴龙灿第三章学习笔记

目录Ⅰ知识点归纳1.进程的概念什么是进程?进程的特征动态性并发性独立性异步性结构性程序和进程主要区别2.多任务处理系统(1)背景(2)多任务处理系统代码介绍3.进程同步(1)同步(2)进程唤醒与睡眠无效唤醒A 进程:B 进程:避免无效唤醒A 进程:Linux 内核的例子Ⅱ实践内容与…

docker jenkins升级以及失败处理

一、概述 jenkins是由docker安装的,目前的jenkins版本为2.356。然后jenkins右上角提示版本升级 点击了升级,升级完成后,需要重启一下。 然后就芭比Q了,访问jenkins出现504错误。查看docker日志,提示需要jdk升级到1.8。默认的jenkins的jdk版本为1.7,然后docker就开始一直…

督办管理系统——让企业工作落实到位

开展督查督办工作是企业在经营管理过程中的重要环节和管理手段&#xff0c;是企业办公室系统政务服务的一项重要工作。其具有间接性、权威性、实效性等特点。要加强企业督查督办工作&#xff0c;必须思想认识到位&#xff0c;充分把握督查督办工作原则;制度建设到位&#xff0c…

linux NTP同步时间后比实际时间慢8小时

1. issue ntp同步时间后比实际时间慢8小时 2. analysis 查询系统当前的时区设置 date -R&#xff0c;看到系统是 0000 时区&#xff0c;而中国统一采用北京所在的东8时区&#xff0c;由此造成了8小时的时间偏差。 3. solution 将PC ubuntu /usr/share/zoneinfo/Asia/Shanghai…

Django定义路由_子路由_函数视图

Django定义路由什么是路由&#xff0c;怎么去定义路由&#xff1f;添加路由Path 函数路由定义的痛点处理路由中的动态参数什么是路由&#xff0c;怎么去定义路由&#xff1f; 通常在我们创建项目包中&#xff0c;有个url.py的文件&#xff0c;我们需要去定义路由信息&#xff…

二叉树专项训练LeetCode

144. 二叉树的前序遍历 二叉树入门 递归 与 迭代 class Solution {List<Integer> ans new ArrayList<>();void dfs(TreeNode root){if(root null) {return;}ans.add(root.val);dfs(root.left);dfs(root.right);}public List<Integer> preorderTraversal(T…

【Golang开发面经】蔚来(两轮技术面)

文章目录一面1. channel 缓冲与非缓冲2. mysql引擎3. 索引如何建立&#xff1f;4. linux 如何看进程5. redis 字符串的底层6. 线程池理解7. 线程池的拒绝策略8.悲观锁&#xff0c;乐观锁9. HTTP 各个版本的区别10. HTTP2.0之前怎么实现服务器推送机制&#xff1f;11. websocket…

[操作系统] 启动

启动 一、通电 由于内存是随机存储器&#xff08;Random access memory&#xff0c;RAM&#xff09;&#xff0c;属于易失性存储器&#xff0c;未通电时&#xff0c;RAM中不会有任何内容&#xff0c;因此刚一通电&#xff0c;RAM不可能有任何实际信息。计算机硬件厂商在只读存…

信创浪潮下,看看大公司是如何建立数据安全保护体系的?

信创&#xff0c;即信息技术应用创新产业&#xff0c;它是数据安全、网络安全的基础&#xff0c;也是新基建的重要组成部分。信创涉及到的行业包括IT基础设施&#xff1a;CPU芯片、服务器、存储、交换机、路由器、各种云和相关服务内容&#xff0c;基础软件&#xff1a;数据库、…

1.ROS机器视觉:单目摄像头的调用与标定

(1条消息) ROS改错&#xff1a;vm虚拟机中调用摄像头失败_机械专业的计算机小白的博客-CSDN博客https://blog.csdn.net/wzfafabga/article/details/127204106?spm1001.2014.3001.5502 首先保证摄像头是可调用的。 1.安装usb_cam驱动 sudo apt-get install ros-melodic-usb-…

数据导入导出功能的测试点

【数据导入功能】 一、操作按钮校验 1、导入按钮生效 2、取消导入按钮生效 二、导入模板校验 1、文件数量 1&#xff09;不传模板&#xff1a;点确认时提示错误 2&#xff09;传模板&#xff1a;只支持单文件 or 还支持多文件同时导入 2、文件格式 只支持xlsx文件 or 还支…

HTML学生个人网站作业设计 学生大学生活网页设计作品 学生个人网页模板 简单个人主页成品 div+css个人网页制作

&#x1f329;️ 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f482; 作者主页: 【进入主页—&#x1f680;获取更多源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;HTML5网页期末作业 (1000套…

Java项目:ssh网上便利店系统

作者主页&#xff1a;夜未央5788 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 该项目分为前后台。非maven项目&#xff1b; 前台主要功能包括&#xff1a; 会员登录、注册、商品展示、加入购物车、会员中心、我的订单、我的地址…

【跟学C++】C++队列——queue类(Study13)

文章目录1、队列2、队列--queue类的使用2.1 实例化queue2.2 queue的成员函数3、优先级队列--priority_queue类的使用3.1 实例化priority_queue3.1 priority_queued的成员函数4、总结 【说明】 大家好&#xff0c;本专栏主要是跟学C内容&#xff0c;自己学习了这位博主【 AI菌】…

多测师肖sir_高级讲师_第2个月第21讲解jmeter安装

一、安装流程&#xff1a; 1、安装jdk &#xff08;linux&#xff0c;windows上&#xff09;&#xff0c;jdk编译java语言&#xff0c; 2、jdk环境配置&#xff0c;dos中java -version 查看jdk版本 3、下载jmeter包&#xff0c;解压&#xff0c;bin 目录 &#xff0c;jmeter.ba…

从零开始配置vim(25)——关于 c++ python 的配置

从9月份到国庆这段时间,因为得了女儿,于是回老家帮忙料理家事以及陪伴老婆和女儿。一时之间无暇顾及该系列教程的更新。等我回来的时候发现很多小伙伴私信我催更。在这里向支持本人这一拙劣教程的各位小伙伴表示真诚的感谢。言归正传,让我们开始吧 之前我们根据lua语言配置了…

(附源码)计算机毕业设计ssm电子购物商城

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【DL】第 11 章:自动驾驶汽车的深度学习

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…