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

实战项目 在线学院之集成springsecurity的配置以及执行流程

一  后端操作配置

1.0  工程结构

1.1 在common下创建spring_security模块

1.2 pom文件中依赖的注入

1.3 在service_acl模块服务中引入spring-security权限认证模块

1.3.1 service_acl引入spring-security

1.3.2 在service_acl编写查询数据库信息

 定义userDetailServiceImpl 查询用户信息的实现类

1.4 springsecurity的配置文件

1.Spring Security的核心配置就是继承WebSecurityConfigurerAdapter并注解@EnableWebSecurity的配置。

这个配置指明了用户名密码的处理方式、请求路径的开合、登录登出控制等和安全相关的配置

2.配置一些放行的请求

3.代码

package com.atguigu.serurity.config;import com.atguigu.serurity.filter.TokenAuthenticationFilter;
import com.atguigu.serurity.filter.TokenLoginFilter;
import com.atguigu.serurity.security.DefaultPasswordEncoder;
import com.atguigu.serurity.security.TokenLogoutHandler;
import com.atguigu.serurity.security.TokenManager;
import com.atguigu.serurity.security.UnauthorizedEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;/*** <p>* Security配置类* </p>** @author qy* @since 2019-11-18*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {private UserDetailsService userDetailsService;private TokenManager tokenManager;private DefaultPasswordEncoder defaultPasswordEncoder;private RedisTemplate redisTemplate;@Autowiredpublic TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,TokenManager tokenManager, RedisTemplate redisTemplate) {this.userDetailsService = userDetailsService;this.defaultPasswordEncoder = defaultPasswordEncoder;this.tokenManager = tokenManager;this.redisTemplate = redisTemplate;}/*** 配置设置* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint()).and().csrf().disable().authorizeRequests().anyRequest().authenticated().and().logout().logoutUrl("/admin/acl/index/logout").addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and().addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate)).addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();}/*** 密码处理* @param auth* @throws Exception*/@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);}/*** 配置哪些请求不拦截* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {
//        web.ignoring().antMatchers("/api/**",
//                "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"
//               );web.ignoring().antMatchers("/*/**");}
}

 1.5 认证授权工具类

1.加密解密工具类

@Component
public class DefaultPasswordEncoder implements PasswordEncoder {public DefaultPasswordEncoder() {this(-1);}/*** @param strength*            the log rounds to use, between 4 and 31*/public DefaultPasswordEncoder(int strength) {}public String encode(CharSequence rawPassword) {return MD5.encrypt(rawPassword.toString());}public boolean matches(CharSequence rawPassword, String encodedPassword) {return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));}
}

2.token加密工具

@Component
public class TokenManager {private long tokenExpiration = 24*60*60*1000;private String tokenSignKey = "123456";public String createToken(String username) {String token = Jwts.builder().setSubject(username).setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)).signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();return token;}public String getUserFromToken(String token) {String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();return user;}public void removeToken(String token) {//jwttoken无需删除,客户端扔掉即可。}}

3.TokenLogoutHandler:退出实现

package com.atguigu.serurity.security;import com.atguigu.commonutils.R;
import com.atguigu.commonutils.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** <p>* 登出业务逻辑类* </p>** @author qy* @since 2019-11-08*/
public class TokenLogoutHandler implements LogoutHandler {private TokenManager tokenManager;private RedisTemplate redisTemplate;public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {this.tokenManager = tokenManager;this.redisTemplate = redisTemplate;}@Overridepublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {String token = request.getHeader("token");if (token != null) {tokenManager.removeToken(token);//清空当前用户缓存中的权限数据String userName = tokenManager.getUserFromToken(token);redisTemplate.delete(userName);}ResponseUtil.out(response, R.ok());}}

4.异常管理:未授权统一处理

public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {ResponseUtil.out(response, R.error());}
}

 1.6 创建认证授权实体类

1.user

@Data
@ApiModel(description = "用户实体类")
public class User implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "微信openid")private String username;@ApiModelProperty(value = "密码")private String password;@ApiModelProperty(value = "昵称")private String nickName;@ApiModelProperty(value = "用户头像")private String salt;@ApiModelProperty(value = "用户签名")private String token;}

2.securityUser

@Data
@Slf4j
public class SecurityUser implements UserDetails {//当前登录用户private transient User currentUserInfo;//当前权限private List<String> permissionValueList;public SecurityUser() {}public SecurityUser(User user) {if (user != null) {this.currentUserInfo = user;}}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {Collection<GrantedAuthority> authorities = new ArrayList<>();for(String permissionValue : permissionValueList) {if(StringUtils.isEmpty(permissionValue)) continue;SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);authorities.add(authority);}return authorities;}@Overridepublic String getPassword() {return currentUserInfo.getPassword();}@Overridepublic String getUsername() {return currentUserInfo.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

 1.7 认证授权的过滤器

1.7.1 认证TokenLoginFilter:认证的filter

public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;private TokenManager tokenManager;private RedisTemplate redisTemplate;public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {this.authenticationManager = authenticationManager;this.tokenManager = tokenManager;this.redisTemplate = redisTemplate;this.setPostOnly(false);this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)throws AuthenticationException {try {User user = new ObjectMapper().readValue(req.getInputStream(), User.class);return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));} catch (IOException e) {throw new RuntimeException(e);}}/*** 登录成功* @param req* @param res* @param chain* @param auth* @throws IOException* @throws ServletException*/@Overrideprotected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,Authentication auth) throws IOException, ServletException {SecurityUser user = (SecurityUser) auth.getPrincipal();String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());ResponseUtil.out(res, R.ok().data("token", token));}/*** 登录失败* @param request* @param response* @param e* @throws IOException* @throws ServletException*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,AuthenticationException e) throws IOException, ServletException {ResponseUtil.out(response, R.error());}
}

1.7.2  授权TokenAuthenticationFilter

package com.atguigu.serurity.filter;import com.atguigu.commonutils.R;
import com.atguigu.commonutils.ResponseUtil;
import com.atguigu.serurity.security.TokenManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** <p>* 访问过滤器* </p>** @author qy* @since 2019-11-08*/
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {private TokenManager tokenManager;private RedisTemplate redisTemplate;public TokenAuthenticationFilter(AuthenticationManager authManager, TokenManager tokenManager,RedisTemplate redisTemplate) {super(authManager);this.tokenManager = tokenManager;this.redisTemplate = redisTemplate;}@Overrideprotected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)throws IOException, ServletException {logger.info("================="+req.getRequestURI());if(req.getRequestURI().indexOf("admin") == -1) {chain.doFilter(req, res);return;}UsernamePasswordAuthenticationToken authentication = null;try {authentication = getAuthentication(req);} catch (Exception e) {ResponseUtil.out(res, R.error());}if (authentication != null) {SecurityContextHolder.getContext().setAuthentication(authentication);} else {ResponseUtil.out(res, R.error());}chain.doFilter(req, res);}private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {// token置于header里String token = request.getHeader("token");if (token != null && !"".equals(token.trim())) {String userName = tokenManager.getUserFromToken(token);List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);Collection<GrantedAuthority> authorities = new ArrayList<>();for(String permissionValue : permissionValueList) {if(StringUtils.isEmpty(permissionValue)) continue;SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);authorities.add(authority);}if (!StringUtils.isEmpty(userName)) {return new UsernamePasswordAuthenticationToken(userName, token, authorities);}return null;}return null;}
}

二   前端整合

2.1 element-ui的替换

将修改好的element-ui替换现在工程中的element-ui文件夹。先删除旧的,再复制新的element-ui文件夹内容。

2.2 其他文件替换

2.2.1 login.js替换和acl文件夹

将图中右边修改好的,acl文件目录和login.js复制到左边,进行替换。

login.js的内容

2.2.2 router的替换

右边写好的router.js替换左边的

router.js的内容

2.2.3 store的替换 

 2.3 安装依赖

2.4 需要修改的配置的地方

1.router.js:修改router文件夹里面index.js里面路径和vue文件地址

2. 修改数据库菜单表路径和页面地址

2.5  修改前端项目请求地址是网关地址

三   启动

3.1 前端启动

启动命令: npm run  dev

3.2 后端启动

1.nacos  2.redis

3.acl服务,和网关服务

springsecurity执行流程*

4.1 步骤

4.1.1.输入用户名和密码

4.2.2.请求执行到TokenLoginFilter的attemptAuthentication方法

在这个方法中获取用户名和密码

4.3.3. 查询用户信息:跳转到userDetailServiceImpl的loadUserByUserName方法。

主要是通过用户名,查询用户信息和权限信息,封装到springsecurity对象中进行返回。

4.3.4.认证成功后,再次跳转到Tolenloginfilter类中的sucessfulAuthentication()方法,如果失败跳转到unsuccessfulAuthentication方法

获取返回对象;

根据对象里面用户名生成token;

把用户和权限信息放到redis

返回生成的token

 4.3.5 请求到达授权过滤器

请求到达授权过滤器TokenAuthenticationFilter的getAuthentication方法。

从header中获取token信息,从token中获取用户名,根据用户名从redis中获取该用的权限信息。

相关文章:

实战项目 在线学院之集成springsecurity的配置以及执行流程

一 后端操作配置 1.0 工程结构 1.1 在common下创建spring_security模块 1.2 pom文件中依赖的注入 1.3 在service_acl模块服务中引入spring-security权限认证模块 1.3.1 service_acl引入spring-security 1.3.2 在service_acl编写查询数据库信息 定义userDetailServiceImpl 查…...

【ARM CoreLink CCI-400 控制器简介】

文章目录 CCI-400 介绍 CCI-400 介绍 CCI&#xff08;Cache Coherent Interconnect&#xff09;是ARM 中 的Cache一致性控制器。 CCI-400 将 Interconnect 和coherency 功能结合到一个模块中。它支持多达两个ACE master 点的interface&#xff0c;例如&#xff1a; Cortex-A…...

Linux xargs命令继续学习

之前学习过Linux xargs&#xff0c;对此非常的不熟悉&#xff0c;下面继续学习一下&#xff1b; xargs 可以将管道或标准输入&#xff08;stdin&#xff09;数据转换成命令行参数&#xff0c;也能够从文件的输出中读取数据&#xff1b; xargs也可以给命令传递参数&#xff1b;…...

【广州华锐互动】数字孪生智慧楼宇3D可视化系统:掌握实时运行状态,优化运营管理

在过去的几年中&#xff0c;科技的发展极大地改变了我们的生活和工作方式。其中&#xff0c;三维数据可视化技术的出现&#xff0c;为我们提供了全新的理解和观察世界的方式。特别是在建筑行业&#xff0c;数字孪生智慧楼宇3D可视化系统的出现&#xff0c;让我们有机会重新定义…...

20230904工作心得:集合应该如何优雅判空?

1 集合判空 List<String> newlist null;//空指针if( !newlist.isEmpty()){newlist.forEach(System.out::println);}//空指针if(newlist.size()>0 && newlist!null){newlist.forEach(System.out::println);}//可行if(newlist!null && newlist.size()&…...

使用Python进行健身手表数据分析

健身手表(Fitness Watch)数据分析涉及分析健身可穿戴设备或智能手表收集的数据&#xff0c;以深入了解用户的健康和活动模式。这些设备可以跟踪所走的步数、消耗的能量、步行速度等指标。本文将带您完成使用Python进行Fitness Watch数据分析的任务。 Fitness Watch数据分析是健…...

什么是malloxx勒索病毒,服务器中malloxx勒索病毒了怎么办?

Malloxx勒索病毒是一种新型的电脑病毒&#xff0c;它通过加密用户电脑中的重要文件数据来威胁用户&#xff0c;并以此勒索钱财。这种病毒并不是让用户的电脑瘫痪&#xff0c;而是以非常独特的方式进行攻击。在感染了Malloxx勒索病毒后&#xff0c;它会加密用户服务器中的数据&a…...

CocosCreator3.8研究笔记(六)CocosCreator 脚本装饰器的理解

一、什么是装饰器&#xff1f; 装饰器是TypeScript脚本语言中的概念。 TypeScript的解释&#xff1a;在一些场景下&#xff0c;我们需要额外的特性来支持标注或修改类及其成员。装饰器&#xff08;Decorators&#xff09;为我们在类的声明及成员上通过元编程语法添加标注提供了…...

docker login harbor http login登录

前言 搭建的 harbor 仓库为 http 协议&#xff0c;在本地登录时出现如下报错&#xff1a; docker login http://192.168.xx.xx Username: admin Password: Error response from daemon: Get "https://192.168.xx.xx/v2/": dialing 192.168.xx.xx:443 matches static …...

day5 qt

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);timer_idthis->startTimer(100);//啓動一個定時器 每100ms發送一次信號ui->Edit1->setPlaceholderTex…...

【80天学习完《深入理解计算机系统》】第十三天 3.7 缓冲区溢出 attack lab

3.7 缓冲区溢出 && attack lab...

Hadoop生态之hive

一 概述与特点 之所以把Hive放在Hadoop生态里面去写,是因为它本身依赖Hadoop。Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类 SQL 查询功能。 其本质是将 SQL 转换为 MapReduce/Spark 的任务进行运算,底层由 HDFS 来提供…...

AWS DynamoDB浅析

AWS DynamoDB是一个NOSQL数据库。 可以通过IAM直接控制权限&#xff0c;和AWS其他服务连用非常方便。 DynamoDB的几个概念 Partition Key&#xff1a;分区键。如果没有Sort key&#xff0c;那么Partition Key必须唯一&#xff0c;如有Sort key&#xff0c;Partition Key可以重…...

Linux安装ffmpeg

1 下载yasm wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz tar -zxvf yasm-1.3.0.tar.gz cd yasm-1.3.0 ./configure make && make install2 下载ffmpeg wget http://ffmpeg.org/releases/ffmpeg-3.1.3.tar.bz2 tar jxvf ffmpeg-3.1.3.tar.…...

(18)不重启服务动态停止、启动RabbitMQ消费者

我们在消费RabbitMQ消息的过程中&#xff0c;有时候可能会想先暂停消费一段时间&#xff0c;然后过段时间再启动消费者&#xff0c;这个需求怎么实现呢&#xff1f;我们可以借助RabbitListenerEndpointRegistry这个类来实现&#xff0c;它的全类名是org.springframework.amqp.r…...

数据仓库的流程

数据仓库完全用统计分析框架实现:Spark,MR 但是因为实际生产环境中,需求量非常大, 如果每个需求都采用独立c代码开发方式,重复计算会很多. 提高性能的方法: 1.减少数据量 2. 减少重复计算 例如RDD cache 可以减少重复计算,但是不安全,都在缓存中, persist 都放内存中,但是慢 而…...

MyBatis-Plus深入 —— 条件构造器与插件管理

前言 在前面的文章中&#xff0c;荔枝梳理了一个MyBatis-Plus的基本使用、配置和通用Service接口&#xff0c;我们发现在MyBatis-Plus的辅助增强下我们不再需要通过配置xml文件中的sql语句来实现基本的sql操作了&#xff0c;不愧是最佳搭档&#xff01;在这篇文章中&#xff0c…...

C语言结构体的初始化方式

逐个初始化字段&#xff1a;这是最直接的方式&#xff0c;你可以逐个为结构体的每个字段进行初始化。 struct Student { char name[50]; int age; float marks; }; struct Student student1 {"Alice", 20, 89.5}; 2.使用结构体字面值初始化&#xff1a;这种方…...

Vue生成多文件pdf准考证

这是渲染的数据 这是生成的pdf文件&#xff0c;直接可以打印 需要安装和npm依赖和引入封装的pdf.js文件 npm install --save html2canvas // 页面转图片 npm install jspdf --save // 图片转pdfpdf.js文件 import html2canvas from "html2canvas"; import jsPDF …...

Rust的derive思考

这几天在Yew的学习实践中&#xff0c;发现derive中的参数中包含了yew自己的东西&#xff0c;比如yew::Properties。习惯使用#[derive(Clone, Debug, PartialEq)]之后&#xff0c;发现还有新的成员&#xff0c;这让我好奇起来。 首先让我们来回顾一下derive是什么。 #[derive(…...

OpenClaw+千问3.5-9B数据清洗:Excel复杂表格自动化处理

OpenClaw千问3.5-9B数据清洗&#xff1a;Excel复杂表格自动化处理 1. 为什么需要自动化Excel处理 每次面对上百行的Excel表格时&#xff0c;我总会在合并单元格和异常值上浪费大量时间。作为数据分析师&#xff0c;最痛苦的莫过于收到业务部门发来的"美化版"报表—…...

本地AI模型开发终极指南:从零开始构建智能应用社区

本地AI模型开发终极指南&#xff1a;从零开始构建智能应用社区 【免费下载链接】gallery A gallery that showcases on-device ML/GenAI use cases and allows people to try and use models locally. 项目地址: https://gitcode.com/GitHub_Trending/gallery44/gallery …...

5分钟掌握Scala.js构建工具链:从开发到生产的完整指南

5分钟掌握Scala.js构建工具链&#xff1a;从开发到生产的完整指南 【免费下载链接】scala-js Scala.js, the Scala to JavaScript compiler 项目地址: https://gitcode.com/gh_mirrors/sc/scala-js Scala.js是一个功能强大的Scala到JavaScript编译器&#xff0c;它允许开…...

OpenClaw语音交互方案:千问3.5-27B对接Whisper实现听写

OpenClaw语音交互方案&#xff1a;千问3.5-27B对接Whisper实现听写 1. 为什么需要语音交互自动化 上个月帮朋友整理一场3小时的行业访谈录音时&#xff0c;我对着逐字稿反复暂停播放、标记重点、提炼观点&#xff0c;整整花了6小时才完成笔记。这种机械劳动让我开始思考&…...

为什么80%AI率降完后还有残留?根本原因在这

你用了降AI工具&#xff0c;处理前90%&#xff0c;处理后17%。 17%达标了&#xff0c;但还是有AI率残留。或者更糟&#xff0c;处理后22%&#xff0c;还是没过。 为什么工具处理后AI率不能降到0%&#xff1f;残留从哪来&#xff1f;怎么进一步处理&#xff1f; 残留AI率的三…...

STM32单片机技术解析与应用实践

1. STM32的崛起背景与市场格局演变在2007年之前&#xff0c;单片机市场呈现"三足鼎立"的格局&#xff1a;以STC为代表的51系列8位机占据教学和工业控制领域&#xff0c;MSP430在低功耗应用场景表现突出&#xff0c;AVR和PIC则在特定细分市场各有所长。这些8位和16位单…...

OpenClaw v2026.3.31 深度解读:为什么这次更新不是“小修小补”,而是一次明显的安全收口与后台任务体系成形

&#x1f525;个人主页&#xff1a;杨利杰YJlio❄️个人专栏&#xff1a;《Sysinternals实战教程》《Windows PowerShell 实战》《WINDOWS教程》《IOS教程》《微信助手》《锤子助手》 《Python》 《Kali Linux》《那些年未解决的Windows疑难杂症》&#x1f31f; 让复杂的事情更…...

AI时代程序员必看!揭秘Harness Engineerin

当AI智能体开始批量编写代码&#xff0c;程序员会失业吗&#xff1f;OpenAI的一个实验给出了惊人答案&#xff1a;在一次实验中&#xff0c;3名工程师配合1500个AI智能体&#xff0c;竟在5个月内完成了100万行代码的产品开发——人类一行代码都没写&#xff01;但背后真正的秘密…...

入门首选:8bit逐次逼近型SAR ADC电路设计成品,基于SMIC 0.18工艺,3.3V供...

8bit逐次逼近型SAR ADC电路设计成品 入门时期的第三款sarADC&#xff0c;适合新手学习等。 包括电路文件和详细设计文档。 smic0.18工艺&#xff0c;单端结构&#xff0c;3.3V供电。 整体采样率500k&#xff0c;可实现基本的模数转换&#xff0c;未做动态仿真&#xff0c;文档内…...

Excel处理地理数据进阶:除了度分秒转换,这些隐藏技巧让你效率翻倍

Excel地理数据处理进阶&#xff1a;从度分秒转换到地图可视化的全流程实战 当你面对一份包含数百条经纬度数据的地理信息表格时&#xff0c;单纯掌握度分秒转换公式远远不够。真正的高效工作流需要将数据清洗、格式转换、可视化呈现串联成自动化流程。本文将带你超越基础公式&a…...