Shiro整合SpringBoot,实战下的应用场景

news2024/12/29 9:59:57

文章目录

  • 前言
  • 一、springBoot+shiro环境准备
    • 1.数据库
    • 2.ssmp环境搭建
    • 3.实体类
    • 4.三层搭建
    • 5.初始化测试数据
  • 二、Shiro过滤器
    • 1.Shiro认证过滤器
    • 2.Shiro授权过滤器
  • 三、springBoot+shiro身份认证
    • 1.创建Realm,重写认证方法doGetAuthenticationInfo
    • 2.创建shiro配置类
    • 3.Postman测试
  • 四、springBoot+shiro授权,鉴权
    • 1.重写授权方法doGetAuthenticationInfo
    • 2.访问程序资源(鉴权)
    • 3.全局异常处理器
    • 4.Postman测试


前言

整合springBoot+shiro流程:

  1. 环境准备
  2. 身份认证
    2.1 密码加密
    2.2 非法请求控制
  3. 授权,鉴权

一、springBoot+shiro环境准备

项目目录:
在这里插入图片描述

1.数据库

认证框架五表设计:
准备user用户表、user_role用户角色关系表、role角色表、 role_permission角色权限关系表、permission权限表
user用户表:
在这里插入图片描述
user_role用户角色关系表:
在这里插入图片描述
role角色表:
在这里插入图片描述
role_permission角色权限关系表:
在这里插入图片描述
permission权限表:
在这里插入图片描述

2.ssmp环境搭建

2.1准备依赖

		<!--web启动器-->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		<!--test启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

		<!--Mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
            <scope>runtime</scope>
        </dependency>
        <!--Mybatisplus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <!--lombok作用于实体类-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
		<!--shiro相关坐标-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>

2.2 配置Yaml文件

#数据源配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    url: jdbc:mysql://localhost:3306/shiro?serverTimezone=GMT
    password: 12345678
#Mybatisplus配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.实体类

User用户类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_user")
public class User implements Serializable {
    @TableId(value = "id",type = IdType.NONE)
    private String id;
    @TableField(value = "username")
    private String username;
    @TableField(value = "password")
    private String password;
    @TableField(value = "salt")
    private String salt;
    @TableField(exist = false)
    private Set<Role> roles;
}

role角色类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_role")
public class Role implements Serializable {

    private  String id;
    private  String name;
    private  String code;
    private  String description;

    private Set<Permission> permissions;
}

Permission 权限类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_permission")
public class Permission implements Serializable {
    private  String id;
    private  String name;
    private  String code;
    private  String escription;
}

4.三层搭建

DAO层: 因为是五表构造,需要处理关联关系,所以在这里使用级联查询的方式实现:
UserMapper :

@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("select * from pe_user where username =#{username}")
    User findByName(String name);
    
    @Select("select * from pe_user where id =#{id}")
    @Results({
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "password",column = "password"),
            @Result(property = "salt",column = "salt"),
            @Result(property = "roles",column = "id",
                    many = @Many(select = "com.apesource.springboot_shiro_01.dao.RoleMapper.findRoleById"))
    })
    User findUserDetailById(int id);
}

RoleMapper :

@Mapper
public interface RoleMapper {
    @Results({
            @Result(column = "id", property = "id"),
            @Result(column = "name", property = "name"),
            @Result(column = "code", property = "code"),
            @Result(column = "description", property = "description"),
            @Result(column = "id",property = "permissions",
                    many = @Many(select = "com.apesource.springboot_shiro_01.dao.PermissionMapper.findPermissionById"))

    })
//    @Select("select  *  from 角色表 where 角色id in (select 角色ID from 关系表 where 用户id = ?)")
    @Select("select  *  from pe_role where id in (select role_id from pe_user_role where user_id = #{id})")
    public Set<Role> findRoleById(String id);
}

PermissionMapper :

@Mapper
public interface PermissionMapper {
    @Select("select  *  from pe_permission where id in (select permission_id from pe_role_permission where role_id = #{id})")
    @Results({
            @Result(column = "id", property = "id"),
            @Result(column = "name", property = "name"),
            @Result(column = "code",property = "code"),
            @Result(column = "description", property = "description")
    })
    public Set<Permission> findPermissionById(long id);
}

Service层: 因为项目只实现Shrio的用户认证和授权,所以只构造User的业务层就足够了。
UserService :

@Service
public class UserService {
    @Autowired(required = false)
    UserMapper mapper;
    public User findByName(String name){
        return mapper.findByName(name);
    }
    public User findUserDetailById(int id){
        return mapper.findUserDetailById(id);
    }
}

Controller层:

@RestController
public class ShiroController {
    @Autowired
    UserService service;

    /**
     * @RequiresPermissions()  -- 访问此方法必须具备的权限
     *  @RequiresRoles()        -- 访问此方法必须具备的角色
     *
     */

    @RequiresPermissions("user-home")
    @RequestMapping(value = "/user/home")
    public String home() {
        return "访问个人主页成功";
    }

    //添加
    @RequiresPermissions("user-add")
    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String add() {
        return "添加用户成功";
    }

    //查询
    @RequiresPermissions("user-find")
    @RequestMapping(value = "/user/find",method = RequestMethod.GET )
    public String find(){
        return "查询成功";
    }
    //更新
    @RequiresPermissions("user-update")
    @RequestMapping(value = "/user/{id}",method = RequestMethod.PUT)
    public String update(@PathVariable String id) {
        return "更新用户成功";
    }

    //删除
    @RequiresPermissions("user-delete")
    @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
    public String delete(@PathVariable String id) {
        return "删除用户成功";
    }

    //未登陆与未授权页面
    @RequestMapping(value="/autherror")
    public String autherror() {
        return "未登录";
    }

    //用户登录
    @RequestMapping(value="/login")
    public String login(User user) {
        try {
            //1.构造登录令牌
            UsernamePasswordToken upToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());
            //2.获取subject
            Subject subject = SecurityUtils.getSubject();
            //3.调用subject进行登录
            subject.login(upToken);
            return "登录成功";
        }catch (Exception e) {
            e.printStackTrace();
            return "用户名或密码错误";
        }
    }
}

5.初始化测试数据

因为在实战场景下,数据库的密码是不可以明文保存的,这样对于数据的安全性是巨大的隐患,而shiro也支持数据加密的功能。
加密工具类:

public class DigestUtil {
    //算法方式
    public static final String SHA1 = "SHA-1";
    public static final String SHA256 = "SHA-256";

    //加密次数
    public static final Integer Counts =369;

    /**
     * @Description show
     * @param input 需要散列字符串
     * @param salt 盐字符串
     * @return
     */
    public static String show(String input,String salt){
        return new SimpleHash(SHA1,input,salt,Counts).toString();
    }
    /**
     * @Description 随机获得salt字符串
     * @return
     */
    public static String generateSalt(){
        SecureRandomNumberGenerator randomNumberGenerator =new SecureRandomNumberGenerator();
        return randomNumberGenerator.nextBytes().toHex();
    }
    /**
     * @Description 生成密码字符密文和salt密文
     * @param
     * @return
     */
    public static Map<String,String> entryptPassword(String passwordPlain){
        Map<String,String> map = new HashMap<>();
        String salt =generateSalt();
        String password = show(passwordPlain,salt);
        map.put("salt",salt);
        map.put("明文password",passwordPlain);
        map.put("密文密码",password);
        return map;
    }

由于没有实现注册功能,所以对于数据库的密文密码通过自己操作工具类输入进去:

    public static void main(String[] args) {
        String name ="张三丰";
        String password = "123123";
        Map<String, String> map = entryptPassword(password);
        System.out.println(map.toString());
    }

二、Shiro过滤器

Shiro内置了很多默认的过滤器,比如身份验证、授权等相关的,shiro也是通过过滤器的原理来实现认证和授权的扩展功能的。

1.Shiro认证过滤器

在这里插入图片描述

2.Shiro授权过滤器

在这里插入图片描述

三、springBoot+shiro身份认证

1.创建Realm,重写认证方法doGetAuthenticationInfo

使用@PostConstruct注解修饰的init方法就会在Spring容器的启动时自动的执行

public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserService service;
     /**
     * @Description 自定义密码比较器
     * bean标签 init-method属性
     */
     @PostConstruct
    public void initCredentialsMatcher() {
        //指定密码算法
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestUtil.SHA1);
        //指定迭代次数
        hashedCredentialsMatcher.setHashIterations(DigestUtil.Counts);
        //生效密码比较器
        setCredentialsMatcher(hashedCredentialsMatcher);
    }
}
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		//1.获取登录的用户名密码(token)
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        String username = token.getUsername();
		//2.根据用户名查询数据库
        User user  = service.findByName(username);
        //3.判断用户是否存在或者密码是否一致
        if(user==null){
            throw new UnknownAccountException("账户不存在");
        }
        //4.如果一致返回安全数据
        //通过SimpleAuthenticationInfo校验数据
        //构造方法:安全数据,密码(匿名),混淆字符串(salt),realm域名
        return new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),"MyRealm");
    }

2.创建shiro配置类

shiro配置类需要配置八个步骤,比较繁琐,最重要的是shiroFilter过滤器,用于实现非法请求的处理

@Configuration
public class ShiroConfiguration {

    /**
     * 1.创建shiro自带cookie对象
     */
    @Bean
    public SimpleCookie sessionIdCookie(){
        SimpleCookie simpleCookie = new SimpleCookie();
        simpleCookie.setName("ShiroSession");
        return simpleCookie;
    }

    //2.创建realm
    @Bean
    public MyRealm getRealm() {
        return new MyRealm();
    }

    /**
     * 3.创建会话管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setGlobalSessionTimeout(3600000);
        return sessionManager;
    }

    //4.创建安全管理器
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    
    /**
     * 5.保证实现了Shiro内部lifecycle函数的bean执行
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 6.开启对shiro注解的支持
     *   AOP式方法级权限检查
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }

    //8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        //1.创建过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        //2.设置安全管理器
        filterFactory.setSecurityManager(defaultWebSecurityManager());
        //3.通用配置(跳转登录页面,为授权跳转的页面)
        filterFactory.setLoginUrl("/autherror");//跳转url地址
        //4.设置过滤器集合
        //key = 拦截的url地址
        //value = 过滤器类型
        Map<String,String> filterMap = new LinkedHashMap<>();
        //key:请求规则   value:过滤器名称
        filterMap.put("/login","anon");//当前请求地址可以匿名访问
        filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问
        //在过滤器工程内设置系统过滤器
        filterFactory.setFilterChainDefinitionMap(filterMap);
        return filterFactory;
    }
}

3.Postman测试

成功:
在这里插入图片描述
合法请求资源:
在这里插入图片描述

失败:
在这里插入图片描述
非法请求资源:
在这里插入图片描述


四、springBoot+shiro授权,鉴权

1.重写授权方法doGetAuthenticationInfo

授权方法:
操作的时候,判断用户是否具有响应的权限
一定先认证再授权
先认证 – 安全数据
再授权 – 根据安全数据获取用户具有的所有操作权限

//授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    	//1.获取已认证的用户数据
        String id1 = (String) principalCollection.getPrimaryPrincipal();

        int i = Integer.parseInt(id1);

        User user = service.findUserDetailById(i);
        //2.根据用户数据获取用户的权限信息(所有角色,所有权限)
        Set<String> roles = new HashSet<>();//所有角色
        Set<String> perms = new HashSet<>();//所有权限
        for (Role role : user.getRoles()) {
            roles.add(role.getName());
            for (Permission perm : role.getPermissions()) {
                perms.add(perm.getCode());
            }
        }
        //将角色和权限信息返回
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(perms);
        info.setRoles(roles);
        return info;
    }

2.访问程序资源(鉴权)

注解实现:直接在控制器接口上加注解即可;
注解实现权限:如果权限信息不匹配,抛出异常(则使用异常处理器解决);
常用注解:
@RequiresPermissions – 访问此方法必须具备的权限
@RequiresRoles – 访问此方法必须具备的角色
@RequiresAuthentication – 表明当前用户需是经过认证的用户
@ RequiresGuest --表明该用户需为”guest”用户
@ RequiresUser – 当前用户需为已认证用户或已记住用户

具体实现在controller层的每个业务接口,这里只举例一个:

	//个人主页
    @RequiresPermissions("user-home")
    @RequestMapping(value = "/user/home")
    public String home() {
        return "访问个人主页成功";
    }
 @RequiresPermissions("user-add")//参数:对应权限的code值
    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String add() {
        return "添加用户成功";
    }

3.全局异常处理器

专门用于处理权限不足的异常信息:

@ControllerAdvice
public class BaseExceptionHandler {
    @ExceptionHandler(value = AuthorizationException.class)
    @ResponseBody
    public String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e) {
        return "未授权-异常处理器实现";
    }
}

4.Postman测试

张三丰用户只有user-home权限,没有add权限;

1.直接访问权限方法会被拦截做登录:
在这里插入图片描述
2.访问有权限的资源
成功
在这里插入图片描述
3.访问没有权限的资源
失败,被全局异常处理器处理。
在这里插入图片描述


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发(读写锁,线程池)03 详细讲解

读写锁 独占锁&#xff1a;一次只能被一个线程占有 共享锁。多个线程可以同时占有 class Apple{public static void main(String[] args) {Mycache mycachenew Mycache();for (int i 0; i <5 ; i) {final int tempi;new Thread(()->{mycache.put(temp"",tem…

北京985学校,交叉学科考英一数三408

北京师范大学(B) 考研难度&#xff08;☆☆☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1096字&#xff0c;预计阅读&#xff1a;3分钟 2023考情概况 北…

ChatGPT⼊门到精通(5):ChatGPT 和Claude区别

⼀、Claude介绍 Claude是Anthropic开发的⼀款⼈⼯智能助⼿。 官⽅⽹站&#xff1a; ⼆、Claude能做什么 它可以通过⾃然语⾔与您进⾏交互,理解您的问题并作出回复。Claude的主要功能包括: 1、问答功能 Claude可以解答⼴泛的常识问题与知识问题。⽆论是历史上的某个事件,理科…

飞天使-python的字符串转义字符元组字典等

文章目录 基础语法数据类型python的字符串运算符输入和输出 数据结构列表与元组字典与集合 参考文档 基础语法 数据类型 数值型 &#xff0c;整数 浮点型 布尔型&#xff0c; 真假&#xff0c; 假范围 字符型 类型转换python的字符串 了解转义字符一些基本的运算 \ 比如一行…

聊聊大规模敏捷框架和测试启发

这是鼎叔的第七十三篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。 之前的敏捷实践介绍文章&#xff0c;都是以单个团队&#xff08;独立交付特性的敏捷团队…

R语言对综合社会调查GSS数据进行自举法bootstrap统计推断、假设检验、探索性数据分析可视化|数据分享...

全文链接&#xff1a;https://tecdat.cn/?p33514 综合社会调查&#xff08;GSS&#xff09;是由国家舆论研究中心开展的一项观察性研究。自 1972 年以来&#xff0c;GSS 一直通过收集当代社会的数据来监测社会学和态度趋势。其目的是解释态度、行为和属性的趋势和常量。从 197…

口碑最好的洗地机排名 洗地机哪个牌子性价比高

口碑最好的洗地机排名 洗地机哪个牌子性价比高 洗地机&#xff0c;作为现代家庭和商业环境中必备的清洁设备&#xff0c;既方便又高效。不过&#xff0c;面对市场上众多的洗地机品牌&#xff0c;我们该如何选择呢&#xff1f;毫无疑问&#xff0c;我们都希望能够找到一款口碑最…

vue中使用window.open打开assets文件夹下的pdf文件

需求&#xff1a;系统有个操作手册&#xff0c;点击会在浏览器新开个窗口并打开pdf文件。这个pdf文件存储在本地assets文件夹中。 文件结构&#xff1a; 注&#xff1a;直接使用window.open(文件路径)不能打开&#xff0c;需要在vue.config.js中配置所需文件 引入图中红框中的…

AMD 锐龙 8000系 APU 将补完其产品线,推出 12C24T16CU 和 16C32T40CU

我们大家都知道一直以来&#xff0c;AMD 的 APU 只到 R7&#xff0c;也就是 R7-x700G 和 r7-x800H。虽然也有 R9-x900HX&#xff0c;但它毕竟是 x800H 硬超上去的&#xff0c;核心数完全一样&#xff0c;并不能叫做真正的 R9。 究其原因&#xff0c;AMD 的 APU 是移动端优先的…

如何将Word中的中文数字转化为阿拉伯数字

例如这种情况&#xff1a; 需要把这些汉字数字改为阿拉伯数字。 步骤1&#xff1a;在任意位置输入“第章”&#xff0c;然后把光标放到“第”和“章”的中间&#xff0c;然后ctrlf9插入域&#xff0c;在域里面输入 autonum&#xff0c;然后按altf9 显示域值。 按下altF9后 第 …

优化案例5:视图目标列改写优化

优化案例5&#xff1a;视图目标列改写优化 1. 问题描述2. 分析过程2.1 目标SQL2.2 解决思路1&#xff09;效率低的执行计划2&#xff09;视图过滤性3&#xff09;查看已有索引定义 2.3 视图改写2.4 增添复合索引 3. 优化总结 DM技术交流QQ群&#xff1a;940124259 1. 问题描述…

Typora 远程代码执行漏洞(CVE-2023-2317)

Typora 远程代码执行漏洞 【CVE-2023-2317】 一、产品介绍二、影响版本三、漏洞复现四、深入复现1.启动teamserver服务2.cs生成exe马子3.开启http.server4.导入组合拳到md文件(1)下载马子的第一个命令&#xff0c;自行base64解密修改路径(2)执行马子的第二条代码&#xff0c;注…

【高级程序设计语言C++】右值引用

1. 左值引用和右值引用2. 修改的右值3. 左值引用和右值引用的比较3.1. 左值引用总结3.2. 右值引用总结 4. 右值引用使用场景和意义5. 完美转发 1. 左值引用和右值引用 什么是左值&#xff1f;什么是左值引用&#xff1f; 左值是一个表示数据的表达式(如变量名或解引用的指针)…

计算机网络 概述部分

目录 计算机网络在信息时代的作用 计算机网络的重要特征 网络&#xff0c;internet,Internet的区别 局域网 广域网的区别 网络协议的分层 计算机网络在信息时代的作用 计算机网络的重要特征 连通性&#xff1a;彼此联通&#xff0c;交换信息 共享性&#xff1a;信息共享…

精准运营,智能决策!解锁天翼物联水利水务感知云

面向智慧水利/水务数字化转型需求&#xff0c;天翼物联基于感知云平台创新能力&#xff0c;提供涵盖水利水务泛协议接入、感知云水利/水务平台、水利/水务感知数据治理、数据看板在内的水利水务感知云服务&#xff0c;构建水利水务感知神经系统新型数字化底座&#xff0c;实现智…

WebGpu VS WebGL

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建3D应用场景 WEBGPU VS. WEBGL 粗略地概述一下WebGPU与WebGL的不同之处是很有用的。在不涉及太多复杂的技术细节的情况下&#xff0c;两者的整体设计大致如下&#xff1a; WebGL和OpenGL一样&#xff0c;涉及许多单独的函数调…

Docker基础入门:容器数据卷与Dockerfile构建镜像(发布)

Docker基础入门&#xff1a;容器数据卷与Dockerfile构建镜像&#xff08;发布&#xff09; 一、docker容器数据卷1.1、使用docker容器数据卷1.2、具名挂载、匿名挂载1.3、如何确定是具名挂载还是匿名挂载 二、使用dockerfile2.1 初识Dockerfile2.2 Dockerfile构建过程2.3 Docke…

Nacos 未授权访问(CVE-2021-29441)

Nacos 未授权访问(CVE-2021-29441) Nacos是阿里巴巴推出的一个新的开源项目。它是一个动态的服务发现、配置管理和服务管理平台&#xff0c;可以更轻松地构建云原生应用程序。致力于帮助发现、配置和管理微服务。Nacos 提供了一套简单易用的功能集&#xff0c;可以快速实现动态…

火山引擎 DataLeap 助你拥有 Notebook 交互式的开发体验

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 Notebook 是一种支持 REPL 模式的开发环境。所谓「REPL」&#xff0c;即「读取-求值-输出」循环&#xff1a;输入一段代码&#xff0c;立刻得到相应的结果&#xff…

SAP_ABAP_接口技术_PI实践总结

SAP ABAP顾问能力模型梳理_企业数字化建设者的博客-CSDN博客SAP Abap顾问能力模型https://blog.csdn.net/java_zhong1990/article/details/132469977一、背景说明 1.1 案例介绍 1.1.1 实现OA系统 --> PI系统 --> SAP系统的过程 二、实现步骤 1.2 PI中间件的实现过程 …