电商项目part06 微服务网关整合OAuth2.0授权中心

微服务网关整合 OAuth2.0 思路分析

网关整合 OAuth2.0 有两种思路,一种是授权服务器生成令牌, 所有请求统一在网关层验证,判断权
限等操作;另一种是由各资源服务处理,网关只做请求转发。 比较常用的是第一种,把API网关作
为OAuth2.0的资源服务器角色,实现接入客户端权限拦截、令牌解析并转发当前登录用户信息给微
服务,这样下游微服务就不需要关心令牌格式解析以及OAuth2.0相关机制了。
网关在认证授权体系里主要负责两件事:
(1)作为OAuth2.0的资源服务器角色,实现接入方访问权限拦截。
(2)令牌解析并转发当前登录用户信息(明文token)给微服务

微服务拿到明文token(明文token中包含登录用户的身份和权限信息)后也需要做两件事:
(1)用户授权拦截(看当前用户是否有权访问该资源)
(2)将用户信息存储进当前线程上下文(有利于后续业务逻辑随时获取当前用户信息)

大致流程

在这里插入图片描述

授权中心

<!-- spring security oauth2--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency>

数据库 表

DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (`client_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`access_token_validity` int(11) NULL DEFAULT NULL,`refresh_token_validity` int(11) NULL DEFAULT NULL,`additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

在这里插入图片描述
在这里插入图片描述

实现非对称加密

第一步:生成jks 证书文件
我们使用jdk自动的工具生成
命令格式
keytool
-genkeypair 生成密钥对
-alias jwt(别名)
-keypass 123456(别名密码)
-keyalg RSA(生证书的算法名称,RSA是一种非对称加密算法)
-keysize 1024(密钥长度,证书大小)
-validity 365(证书有效期,天单位)
-keystore D:/jwt/jwt.jks(指定生成证书的位置和证书名称)
-storepass 123456(获取keystore信息的密码)
-storetype (指定密钥仓库类型)
使用 “keytool -help” 获取所有可用命令

keytool ‐genkeypair ‐alias jwt ‐keyalg RSA ‐keysize 2048 ‐keystore D:/jwt/jwt.jks

在这里插入图片描述
将生成的jwt.jks文件cope到授权服务器的resource目录下
在这里插入图片描述
查看公钥信息

keytool ‐list ‐rfc ‐‐keystore jwt.jks | openssl x509 ‐inform pem ‐pubkey

在这里插入图片描述

第二步:授权服务中增加jwt的属性配置类

/*** @vlog: 高于生活,源于生活* @desc: 类的描述: jwt 证书配置* @author: smlz* @version: 1.0*/
@Data
@ConfigurationProperties(prefix = "my.jwt")
public class JwtCAProperties {/*** 证书名称*/private String keyPairName;/*** 证书别名*/private String keyPairAlias;/*** 证书私钥*/private String keyPairSecret;/*** 证书存储密钥*/private String keyPairStoreSecret;
}

yml中添加jwt配置

tuling:jwt:keyPairName: jwt.jkskeyPairAlias: jwtkeyPairSecret: 123123keyPairStoreSecret: 123123

第三步:修改AuthServerConfig的配置,支持非对称加密

@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(value = JwtCAProperties.class)
public class MyAuthServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate DataSource dataSource;@Autowiredprivate MyUserDetailService myUserDetailService;//修改JwtTokenStoreConfig的配置,支持非对称加密@Autowiredprivate JwtCAProperties jwtCAProperties;/*** 方法实现说明:我们颁发的token通过jwt存储* @author:smlz* @return:* @exception:* @date:2020/1/21 21:49*/@Beanpublic TokenStore tokenStore(){return new JwtTokenStore(jwtAccessTokenConverter());}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();//jwt的密钥converter.setKeyPair(keyPair());return converter;}@Beanpublic KeyPair keyPair() {KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jwtCAProperties.getKeyPairName()), jwtCAProperties.getKeyPairSecret().toCharArray());return keyStoreKeyFactory.getKeyPair(jwtCAProperties.getKeyPairAlias(), jwtCAProperties.getKeyPairStoreSecret().toCharArray());}//扩展JWT中的存储内容@Beanpublic MyTokenEnhancer myTokenEnhancer() {return new MyTokenEnhancer();}/*** 方法实现说明:认证服务器能够给哪些 客户端颁发token  我们需要把客户端的配置 存储到* 数据库中 可以基于内存存储和db存储* @author:smlz* @return:* @exception:* @date:2020/1/15 20:18*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(clientDetails());}/*** 方法实现说明:用于查找我们第三方客户端的组件 主要用于查找 数据库表 oauth_client_details* @author:smlz* @return:* @exception:* @date:2020/1/15 20:19*/@Beanpublic ClientDetailsService clientDetails() {return new JdbcClientDetailsService(dataSource);}/*** 方法实现说明:授权服务器的配置的配置* @author:smlz* @return:* @exception:* @date:2020/1/15 20:21*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();tokenEnhancerChain.setTokenEnhancers(Arrays.asList(myTokenEnhancer(),jwtAccessTokenConverter()));endpoints.tokenStore(tokenStore()) //授权服务器颁发的token 怎么存储的.tokenEnhancer(tokenEnhancerChain).userDetailsService(myUserDetailService) //用户来获取token的时候需要 进行账号密码.authenticationManager(authenticationManager);}/*** 方法实现说明:授权服务器安全配置* @author:smlz* @return:* @exception:* @date:2020/1/15 20:23*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {//第三方客户端校验token需要带入 clientId 和clientSecret来校验security .checkTokenAccess("isAuthenticated()").tokenKeyAccess("isAuthenticated()");//来获取我们的tokenKey需要带入clientId,clientSecretsecurity.allowFormAuthenticationForClients();}

第四步:扩展JWT中的存储内容
MyTokenEnhancer

/*** @vlog: 高于生活,源于生活* @desc: 类的描述:jwt自定义增强器(根据自己的业务需求添加非敏感字段)* @author: yuyang* @version: 1.0*/
public class MyTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {MemberDetails memberDetails = (MemberDetails) authentication.getPrincipal();final Map<String, Object> additionalInfo = new HashMap<>();final Map<String, Object> retMap = new HashMap<>();//这里暴露memberId到Jwt的令牌中,后期可以根据自己的业务需要 进行添加字段additionalInfo.put("memberId",memberDetails.getUmsMember().getId());additionalInfo.put("nickName",memberDetails.getUmsMember().getNickname());additionalInfo.put("integration",memberDetails.getUmsMember().getIntegration());retMap.put("additionalInfo",additionalInfo);((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(retMap);return accessToken;}
}

配置SpringSecurity

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MyUserDetailService myUserDetailService;/*** 方法实现说明:用于构建用户认证组件,需要传递userDetailsService和密码加密器* @author:smlz* @param auth* @return:* @exception:* @date:2019/12/25 14:31*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(myUserDetailService).passwordEncoder(passwordEncoder());}/*** 设置前台静态资源不拦截* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**");}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 密码模式需要这个bean@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {// oauth2 密码模式需要拿到这个beanreturn super.authenticationManagerBean();}}

UserDetailService

@Slf4j
@Component
public class MyUserDetailService implements UserDetailsService {/*** 方法实现说明:用户登陆* @author:smlz* @param userName 用户名密码* @return: UserDetails* @exception:* @date:2020/1/21 21:30*/@Autowiredprivate MemberMapper memberMapper;@Overridepublic UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {if(StringUtils.isEmpty(userName)) {log.warn("用户登陆用户名为空:{}",userName);throw new UsernameNotFoundException("用户名不能为空");}Member member = getByUsername(userName);if(null == member ) {log.warn("根据用户名没有查询到对应的用户信息:{}",userName);}log.info("根据用户名:{}获取用户登陆信息:{}",userName,member );MemberDetails memberDetails = new MemberDetails(member );return memberDetails;}/*** 方法实现说明:根据用户名获取用户信息* @author:smlz* @param username:用户名* @return: UmsMember 会员对象* @exception:* @date:2020/1/21 21:34*/public Member getByUsername(String username) {MemberExample example = new MemberExample();example.createCriteria().andUsernameEqualTo(username);List<Member> memberList = memberMapper.selectByExample(example);if (!CollectionUtils.isEmpty(memberList)) {return memberList.get(0);}return null;}
}

UserDetial类

public class MemberDetails implements UserDetails {private UmsMember umsMember;public MemberDetails(UmsMember umsMember) {this.umsMember = umsMember;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {//返回当前用户的权限return Arrays.asList(new SimpleGrantedAuthority("TEST"));}@Overridepublic String getPassword() {return umsMember.getPassword();}@Overridepublic String getUsername() {return umsMember.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return umsMember.getStatus()==1;}public UmsMember getUmsMember() {return umsMember;}
}

接入网关gateway

认证过滤器AuthenticationFilter#filter中需要实现的逻辑

//1.过滤不需要认证的url,比如/oauth/**
//2. 获取token
//从请求头中解析 Authorization value: bearer xxxxxxx
//或者从请求参数中解析 access_token
//3. 校验token
// 拿到token后,通过公钥(需要从授权服务获取公钥)校验
// 校验失败或超时抛出异常
//4. 校验通过后,从token中获取的用户登录信息存储到请求头中

JWT依赖

<!‐‐添加jwt相关的包‐‐>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt‐api</artifactId><version>0.10.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt‐impl</artifactId><version>0.10.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt‐jackson</artifactId><version>0.10.5</version><scope>runtime</scope>
</dependency>

编写GateWay的全局过滤器进行权限的校验拦截

@Component
@Slf4j
@EnableConfigurationProperties(value = NotAuthUrlProperties.class)
public class AuthorizationFilter implements GlobalFilter,Ordered,InitializingBean {@Autowiredprivate RestTemplate restTemplate;/*** 请求各个微服务 不需要用户认证的URL*/@Autowiredprivate NotAuthUrlProperties notAuthUrlProperties;/*** jwt的公钥,需要网关启动,远程调用认证中心去获取公钥*/private PublicKey publicKey;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String currentUrl = exchange.getRequest().getURI().getPath();//1:不需要认证的urlif(shouldSkip(currentUrl)) {//log.info("跳过认证的URL:{}",currentUrl);return chain.filter(exchange);}//log.info("需要认证的URL:{}",currentUrl);//第一步:解析出我们Authorization的请求头  value为: “bearer XXXXXXXXXXXXXX”String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");//第二步:解析请求,获取token,判断Authorization的请求头是否为空if(StringUtils.isEmpty(authHeader)) {log.warn("需要认证的url,请求头为空");throw new GateWayException(ResultCode.AUTHORIZATION_HEADER_IS_EMPTY);}//第三步 校验我们的jwt 若jwt不对或者超时都会抛出异常Claims claims = JwtUtils.validateJwtToken(authHeader,publicKey);//第四步 把从jwt中解析出来的 用户登陆信息存储到请求头中ServerWebExchange webExchange = wrapHeader(exchange,claims);return chain.filter(webExchange);}/*** 方法实现说明:把我们从jwt解析出来的用户信息存储到请求中* @author:smlz* @param serverWebExchange* @param claims* @return: ServerWebExchange* @exception:* @date:2020/1/22 12:12*/private ServerWebExchange wrapHeader(ServerWebExchange serverWebExchange,Claims claims) {String loginUserInfo = JSON.toJSONString(claims);//log.info("jwt的用户信息:{}",loginUserInfo);String memberId = claims.get("additionalInfo",Map.class).get("memberId").toString();String nickName = claims.get("additionalInfo",Map.class).get("nickName").toString();//向headers中放文件,记得buildServerHttpRequest request = serverWebExchange.getRequest().mutate().header("username",claims.get("user_name",String.class)).header("memberId",memberId).header("nickName",nickName).build();//将现在的request 变成 change对象return serverWebExchange.mutate().request(request).build();}/*** 方法实现说明:不需要授权的路径* @author:smlz* @param currentUrl 当前请求路径* @return:* @exception:* @date:2019/12/26 13:49*/private boolean shouldSkip(String currentUrl) {//路径匹配器(简介SpringMvc拦截器的匹配器)//比如/oauth/** 可以匹配/oauth/token    /oauth/check_token等PathMatcher pathMatcher = new AntPathMatcher();for(String skipPath:notAuthUrlProperties.getShouldSkipUrls()) {if(pathMatcher.match(skipPath,currentUrl)) {return true;}}return false;}@Overridepublic int getOrder() {return 0;}/*** 方法实现说明:网关服务启动 生成公钥* @author:smlz* @return:* @exception:* @date:2020/1/22 11:58*/@Overridepublic void afterPropertiesSet() throws Exception {//初始化公钥this.publicKey = JwtUtils.genPulicKey(restTemplate);}}

设置非拦截路径

/**
* @vlog: 高于生活,源于生活
* @desc: 类的描述:网关跳过认证的配置类
* @author: smlz
* @createDate: 2020/1/22 10:56
* @version: 1.0
*/
@Data
@ConfigurationProperties("my.gateway")
public class NotAuthUrlProperties {private LinkedHashSet<String> shouldSkipUrls;
}//application.yml
my:gateway:shouldSkipUrls:- /oauth/**- /sso/**- /home/**- /portal/commentlist/**- /order/paySuccess/**- /pms/**- /static/qrcode/**

校验token逻辑

@Slf4j
public class JwtUtils {/*** 认证服务器许可我们的网关的clientId(需要在oauth_client_details表中配置)*/private static final String CLIENT_ID = "api-gateway";/*** 认证服务器许可我们的网关的client_secret(需要在oauth_client_details表中配置)*/private static final String CLIENT_SECRET = "mall";/*** 认证服务器暴露的获取token_key的地址*/private static final String AUTH_TOKEN_KEY_URL = "http://mall-authcenter/oauth/token_key";/*** 请求头中的 token的开始*/private static final String AUTH_HEADER = "bearer ";/*** 方法实现说明: 通过远程调用获取认证服务器颁发jwt的解析的key* @author:smlz* @param restTemplate 远程调用的操作类* @return: tokenKey 解析jwt的tokenKey* @exception:* @date:2020/1/22 11:31*/private static String getTokenKeyByRemoteCall(RestTemplate restTemplate) throws GateWayException {//第一步:封装请求头HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);headers.setBasicAuth(CLIENT_ID,CLIENT_SECRET);HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(null, headers);//第二步:远程调用获取token_keytry {ResponseEntity<Map> response = restTemplate.exchange(AUTH_TOKEN_KEY_URL, HttpMethod.GET, entity, Map.class);String tokenKey = response.getBody().get("value").toString();log.info("去认证服务器获取Token_Key:{}",tokenKey);return tokenKey;}catch (Exception e) {log.error("远程调用认证服务器获取Token_Key失败:{}",e.getMessage());throw new GateWayException(ResultCode.GET_TOKEN_KEY_ERROR);}}/*** 方法实现说明:生成公钥* @author:smlz* @param restTemplate:远程调用操作类* @return: PublicKey 公钥对象* @exception:* @date:2020/1/22 11:52*/public static PublicKey genPulicKey(RestTemplate restTemplate) throws GateWayException {String tokenKey = getTokenKeyByRemoteCall(restTemplate);try{//把获取的公钥开头和结尾替换掉String dealTokenKey =tokenKey.replaceAll("\\-*BEGIN PUBLIC KEY\\-*", "").replaceAll("\\-*END PUBLIC KEY\\-*", "").trim();java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(dealTokenKey));KeyFactory keyFactory = KeyFactory.getInstance("RSA");PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);log.info("生成公钥:{}",publicKey);return publicKey;}catch (Exception e) {log.info("生成公钥异常:{}",e.getMessage());throw new GateWayException(ResultCode.GEN_PUBLIC_KEY_ERROR);}}public static Claims validateJwtToken(String authHeader,PublicKey publicKey) {String token =null ;try{token = StringUtils.substringAfter(authHeader, AUTH_HEADER);Jwt<JwsHeader, Claims> parseClaimsJwt = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);Claims claims = parseClaimsJwt.getBody();//log.info("claims:{}",claims);return claims;}catch(Exception e){log.error("校验token异常:{},异常信息:{}",token,e.getMessage());throw new GateWayException(ResultCode.JWT_TOKEN_EXPIRE);}}
}

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

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

相关文章

Python语言实现React框架

迷途小书童的 Note 读完需要 6分钟 速读仅需 2 分钟 1 reactpy 介绍 reactpy 是一个用 Python 语言实现的 ReactJS 框架。它可以让我们使用 Python 的方式来编写 React 的组件&#xff0c;构建用户界面。 reactpy 的目标是想要将 React 的优秀特性带入 Python 领域&#xff0c;…

HTML总结1【转】

以下内容转载和参考自&#xff1a;w3school的HTML学习内容&#xff0c;HTML 简介 。 一、概述 HTML不是一种编程语言&#xff0c;它是超文本标记语言 (Hyper Text Markup Language)&#xff0c;使用标记标签来描述网页内容。HTML标签是由尖括号包围的关键词&#xff0c;标签通…

机器学习笔记:KD树

1 引入原因 K近邻算法需要在整个数据集中搜索和测试数据x最近的k个点&#xff0c;如果一一计算&#xff0c;然后再排序&#xff0c;开销过大 引入KD树的作用就是对KNN搜索和排序的耗时进行改进 2 KD树 2.1 主体思路 以空间换时间&#xff0c;利用训练样本集中的样本点&…

HTTPS协议加密原理

目录 一、什么是HTTPS 二、什么是加密/解密 三、为什么要加密 四、常见的加密方式 1.对称加密 2. 非对称加密 五、HTTPS加密方式探讨 1.只使用对称加密 2.只使用非对称加密 3.非对称加密对称加密 4.非对称加密对称加密CA认证 六、总结 一、什么是HTTPS HTTP 协议&a…

Pytorch06-复杂模型构建

https://github.com/ExpressGit/Pytorch_Study_Demo 1、PyTorch 复杂模型构建 1、模型截图2、模型部件实现3、模型组装 2、模型定义 2.1、Sequential 1、当模型的前向计算为简单串联各个层的计算时&#xff0c; Sequential 类可以通过更加简单的方式定义模型。2、可以接收…

HTML5岗位技能实训室建设方案

一 、系统概述 HTML5岗位技能技术是计算机类专业重要的核心课程&#xff0c;课程所包含的教学内容多&#xff0c;实践性强&#xff0c;并且相关技术更新快。传统的课堂讲授模式以教师为中心&#xff0c;学生被动式接收&#xff0c;难以调动学生学习的积极性和主动性。混合式教学…

Linux--进程地址空间

1.线程地址空间 所谓进程地址空间&#xff08;process address space&#xff09;&#xff0c;就是从进程的视角看到的地址空间&#xff0c;是进程运行时所用到的虚拟地址的集合。 简单地说&#xff0c;进程就是内核数据结构和代码和本身的代码和数据&#xff0c;进程本身不能…

【Linux】socket 编程基础

文章目录 &#x1f4d5; 网络间的通信&#x1f4d5; socket 是什么1. socket 套接字2. 套接字描述符3. 基本的 socket 接口函数3.1 头文件3.2 socket() 函数3.3 bind() 函数struct sockaddr主机序列与网络序列 3.4 listen() 函数3.5 connect() 函数3.6 accept() 函数IP 地址风格…

3.BGP状态机和路由注入方式

BGP状态机 BGP路由的生成 不同于IGP路由协议,BGP自身并不会发现并计算产生路由,BGP将GP路由表中的路由注入到BGP路由表中,并通过Update报文传递给BGP对等体。 BGP注入路由的方式有两种: Networkimport-route与IGP协议相同,BGP支持根据已有的路由条目进行聚合,生成聚合路由…

Java 时间日期处理,工作必用(建议收藏)

工作中经常会遇到对时间日期进行处理的业务&#xff0c;像日期类的API个人觉得不需要背&#xff0c;需要的时候去查资料就行。我整理了Java8之前及之后日期类常用的时间日期处理方法&#xff0c;方便工作需要时查找&#xff0c;觉得有用的朋友可以收藏。 一、日期格式化和解析 …

linux挂载内网镜像源文件,支持yum安装

cd /etc/yum.repos.d/vim kylin_aarch64.repo 重建yum缓存 yum clean allyum makecacheyum repolist

RH1288V3 - 初识物理服务器

如果你拥有一台物理服务器(不是云服务器) 个人比较推荐你用物理服务器&#xff0c;虽然性能会比云要来的差&#xff0c;但是不用每月交钱上。云服务固然方便&#xff0c;但是几个核的性能和一点存储&#xff0c;想做一个动漫网站固然要很多mp4这种影视资源&#xff0c;云服务器…

积跬步至千里 || 数学基础、算法与编程

数学基础、算法与编程 1. BAP 技能 BAP 技能是指基础(Basic)、算法(Algorithm)和编程(Programm)三种基本技能的深度融合。理工科以数学、算法与编程为根基&#xff0c;这三个相辅相成又各有区别。 &#xff08;1&#xff09;数学以线性代数为主要研究工具和部分微积分技术为手…

最新绕过目标域名CDN进行信息收集技术

绕过目标域名CDN进行信息收集 1&#xff0e;CDN简介及工作流程 CDN&#xff08;Content Delivery Network&#xff0c;内容分发网络&#xff09;的目的是通过在现有的网络架构中增加一层新的Cache&#xff08;缓存&#xff09;层&#xff0c;将网站的内容发布到最接近用户的网…

打开软件提示msvcp140.dll丢失的解决方法,msvcp140主要丢失原因

今天&#xff0c;我将为大家介绍一种非常常见的问题——msvcp140.dll丢失。这个问题可能会导致许多应用程序无法正常运行&#xff0c;甚至崩溃。但是&#xff0c;请不要担心&#xff0c;我会为大家提供5种解决方法&#xff0c;帮助大家轻松解决问题。 首先&#xff0c;我们来看…

单片机学习-蜂鸣器电子元件

蜂鸣器是有什么作用的&#xff1f; 蜂鸣器 是 一种 一体化结构 的电子训响器&#xff0c;可以发出声音的电子元器件 蜂鸣器分类&#xff1f; ①压电式蜂鸣器&#xff08;图左&#xff09; 称&#xff1a; 无源蜂鸣器 ②电磁式蜂鸣器&#xff08;图右&#xff09; 称&#xf…

陪诊系统源码开发:实现个性化医疗陪护的创新之路

陪诊系统的源码开发在现代医疗中具有重要意义。本文将通过代码示例介绍陪诊系统的源码开发&#xff0c;展示如何实现个性化医疗陪护的创新方案。 1. 安装和环境设置&#xff1a; 首先&#xff0c;确保你的开发环境中已经安装了合适的编程语言和框架&#xff0c;比如Python和…

人力资源小程序的设计原则与实现方法

随着移动互联网的快速发展&#xff0c;小程序成为了各行各业推广和服务的新利器。对于人力资源行业来说&#xff0c;开发一款定制化的小程序不仅可以提升服务效率&#xff0c;还可以增强品牌形象和用户粘性。那么&#xff0c;如何定制开发人力资源类的小程序呢&#xff1f;下面…

使用Miniconda

Conda是一个开源的包和环境管理器&#xff0c;使用它可以在同一台机器上安装不同版本的Python软件包和依赖了。Anaconda和Miniconda都集成了Conda,Anaconda包括更多的工具包&#xff0c;Miniconda则只包括Conda和Python。 在很奇葩的Deepin下Miniconda安装之旅 中&#xff0c;…

8路光栅尺磁栅尺编码器或16路高速DI脉冲信号转Modbus TCP网络模块 YL99-RJ45

特点&#xff1a; ● 光栅尺磁栅尺解码转换成标准Modbus TCP协议 ● 高速光栅尺磁栅尺4倍频计数&#xff0c;频率可达5MHz ● 模块可以输出5V的电源给光栅尺或传感器供电 ● 支持8个光栅尺同时计数&#xff0c;可识别正反转 ● 可以设置作为16路独立DI高速计数器 ● 可网…