SpringBoot--中间件技术-4:整合Shiro,Shiro基于会话SessionManager实现分布式认证,附案例含源代码!

news2024/11/26 10:34:13

SpringBoot整合安全中间件Shiro

技术栈:SpringBoot+Shiro

代码实现

  1. pom文件加坐标

    Springboot版本选择2.7.14 ;java版本1.8 ; shiro做了版本锁定 1.3.2

    <properties>
      <java.version>1.8</java.version>
      <!--shiro版本锁定-->
      <shiro.version>1.3.2</shiro.version>
    </properties>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
    
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
      </dependency>
      <!--lombok-->
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.16</version>
      </dependency>
      <!--mysql-->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
      </dependency>
      <!--mp-->
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3</version>
      </dependency>
    
      <!-- SECURITY begin -->
      <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>
      <!-- SECURITY end -->
    </dependencies>
    
  2. 主配置文件

    #配置数据源
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/spring?serverTimezone=GMT
        username: root
        password: 123456
    
    #配置自动驼峰映射
    mybatis:
      configuration:
        map-underscore-to-camel-case: true
      type-aliases-package: com.dong.pojo
    #MP配置自动驼峰映射
    mybatis-plus:
      configuration:
        map-underscore-to-camel-case: true
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #mybatis所执行的sql输出控制台
    
  3. POJO实体类

    Permission

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @Component
    @TableName(value = "pe_permission")
    public class Permission {
        @TableField(value = "id")
        private String id;
        @TableField(value = "name")
        private String name;
        @TableField(value = "code")
        private String code;
        @TableField(value = "description")
        private String description;
    }
    

    Role

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @TableName(value = "pe_role")
    @Component
    public class Role {
        @TableField(value = "id")
        private String id;
        @TableField(value = "name")
        private String name;
        @TableField(value = "code")
        private String code;
        @TableField(value = "description")
        private String description;
    
        // 外部属性
        @TableField(exist = false)
        private List<Permission> permissions;
    }
    

    Users

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @TableName(value = "pe_user")
    @Component
    public class Users {
        @TableId(value = "id")
        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 List<Role> rolesList;
        public Users(String id, String username, String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
    }
    
  4. dao层

    UsersMapper

    @Mapper
    public interface UserMapper extends BaseMapper<Users> {
        @Insert("insert into pe_user(id,username,password,salt) values(#{id},#{username},#{password},#{salt})")
        public int save(Users users);
    
        // 级联查询
        @Results(id = "users",value = {
                @Result(column = "id",property = "rolesList",many = @Many(select = "com.dong.springboot_mp_shiro.com.dong.mapper.RoleMapper.findById"))
        })
    
        @Select("select * from pe_user where username=#{v}")
        public Users findUserDetail(String name);
    
        // 简单查询
        @Select("select * from pe_user where username=#{v}")
        public Users findBaseUser(String name);
    }
    

    RoleMapper

    @Mapper
    public interface RoleMapper extends BaseMapper<Role> {
    
        @Results(id = "role",value = {
                @Result(column = "id",property = "permissions",many=@Many(select = "com.dong.springboot_mp_shiro.com.dong.mapper.PermissionMapper.findByPermissionId"))
        })
    
        @Select("select * from pe_role where id in (select role_id from pe_user_role where user_id =#{v} )")
        public Role findById(String id);
    
    }
    

    PermissionMapper

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.dong.springboot_mp_shiro.com.dong.pojo.Permission;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    @Mapper
    public interface PermissionMapper extends BaseMapper<Permission> {
    
        @Select("select * from pe_permission where id in (select permission_id from pe_role_permission where role_id =#{v})")
        public Permission findByPermissionId(String permissionId);
    }
    
  5. service层

    接口:

    IUserService

    public interface IUserService {
        public int save(Users users);
    
        public Users baseFindUser(String name);
    
        public Users findUserDetail(String Name);
    }
    

    IRoleService

    public interface IRoleService {
    }
    

    IPermissionService

    public interface IPermissionService {
    }
    

    实现类:

    UserServiceImp

    @Service
    public class UserServiceImp implements IUserService {
    
      @Autowired(required = false)
      private UserMapper uMapper;
    
      @Override
      public int save(Users users) {
        System.out.println("service:"+ users);
    
        // 获取salt字符串
        String salt = DigestsUtil.generateSalt();
        // 密码加密
        String password = DigestsUtil.generatePassword(users.getPassword(), salt);
    
        users.setPassword(password);
        users.setSalt(salt);
    
        int res = uMapper.save(users);
        return res;
      }
    
      @Override
      public Users baseFindUser(String name) {
        Users baseUser = uMapper.findBaseUser(name);
        return baseUser;
      }
    
      @Override
      public Users findUserDetail(String name) {
        Users userDetail = uMapper.findUserDetail(name);
        return userDetail;
      }
    }
    
  6. controller层

    @RestController
    public class UserController {
        @Autowired(required = false)
        private UserServiceImp service;
    
        // 首页
        @RequiresPermissions("user-home")
        @RequestMapping("/user/home")
        public  String home(){
            return "访问个人主页成功";
        }
    
        // 用户注册
        @RequiresPermissions("user-add")
        @RequestMapping("/user/{id}")
        public String save(@PathVariable String id){
            /*int res = service.save(users);
            if(res>0){
                return "添加成功";
            }else{
                return "添加失败";
            }*/
            return "新增成功";
        }
    
        @RequiresPermissions("user-delete")
        @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
        public String delete(@PathVariable String id){
            return "删除成功";
        }
    
        @RequiresPermissions("user-update")
        @RequestMapping(value = "/user/{id}",method = RequestMethod.PUT)
        public String update(@PathVariable String id){
            return "修改成功";
        }
    
        @RequiresPermissions("user-find")
        @RequestMapping(value = "/user",method = RequestMethod.GET)
        public String find(){
            return "查询成功";
        }
    
        // 登录认证
        @RequestMapping("/login")
        public String login(Users users){
            try {
                // 构造登录令牌
                UsernamePasswordToken token = new UsernamePasswordToken(users.getUsername(), users.getPassword());
                // 获取subject
                Subject subject = SecurityUtils.getSubject();
                // 调用subject认证
                subject.login(token);
                return "登录成功";
            } catch (AuthenticationException e) {
                return  "用户名或密码错误";
            }
        }
    
        // 未登录跳转
        @RequestMapping("/autherror")
        public String autherror(){
            return "未认证,请登录";
        }
    }
    

    ==@RequiresPermissions(" "):==标注访问该资源需要的权限

    • 执行subject.long登录方法,执行Reaml的AuthenticationException方法

    • 鉴权授权,执行Reaml的AuthorizationInfo方法

  7. MyRealm

    public class MyRealm extends AuthorizingRealm {
    
        @Autowired(required = false)
        private UserServiceImp serviceImp;
    
         // 授权鉴权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            // 获取已经认证的用户数据
            Users users = (Users) principalCollection.getPrimaryPrincipal();
    
            // 查询用户的详细信息
            Users userDetail = serviceImp.findUserDetail(users.getUsername());
    
            HashSet<String> perms = new HashSet<>(); // 权限set集合
            HashSet<String> roles = new HashSet<>(); // 角色set集合
    
            for(Role role: userDetail.getRolesList() ){
                roles.add(role.getCode());
                for(Permission permission: role.getPermissions() ){
                    perms.add(permission.getCode());
                }
            }
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.setStringPermissions(perms);
            info.setRoles(roles);
            return info;
        }
    
        // 认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
           // 获取用户登陆输入的密码(token)
            UsernamePasswordToken upToken =  (UsernamePasswordToken)authenticationToken;
            // 用户输入的账号
            String username = upToken.getUsername();
            Users users = serviceImp.baseFindUser(username);
            if(users != null){
                SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(users, users.getPassword(), ByteSource.Util.bytes(users.getSalt()), "MyRealm");
                return info;
            }
            // 账号查不到,返回null(抛出异常)
            return null;
        }
    
        @PostConstruct   // 属性初始化
        public void  initCredentialsMatcher(){
            // 指定密码算法
            HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
            // 指定迭代次数
            hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);
            // 生成密码比较器
            setCredentialsMatcher(hashedCredentialsMatcher);
        }
    }
    

    @PostConstruct注解,属性初始化

    加密工具类

    public class DigestsUtil {
        // 编码方式
        public static final String SHA1="SHA-1";
        // 加密次数
        public static final Integer COUNTS=369;
    
        //  获取salt字符串
        public static String generateSalt(){
            SecureRandomNumberGenerator secureRandomNumberGenerator = new SecureRandomNumberGenerator();
            return secureRandomNumberGenerator.nextBytes().toHex();
        }
    
        // 生成密文密码
        public static String generatePassword(String input,String salt){
            return new SimpleHash(SHA1,input,salt,COUNTS).toString();
        }
    
    }
    
  8. Shiro配置类:ShiroConfiguration

    @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();    //new 自定义的Reaml
        }
    
        /**
         * 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 SecurityManager 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 filterFactoryBean = new ShiroFilterFactoryBean();
            // 2.设置安全管理器
            filterFactoryBean.setSecurityManager(defaultWebSecurityManager());
            // 3.通用配置(跳转登录页面,为授权跳转的页面)
            filterFactoryBean.setLoginUrl("/autherror");
            //4.设置过滤器集合
            //key = 拦截的url地址
            //value = 过滤器类型
            LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
            filterMap.put("/login","anon");//当前请求地址可以匿名访问
            filterMap.put("/user/**","authc");// 当前请求地址必须认证之后才可以访问
    			// 在过滤器工程内设置系统过滤器
          filterFactoryBean.setFilterChainDefinitionMap(filterMap);
            return filterFactoryBean;
        }
    }
    
  9. 统一异常处理器

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

Shiro实现分布式会话SessionManager

代码结构:代码结构和SpringBoot整合Shiro中的案例相同

问题:如图

在这里插入图片描述

解决思路:将当前的session会话存到缓存Redis中

在这里插入图片描述

实现步骤:

  1. 创建RedisSessionDao extends AbstractSessionDAO
  2. 配置ShiroConfig

代码实现:

  1. RedisSessionDao

    public class RedisSessionDao extends AbstractSessionDAO {
    
        @Autowired
        private RedisTemplate redisTemplate;
      
        //创建会话
        @Override
        protected Serializable doCreate(Session session) {
            Serializable sessionId = generateSessionId(session);
            assignSessionId(session, sessionId);
            redisTemplate.opsForValue().set(sessionId,session);
            return sessionId;
        }
    
        @Override
        protected Session doReadSession(Serializable sessionId) {
            return (Session) redisTemplate.opsForValue().get(sessionId);
        }
    
        @Override
        public void delete(Session session) {
            redisTemplate.delete(session.getId());
        }
    
        @Override
        public Collection<Session> getActiveSessions() {
            return Collections.emptySet();
        }
    
        @Override
        public void update(Session session) {
            redisTemplate.opsForValue().set(session.getId(),session);
        }
    
    }
    
  2. 需要操作Redis,整合Redis

    1. 导入redis坐标

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      
    2. yaml主配置文件配置redis

      server:
        port: 8080
      #配置数据源
      spring:
        datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/spring?serverTimezone=GMT
          username: root
          password: 123456
        redis:
          port: 6379
      
      #配置自动驼峰映射
      mybatis:
        configuration:
          map-underscore-to-camel-case: true
        type-aliases-package: com.dong.pojo
      #MP配置自动驼峰映射
      mybatis-plus:
        configuration:
          map-underscore-to-camel-case: true
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #mybatis所执行的sql输出控制台
      
    3. ShiroConfig

      向容器中注入一个SessionDao,把SessionDao绑定给会话管理器

      @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();
          }
      
          // 向容器中注入一个SessionDao
          @Bean
          public SessionDAO redisSessionDao(){
              RedisSessionDao sessionDAO =   new RedisSessionDao();
              return sessionDAO;
          }
      
          /**
           * 3.创建会话管理器
           */
          @Bean
          public DefaultWebSessionManager sessionManager(){
              DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            	//把SessionDao绑定给会话管理器
              sessionManager.setSessionDAO(redisSessionDao()); 
            	
              sessionManager.setSessionValidationSchedulerEnabled(false);
              sessionManager.setSessionIdCookieEnabled(true);
              sessionManager.setSessionIdCookie(sessionIdCookie());
              sessionManager.setGlobalSessionTimeout(3600000);
              return sessionManager;
          }
      
          //4.创建安全管理器
          @Bean
          public SecurityManager 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 filterFactoryBean = new ShiroFilterFactoryBean();
              // 2.设置安全管理器
              filterFactoryBean.setSecurityManager(defaultWebSecurityManager());
              // 3.通用配置(跳转登录页面,为授权跳转的页面)
              filterFactoryBean.setLoginUrl("/autherror");
              //4.设置过滤器集合
              //key = 拦截的url地址
              //value = 过滤器类型
              LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
              filterMap.put("/login","anon");//当前请求地址可以匿名访问
              filterMap.put("/user/**","authc");
              filterFactoryBean.setFilterChainDefinitionMap(filterMap);
              return filterFactoryBean;
          }
      }
      

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

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

相关文章

JavaScript的函数的形参与实参是怎么回事

0 写在前面 此文给小白看的&#xff0c;如果不是可以直接关闭 1 讲解 例如JavaScript中定义函数 //定义函数 function 方法名(形参){方法体-->使用形参}//使用函数 方法名字(实参)具体干了什么呢&#xff1f;此处以伪代码举例 //定义函数 function eat(A,B){A 去 B 家吃…

每日一题:编写程序,使程序分别输出两个整数的加减乘除运算结果

文章目录 每日一题一、编写程序&#xff0c;使程序分别输出两个整数的加减乘除运算结果以下是一个使用 Java 编写的程序&#xff0c;可以输出两个整数的加减乘除运算结果&#xff1a;以下是一个简单的 Python 程序&#xff0c;可以计算两个整数的加减乘除运算结果&#xff1a; …

mindspore mindyolo目标检测华为昇腾上推理使用、训练;华为OBS文件传输使用

参考&#xff1a; https://github.com/mindspore-lab/mindyolo 使用案例&#xff1a; https://github.com/mindspore-lab/mindyolo/blob/master/GETTING_STARTED.md 安装&#xff1a; pip install mindyolo特别注意opencv-python、opencv-python-headless版本问题&#xff0…

阿里云2核2G3M带宽服务器,新老用户同价99元/年!续费不涨价!

作为双11服务器中备受用户关注的一款&#xff0c;轻量服务器2核2G3M带宽优惠价87元一年的价格令人惊喜。不仅价格实惠&#xff0c;而且配置也十分出色。2核2G的配置足够应对一般网站和轻量级应用的需求&#xff0c;同时3M的带宽也能够保障数据的快速传输。对于个人网站、小型企…

CSDN每日一题学习训练——Java版(二叉搜索树迭代器、二叉树中的最大路径和、按要求补齐数组)

版本说明 当前版本号[20231115]。 版本修改说明20231115初版 目录 文章目录 版本说明目录二叉搜索树迭代器题目解题思路代码思路参考代码 二叉树中的最大路径和题目解题思路代码思路参考代码 按要求补齐数组题目解题思路代码思路参考代码 二叉搜索树迭代器 题目 实现一个二…

DevExpress WinForms HeatMap组件,一个高度可自定义热图控件!

通过DevExpress WinForms可以为Windows Forms桌面平台提供的高度可定制的热图UI组件&#xff0c;体验DevExpress的不同之处。 DevExpress WinForms有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。同时能完美构建流畅、美观且易于使用的应用程…

系列三、双亲委派机制

一、概述 当一个类收到了类加载的请求&#xff0c;它首先不会尝试自己去加载这个类&#xff0c;而是把这个请求委派给父类去完成&#xff0c;每一层的类加载器都是如此&#xff0c;因此所有的请求都应该传送到启动类加载器中&#xff0c;只有当父类加载器反馈自己无法完成这个…

人工智能赋能职业教育:技术融合引领教育变革

人工智能赋能职业教育&#xff1a;技术融合引领教育变革 摘要&#xff1a;本文探讨了人工智能技术在职业教育领域的应用及其带来的变革。通过分析人工智能在个性化教学、智能评估和教学资源优化等方面的技术优势&#xff0c;结合职业教育的现状和发展需求&#xff0c;提出了人…

计算机网络之网络体系结构

计算机网络体系结构 一、常见的计算机体系结构 1.1 OSI标准以及TCP/IP体系结构 OSI标准失败的原因&#xff1a; OSI的专家们缺乏实际经验&#xff0c;他们在完成OSI标准时没有商业驱动力OSI的协议实现起来过分复杂&#xff0c;而且运行效率很低OSI标准的制定周期太长&#x…

【ArcGIS Pro二次开发】(76):面积平差工具

之前做过一个【三调土地利用现状分类面积汇总】的工具&#xff0c;在流程中使用了面积平差的方法。 考虑了在其它场合可能也需要进行面积平差&#xff0c;因此单独提取出来作为一个工具。 平差实现的方法如下图&#xff1a; 主要的计算过程如上图所示&#xff0c;算出总面积差…

队列的实现---超详细

队列的实现—超详细 文章目录 队列的实现---超详细一、队列的模型二、代码实现以及测试用例①队列初始化②入队③出队④输出队头⑤输出队尾⑥判断队列是否为空⑦队列的长度⑧队列的销毁⑨测试用例 一、队列的模型 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在…

从零搭建微服务架构:Spring Boot与Nacos完美整合

&#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 从零搭建微服务架构&#xff1a;Spring Boot与Nacos完美整合 前言第一&#xff1a;服务注册与发现第二&#xff1a;配置中心第三&#xff1a;报错问题解决第四&#xff1a;什…

深度学习+opencv+python实现车道线检测 - 自动驾驶 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

【数据结构】单链表基本操作的实现

【单链表的头插和尾插】//无头结点 #include<stdio.h> #include<stdlib.h> typedef struct LNode {int date;struct LNode *next; }LNode,*LinkList; LinkList great_LinkList(LinkList L)//头部插入 {LinkList s;int x,j1;scanf("%d",&x);while(x…

OPPO Watch纯手机开启远程ADB调试

Wear OS手表中&#xff0c;我们可以直接在开发者设置中打开WiFi调试。但是这在OPPO等魔改Android系统中不再奏效。 需要什么&#xff1f;&#xff1f; 手表一台手机一个OTG转接头一个手表充电器一个 演示设备 手机&#xff1a; OPPO Find X手表&#xff1a; OPPO Watch 1代 …

第十九章绘图

Java绘图类 Graphics 类 Grapics 类是所有图形上下文的抽象基类&#xff0c;它允许应用程序在组件以及闭屏图像上进行绘制。Graphics 类封装了Java 支持的基本绘图操作所需的状态信息&#xff0c;主要包括颜色、字体、画笔、文本、图像等。 Graphics 类提供了绘图常用的…

Oracle OCP / MySQL OCP认证容易通过吗

诸多学员在首次考OCP时&#xff0c;不清楚要如何选择。在本文中&#xff0c;我会为大家进行讲解&#xff01; 选择OCP认证时需要考虑的几大项目&#xff1a; 授课老师师资经验 课程大纲 试听课程 考试通过率 业界口碑 服务质量 郭一军老师的OCP培训在业界培训的学员中已…

SystemVerilog学习 (6)——验证平台

一、概述 测试平台&#xff08;Testbench&#xff09;是整个验证系统的总称。它包含了验证系统的各个组件、组件之间的互联关系&#xff0c;测试平台的配置与控制等&#xff0c; 从更系统的意义来讲&#xff0c;它还包括编译仿真的流程、结果分析报告和覆盖率检查等。 从狭义上…

【Phoenix】请求的生命周期

本文的目的是讨论Phoenix请求的生命周期。我们实战添加两个新的页面&#xff0c;并讨论整个过程是如何串起来的。 让我们从添加第一个新页面开始。 添加一个新页面 web应用通常通过将HTTP方法和路径映射到应用的某个函数来处理请求。Phoenix通过路由器来实现这个匹配。例如将…

蒙HarmonyOS从零实现类微信app效果第二篇,我的+发现页面实现

本着不拖更的原则&#xff0c;今天上新了&#xff0c;今天实现了类微信app的发现页和我的页面。先看效果。 效果是不是看着还不错。其实这两个页面功能实现还是比较简单的&#xff0c;接下来还是老规矩&#xff0c;先进行页面的拆分和代码实现&#xff0c;然后进行相关我认为比…