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

Springboot 实践(9)springboot集成Oauth2.0授权包,5个接口文件配置详解

        前文讲解实现了spring boot集成Oauth2.0,实现了授权服务器和资源服务器的搭建,并通过浏览器和postman测试,获取到了授权码,用携带授权码的URL能够争取范文到资源。

本文详细讲解spring boot集成Oauth2.0的几个重要文件接口,详细如下:

1、授权服务配置接口AuthorizationServerConfigurerAdapter

AuthorizationServerConfigurer接口,其中存在3个方法:

☆ AuthorizationServerSecurityConfigurer:配置令牌端点(Token Endpoint)的安全约束;

☆ ClientDetailsServiceConfigurer:配置OAuth2客户端;

☆ AuthorizationServerEndpointsConfigurer:配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services);

详细代码如下:bleAuthorizationServer

public class OAuthAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired

    private DataSource dataSource;

    @Autowired

    private AuthenticationManager authenticationManager;

    @Autowired

    private OAuthWebSecurityConfig oauthWebSecurityConfig;

   

  //AuthorizationServerSecurityConfigurer:配置令牌端点(Token Endpoint)的安全约束;

  //AuthorizationServerSecurityConfigurer继承自SecurityConfigurerAdapter,也就是一个 Spring Security安全配置提供给AuthorizationServer去配置AuthorizationServer的端点(/oauth/****)的安全访问规则、过滤器Filter。

    @Override

    public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {

        oauthServer

        .tokenKeyAccess("permitAll()")//客户端token调用许可

        .checkTokenAccess("permitAll()")//客户端校验token访问许可

       .allowFormAuthenticationForClients();       

    }

   

    //refresh_token 单独配置UserDetailsService

    @Bean

    public UserDetailsService userDetailsServiceRefresh_token() { 

        return oauthWebSecurityConfig.userDetailsService();

}

    //AuthorizationServerEndpointsConfigurer:配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services);

    @Override

    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints // 设置令牌   

                .tokenStore(new JdbcTokenStore(dataSource)).userDetailsService(userDetailsServiceRefresh_token())

                .authenticationManager(authenticationManager);

    }

    @Bean // 声明 ClientDetails实现

    public ClientDetailsService clientDetailsService() {

        return new JdbcClientDetailsService(dataSource);

    }   

    //ClientDetailsServiceConfigurer:配置OAuth2客户端;

    @Override

    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {

        clients.withClientDetails(clientDetailsService());

    }   

}

2、资源服务配置接口ResourceServerConfigurerAdapter

        ResourceServerConfigurerAdapter (资源服务器配置)内部关联了ResourceServerSecurityConfigurer 和 HttpSecurity。前者与资源安全配置相关,后者与http安全配置相关,详细代码如下:

/*** 资源服务器配置*/

@Configuration

@EnableResourceServer

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled = true)

@Order(-1)

public class OAuthResourcesServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired

    private DataSource dataSource;     

    //在每个ResourceServer实例上设置resourceId,该resourceId作为该服务资源的唯一标识。(假如同一个微服务资源部署多份,resourceId相同)

    private static final String DEMO_RESOURCE_ID = "test-resource";

    @Override

    public void configure(ResourceServerSecurityConfigurer resources) {

       resources.resourceId(DEMO_RESOURCE_ID);  //配置resourceServerID

       resources.tokenStore(new JdbcTokenStore(dataSource)); //...... 还可以有有其他的配置 

    }

    @Override

    public void configure(HttpSecurity http) throws Exception {

        http   

            .cors()//开启跨域

            .and()

            .csrf().disable()

            .logout()

            .logoutUrl("/logout")//虚拟的登出地址

            .and()

            .authorizeRequests()

            .antMatchers("/loginSuccess").permitAll()

            .antMatchers("/loginSuccess.html").permitAll()

            .antMatchers("/actuator/health").permitAll()

            .anyRequest().authenticated();

    }

}

3、web安全配置接口WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter是Spring Security提供了一个抽象类,实现了默认的认证和授权,允许用户自定义一个WebSecurity类,重写其中的三个configure来实现自定义的认证和授权,这三个方法如下:

  • 自定义身份认证的逻辑

protected void configure(AuthenticationManagerBuilder auth) throws Exception { }

  • 自定义全局安全过滤的逻辑

public void configure(WebSecurity web) throws Exception { }

  • 自定义URL访问权限的逻辑

protected void configure(HttpSecurity http) throws Exception { }

详细代码如下:

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled = true)

@Order(-1)

public class OAuthWebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired

  private MyUserDetailServiceImpl myUserDetailServiceImpl;

  @Autowired

  private LogoutSuccessHandlerImpl logoutSuccessHandler;

  @Autowired

  private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

  @Autowired

  private DataSource dataSource;

  /** * @return RememberMe 功能的 Repository */

  @Bean

  public PersistentTokenRepository persistentTokenRepository() {

      // 连接数据库的实现类,还有一个实现类时存内存的InMemoryTokenRepositoryImpl

      JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();

      // 配置数据源

      tokenRepository.setDataSource(dataSource);

      // 在启动数据库时创建存储token的表

      //tokenRepository.setCreateTableOnStartup(true);

      return tokenRepository;

  }

  /******************************remember me***********************************/

  @Override

    @Bean

    public AuthenticationManager authenticationManagerBean() throws Exception {

        return super.authenticationManagerBean();

    }

  /** 放行静态资源 */

  @Override

    public void configure(WebSecurity web) throws Exception {

      web.ignoring().antMatchers("/swagger-ui.html");

      web.ignoring().antMatchers("/swagger-resources/**");

      web.ignoring().antMatchers("/v2/api-docs");

      web.ignoring().antMatchers("/configuration/security");

      web.ignoring().antMatchers("/configuration/ui");

      //web.ignoring().antMatchers("/webjars/springfox-swagger-ui/**");

     

      /************************************************************

       * servlet 3.0 以上的版本支持直接访问 jar 包里面的资源文件。

       * 访问方式:将 jar 包里的 META-INF/resources 目录看成根目录,

       * 则这个目录下的文件都可以直接访问

       * swagger-bootstrap-ui-1.9.6.jar资源放行*/

      web.ignoring().antMatchers("/webjars/**");

      web.ignoring().antMatchers("/doc.html");

     

      web.ignoring().antMatchers("/loginSuccess");

      web.ignoring().antMatchers("/loginSuccess.html");

      web.ignoring().antMatchers("/loginError");

      web.ignoring().antMatchers("/lock");

      web.ignoring().antMatchers("/assets/**");

      //路由跳转允许

      web.ignoring().antMatchers("/api/**");

      web.ignoring().antMatchers("/websocket/**"); //农村饮水安全

      web.ignoring().antMatchers("/Serialnum/EnSure"); //序列号确认

      web.ignoring().antMatchers("/lock"); //锁屏页面

      web.ignoring().antMatchers("/login.html"); //序列号确认

      //放行consul安全监测接口

      web.ignoring().antMatchers("/v1");

      web.ignoring().antMatchers("/actuator/health");

      //web.ignoring().antMatchers("/loginError.html");

      web.ignoring().antMatchers("/favicon.ico");

      //放行单点登录

      web.ignoring().antMatchers("/index");

      web.ignoring().antMatchers("/index/**");

      web.ignoring().antMatchers("/index/oauth_callback");

      //web.ignoring().antMatchers("/myLogout");

     

    }

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http       

        .requestMatchers().antMatchers(HttpMethod.OPTIONS,"/login", "/oauth/authorize", "/oauth/token")

        .and()

        .cors()//开启跨域

      .and()

      .csrf().disable()              

        .requestMatchers()       

        .antMatchers("/login")

        .antMatchers(HttpMethod.OPTIONS)

        .antMatchers("/oauth/authorize")

        .and()    

      .rememberMe()//记住我相关配置

      .tokenRepository(persistentTokenRepository())

      .tokenValiditySeconds(60*60*10)

        //.antMatchers("/oauth/token")

        .and()           

        .authorizeRequests()

        .anyRequest().authenticated()

        .and()

        .formLogin()

        .loginPage("/login")

           // 自定义登录页面,这里配置了 loginPage, 就会通过 LoginController 的 login 接口加载登录页面

        // 登入成功后,跳转至指定页面

        //.defaultSuccessUrl("/loginSuccess")

        .failureUrl("/loginError").permitAll()       

        .and()

        .logout()

        .logoutUrl("/logout")//虚拟的登出地址

        .logoutSuccessHandler(logoutSuccessHandler);

       

        http.formLogin().failureHandler(customAuthenticationFailureHandler);

    }

   

    @Bean

    @Override

    public UserDetailsService userDetailsService(){

        return myUserDetailServiceImpl;

        //return userAuthenticationProvider;

    }

   

    @Override

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        // 配置用户名密码,这里采用内存方式,生产环境需要从数据库获取   

//        auth.inMemoryAuthentication()

//            .withUser("admin")

//            .password(passwordEncoder().encode("123"))          

//            .roles("USER");

     

        // 使用自定义认证与授权

        auth.userDetailsService(userDetailsService());

    }

    @Bean

    public BCryptPasswordEncoder passwordEncoder(){

        return new BCryptPasswordEncoder();

    }

}

4、用户登录服务接口UserDetailsService

        Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现,但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider,每个AuthenticationProvider会轮流检查身份认证。检查后或者返回Authentication对象或者抛出异常。验证身份就是加载响应的UserDetails,看看是否和用户输入的账号、密码、权限等信息匹配。UserDetails中需要重定义loadUserByUsername()函数。

详细代码如下:

@Component

public class MyUserDetailServiceImpl implements UserDetailsService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired

    private SysUserService sysUserService;

    @Autowired

    private SysUserMapper sysUserMapper;

    /**

     * @param username

     * @return

     * @throws UsernameNotFoundException

     */

    @Override

    public UserDetails loadUserByUsername(String username) {

        //1.根据用户名去数据库去查询用户信息获取加密后的密码         

        SysUser sysUser = sysUserMapper.findByName(username);

        if(sysUser==null) {

            logger.info("-------------未查询到用户名:" + username);

            throw new UsernameNotFoundException("Invalid username or password.");

        }else {        

             Boolean stateStr=sysUser.getAccountNonLocked();//锁定状态

            if(stateStr==false){//用户被锁定

                long lockTime = sysUser.getLockTime();

                long sysTime = System.currentTimeMillis();

                long time = sysTime - lockTime;

                if(time>=10*60*1000) { //时间超过10分钟,解锁账户

                    sysUserMapper.updateUNLockedByUserId(username);

                    sysUserMapper.updatelockTimeByUserId(username, 0L);

                    sysUserMapper.updatenLockByUserId(username, 0L);

                    stateStr=true;

                }else {

                    throw new InternalAuthenticationServiceException("该账号已被锁定,请联系管理员!");

                }               

             }

           

            Set<String> permissions = sysUserService.findPermissions(username);        

            List<GrantedAuthority> grantedAuthorities = permissions.stream().map(GrantedAuthorityImpl::new).collect(Collectors.toList());

                        

            return new User(username,

                    //encryptedPassWord,

                    sysUser.getPassword(),

                    true,

                    true,

                    true,

                    stateStr, //锁定状态               

                    grantedAuthorities);

        }         

    } 

}

5、授权失败接口SimpleUrlAuthenticationFailureHandler

        在Spring Security Web框架内部,缺省使用的认证错误处理策略是AuthenticationFailureHandler的实现类SimpleUrlAuthenticationFailureHandler。它由配置指定一个defaultFailureUrl,表示认证失败时缺省使用的重定向地址。一旦认证失败,它的方法onAuthenticationFailure被调用时,它就会将用户重定向到该地址。如果该属性没有设置,它会向客户端返回一个401状态码。另外SimpleUrlAuthenticationFailureHandler还有一个属性useForward,如果该属性设置为true,页面跳转将不再是重定向(redirect)机制,取而代之的是转发(forward)机制。

该处只需要重定义onAuthenticationFailure()详细代码如下:

//3、将登录失败提示到前端展示

@Component

public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  private  static ObjectMapper objectMapper = new ObjectMapper();

  @Autowired

  private ThymeleafViewResolver viewResolver;

  private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

   

  @Override

  public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {        

  //此处自行定义授权失败功能。

  }

}

        springboot集成Oauth2.0授权包,接口文件配置详解到此就结束了,学友在实际操作过程中遇到问题,欢迎联系博主。

下文讲解spring cloud的配置与应用,实现服务注册机制。

相关文章:

Springboot 实践(9)springboot集成Oauth2.0授权包,5个接口文件配置详解

前文讲解实现了spring boot集成Oauth2.0&#xff0c;实现了授权服务器和资源服务器的搭建&#xff0c;并通过浏览器和postman测试&#xff0c;获取到了授权码&#xff0c;用携带授权码的URL能够争取范文到资源。 本文详细讲解spring boot集成Oauth2.0的几个重要文件接口&#…...

最新AI系统ChatGPT程序源码/支持GPT4/自定义训练知识库/GPT联网/支持ai绘画(Midjourney)+Dall-E2绘画/支持MJ以图生图

一、前言 SparkAi系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。 那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01…...

【高频面试题】 消息中间件

文章目录 1、RabbitMQ1.1 RabbitMQ-如何保证消息不丢失1.2 RabbitMQ消息的重复消费问题如何解决的1.3 RabbitMQ中死信交换机 ? (RabbitMQ延迟队列有了解过嘛)1.4 RabbitMQ如果有100万消息堆积在MQ , 如何解决(消息堆积怎么解决)1.5 RabbitMQ的高可用机制有了解过嘛 2、Kafka2.…...

物联网智慧安防实训综合实训基地建设方案

一、系统概述 物联网智慧安防实训综合实训基地是一个为学生提供综合实践、培养技能的场所&#xff0c;专注于物联网技术与智慧安防应用的培训和实训。通过物联网智慧安防实训综合实训基地的建设和运营&#xff0c;学生可以在真实的环境中进行实践训练&#xff0c;提高其物联网技…...

openGauss学习笔记-44 openGauss 高级数据管理-存储过程

文章目录 openGauss学习笔记-44 openGauss 高级数据管理-存储过程44.1 语法格式44.2 参数说明44.3 示例 openGauss学习笔记-44 openGauss 高级数据管理-存储过程 存储过程是能够完成特定功能的SQL语句集。用户可以进行反复调用&#xff0c;从而减少SQL语句的重复编写数量&…...

【Linux】进程信号篇Ⅲ:可重入函数、volatile关键字、SIGCHLD信号

信号Ⅲ &#x1f517; 接上篇七、可重入函数八、volatile 关键字九、SIGCHLD 信号 &#x1f517; 接上篇 &#x1f449;&#x1f517;进程信号篇Ⅰ&#xff1a;信号的产生&#xff08;signal、kill、raise、abort、alarm&#xff09;、信号的保存&#xff08;core dump&#x…...

排序算法:冒泡排序

冒泡排序是入门级的算法&#xff0c;但也有一些有趣的玩法。通常来说&#xff0c;冒泡排序有三种写法&#xff1a; 一边比较一边向后两两交换&#xff0c;将最大值 / 最小值冒泡到最后一位&#xff1b;经过优化的写法&#xff1a;使用一个变量记录当前轮次的比较是否发生过交换…...

Spring事件监听源码解析

spring事件监听机制离不开容器IOC特性提供的支持&#xff0c;比如容器会自动创建事件发布器&#xff0c;自动识别用户注册的监听器并进行管理&#xff0c;在特定的事件发布后会找到对应的事件监听器并对其监听方法进行回调。Spring帮助用户屏蔽了关于事件监听机制背后的很多细节…...

Cpp学习——list的模拟实现

目录 一&#xff0c;实现list所需要包含的三个类 二&#xff0c;三个类的实现 1.list_node 2.list类 3.iterator_list类 三&#xff0c;功能实现 1.list类里的push_back() 2.iterator类里的运算符重载 3&#xff0c;list类里面的功能函数 1.insert&#xff08;&#xff…...

工具推荐:Chat2DB一款开源免费的多数据库客户端工具

文章首发地址 Chat2DB是一款开源免费的多数据库客户端工具&#xff0c;适用于Windows和Mac操作系统&#xff0c;可在本地安装使用&#xff0c;也可以部署到服务器端并通过Web页面进行访问。 相较于传统的数据库客户端软件如Navicat、DBeaver&#xff0c;Chat2DB具备了与AIGC…...

C语言刷题指南(二)

&#x1f4d9;作者简介&#xff1a; 清水加冰&#xff0c;目前大二在读&#xff0c;正在学习C/C、Python、操作系统、数据库等。 &#x1f4d8;相关专栏&#xff1a;C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d…...

[C++11]

文章目录 1. 自动类型推导1.1 auto1.1.1 推导规则1.1.2 auto的限制1.1.3 auto的应用1.1.4 范围for 1.2 decltype1.2.1 推导规则1.2.2 decltype的应用 1.3 返回类型后置 2.可调用对象包装器、绑定器2.1 可调用对象包装器2.1.1 基本用法2.1.2 作为回调函数使用 2.2 绑定器 3. usi…...

【MySQL系列】--初识数据库

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …...

Unity导入google.protobuf失败,无法找到google命名空间

问题&#xff1a; 1.刚开始把protobuf的文件夹直接从其他项目里(unity2021)里复制到unity(2020)版本&#xff0c;当时报错protobuf.dll的依赖项system.memory版本不对。 2.没有使用原来的protobuf文件了。使用vs2019的NuGet管理包来下载Google.Protobuf &#xff0c;仍然报错找…...

使用IDM下载视频出现“由于法律原因,IDM无法下载...

一、问题描述 由于法律原因,IDM无法下载..,如图: 二、原因分析 下载该IDM抓取的M3U8文件,查看其中的内容发现 : #EXT-X-KEY 字段已经写明了加密方式是AES-128,包含一个URI和IV值 #EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:8 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:…...

pointnet C++推理部署--tensorrt框架

classification 如上图所示&#xff0c;由于直接export出的onnx文件有两个输出节点&#xff0c;不方便处理&#xff0c;所以编写脚本删除不需要的输出节点193&#xff1a; import onnxonnx_model onnx.load("cls.onnx") graph onnx_model.graphinputs graph.inpu…...

34.Netty源码之Netty如何处理网络请求

highlight: arduino-light 通过前面两节源码课程的学习&#xff0c;我们知道 Netty 在服务端启动时会为创建 NioServerSocketChannel&#xff0c;当客户端新连接接入时又会创建 NioSocketChannel&#xff0c;不管是服务端还是客户端 Channel&#xff0c;在创建时都会初始化自己…...

vscode 安装勾选项解释

1、通过code 打开“操作添加到windows资源管理器文件上下文菜单 &#xff1a;把这个两个勾选上&#xff0c;可以对文件使用鼠标右键&#xff0c;选择VSCode 打开。 2、将code注册为受支持的文件类型的编辑器&#xff1a;不建议勾选&#xff0c;这样会默认使用VSCode打开支持的相…...

Spring 6.0官方文档示例(24): replace-method的用法

一、原始bean定义 package cn.edu.tju.study.service.anno.domain;public class MyValueCalculator {public String computeValue(String input) {return "you inputted: " input;}// some other methods... }二、replace bean定义 package cn.edu.tju.study.serv…...

自然语言处理从入门到应用——LangChain:记忆(Memory)-[聊天消息记录]

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 Cassandra聊天消息记录 Cassandra是一种分布式数据库&#xff0c;非常适合存储大量数据&#xff0c;是存储聊天消息历史的良好选择&#xff0c;因为它易于扩展&#xff0c;能够处理大量写入操作。 # List of contact…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...