Gateway+Springsecurity+OAuth2.0+JWT 实现分布式统一认证授权!

目录

1. OAuth2.0授权服务

2. 资源服务

3. Gateway网关

4. 测试


 

在SpringSecurity+OAuth2.0 搭建认证中心和资源服务中心-CSDN博客 ​​​​​​

基础上整合网关和JWT实现分布式统一认证授权。

 

大致流程如下:

1、客户端发出请求给网关获取令牌

2、网关收到请求,直接转发给授权服务

3、授权服务验证用户名、密码等一系列身份,通过则颁发令牌给客户端

4、客户端携带令牌请求资源,请求直接到了网关层

5、网关对令牌进行校验(验签、过期时间校验....)、鉴权(对当前令牌携带的权限)和访问资源所需的权限进行比对,如果权限有交集则通过校验,直接转发给微服务

6、微服务进行逻辑处理

1. OAuth2.0授权服务

导入依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.2</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-resource-server</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency></dependencies>

application.yaml

server:port: 8080
spring:application:name: oauth2-cloud-auth-servercloud:nacos:## 注册中心配置discovery:# nacos的服务地址,nacos-server中IP地址:端口号server-addr: 127.0.0.1:8848datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/rbac?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 123456

 这里展示部分代码

AccessTokenConfig类

/*** 令牌的配置*/
@Configuration
public class AccessTokenConfig {/*** JWT的秘钥* TODO 实际项目中需要统一配置到配置文件中,资源服务也需要用到*/private final static String SIGN_KEY="jwt";/*** 令牌的存储策略*/@Beanpublic TokenStore tokenStore() {//使用JwtTokenStore生成JWT令牌return new JwtTokenStore(jwtAccessTokenConverter());}/*** JwtAccessTokenConverter* TokenEnhancer的子类,在JWT编码的令牌值和OAuth身份验证信息之间进行转换。* TODO:后期可以使用非对称加密*/@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter converter = new JwtAccessTokenConverter();// 设置秘钥converter.setSigningKey(SIGN_KEY);return converter;}@BeanPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

MyAuthorizationConfig类

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate TokenStore tokenStore;/*** 客户端存储策略,这里使用内存方式,后续可以存储在数据库*/@Autowiredprivate ClientDetailsService clientDetailsService;/*** Security的认证管理器,密码模式需要用到*/@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtAccessTokenConverter jwtAccessTokenConverter;/*** 配置令牌访问的安全约束*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security//开启/oauth/token_key验证端口权限访问.tokenKeyAccess("permitAll()")//开启/oauth/check_token验证端口认证权限访问.checkTokenAccess("permitAll()")//表示支持 client_id 和 client_secret 做登录认证.allowFormAuthenticationForClients();}//配置客户端@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {//内存模式clients.inMemory()//客户端id.withClient("test")//客户端秘钥.secret(new BCryptPasswordEncoder().encode("123456"))//资源id,唯一,比如订单服务作为一个资源,可以设置多个.resourceIds("order")//授权模式,总共四种,1. authorization_code(授权码模式)、password(密码模式)、client_credentials(客户端模式)、implicit(简化模式)//refresh_token并不是授权模式,.authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")//允许的授权范围,客户端的权限,这里的all只是一种标识,可以自定义,为了后续的资源服务进行权限控制.scopes("all")//false 则跳转到授权页面.autoApprove(false)//授权码模式的回调地址.redirectUris("http://www.baidu.com"); //可以and继续添加客户端}@Beanpublic AuthorizationServerTokenServices tokenServices() {DefaultTokenServices services = new DefaultTokenServices();//客户端端配置策略services.setClientDetailsService(clientDetailsService);//支持令牌的刷新services.setSupportRefreshToken(true);//令牌服务services.setTokenStore(tokenStore);//access_token的过期时间services.setAccessTokenValiditySeconds(60 * 60 * 2);//refresh_token的过期时间services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);//设置令牌增强,使用jwtservices.setTokenEnhancer(jwtAccessTokenConverter);return services;}/*** 授权码模式的service,使用授权码模式authorization_code必须注入*/@Beanpublic AuthorizationCodeServices authorizationCodeServices() {//授权码存在内存中return new InMemoryAuthorizationCodeServices();}/*** 配置令牌访问的端点*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints//授权码模式所需要的authorizationCodeServices.authorizationCodeServices(authorizationCodeServices())//密码模式所需要的authenticationManager.authenticationManager(authenticationManager)//令牌管理服务,无论哪种模式都需要.tokenServices(tokenServices())//只允许POST提交访问令牌,uri:/oauth/token.allowedTokenEndpointRequestMethods(HttpMethod.POST);}
}

SecurityConfig类

/*** spring security的安全配置*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 加密算法*/@AutowiredJwtTokenUserDetailsService userDetailsService;@Overrideprotected void configure(HttpSecurity http) throws Exception {//todo 允许表单登录http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginProcessingUrl("/login").permitAll().and().csrf().disable();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//从数据库中查询用户信息auth.userDetailsService(userDetailsService);}/*** AuthenticationManager对象在OAuth2认证服务中要使用,提前放入IOC容器中* Oauth的密码模式需要*/@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

2. 资源服务

导入依赖

  <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

application.yaml

server:port: 8081
spring:application:name: oauth2-cloud-servicecloud:nacos:## 注册中心配置discovery:# nacos的服务地址,nacos-server中IP地址:端口号server-addr: 127.0.0.1:8848

 AuthenticationFilter类

@Component
public class AuthenticationFilter extends OncePerRequestFilter {/*** 具体方法主要分为两步* 1. 解密网关传递的信息* 2. 将解密之后的信息封装放入到request中*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//获取请求头中的用户信息String token = request.getHeader("token");if (token!=null){//解密String json = Base64.decodeStr(token);JSONObject jsonObject = JSON.parseObject(json);//获取用户身份信息、权限信息String principal = jsonObject.getString("user_name");JSONArray tempJsonArray = jsonObject.getJSONArray("authorities");//权限String[] authorities =  tempJsonArray.toArray(new String[0]);//放入LoginValLoginVal loginVal = new LoginVal();loginVal.setUsername(principal);loginVal.setAuthorities(authorities);//放入request的attribute中request.setAttribute("login_message",loginVal);}filterChain.doFilter(request,response);}
}

ServiceController类

@RestController
public class ServiceController {@RequestMapping("/test")public LoginVal test(HttpServletRequest httpServletRequest){return  (LoginVal)httpServletRequest.getAttribute("login_message");}
}

3. Gateway网关

导入依赖

 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-resource-server</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies>

application.yaml

server:port: 7000
spring:main:allow-bean-definition-overriding: trueapplication:name: oauth2-cloud-gatewaycloud:nacos:## 注册中心配置discovery:# nacos的服务地址,nacos-server中IP地址:端口号server-addr: 127.0.0.1:8848gateway:## 路由routes:## id只要唯一即可,名称任意- id: oauth2-cloud-auth-serveruri: lb://oauth2-cloud-auth-serverpredicates:## Path Route Predicate Factory断言- Path=/oauth/**- id: oauth2-cloud-orderuri: lb://oauth2-cloud-servicepredicates:## Path Route Predicate Factory断言- Path=/test/**oauth2:cloud:sys:parameter:ignoreUrls:- /oauth/token- /oauth/authorize

 AccessTokenConfig类

/*** 令牌的配置*/
@Configuration
public class AccessTokenConfig {private final static String SIGN_KEY="jwt";/*** 令牌的存储策略*/@Beanpublic TokenStore tokenStore() {//使用JwtTokenStore生成JWT令牌return new JwtTokenStore(jwtAccessTokenConverter());}/*** JwtAccessTokenConverter* TokenEnhancer的子类,在JWT编码的令牌值和OAuth身份验证信息之间进行转换。* TODO:后期可以使用非对称加密*/@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter converter = new JwtAccessTokenConverter();// 设置秘钥converter.setSigningKey(SIGN_KEY);return converter;}
}

JwtAccessManager类

@Slf4j
@Component
//经过认证管理器JwtAuthenticationManager认证成功后,就需要对令牌进行鉴权,如果该令牌无访问资源的权限,则不允通过。
public class JwtAccessManager implements ReactiveAuthorizationManager<AuthorizationContext> {@Overridepublic Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {URI uri = authorizationContext.getExchange().getRequest().getURI();//设计权限角色,这里简单写一下,实际上应该从数据库或者缓存中获取List<String> authorities = new ArrayList<>();authorities.add("ROLE_admin");//认证通过且角色匹配的用户可访问当前路径return mono//判断是否认证成功.filter(Authentication::isAuthenticated)//获取认证后的全部权限.flatMapIterable(Authentication::getAuthorities).map(GrantedAuthority::getAuthority)//如果权限包含则判断为true.any(authorities::contains).map(AuthorizationDecision::new).defaultIfEmpty(new AuthorizationDecision(false));}}

JwtAuthenticationManager类

/*** JWT认证管理器,主要的作用就是对携带过来的token进行校验,比如过期时间,加密方式等* 一旦token校验通过,则交给鉴权管理器进行鉴权*/
@Component
public class JwtAuthenticationManager implements ReactiveAuthenticationManager {/*** 使用JWT令牌进行解析令牌*/@Autowiredprivate TokenStore tokenStore;@Overridepublic Mono<Authentication> authenticate(Authentication authentication) {return Mono.justOrEmpty(authentication).filter(a -> a instanceof BearerTokenAuthenticationToken).cast(BearerTokenAuthenticationToken.class).map(BearerTokenAuthenticationToken::getToken).flatMap((accessToken -> {OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);//根据access_token从数据库获取不到OAuth2AccessTokenif (oAuth2AccessToken == null) {return Mono.error(new InvalidTokenException("无效的token!"));} else if (oAuth2AccessToken.isExpired()) {return Mono.error(new InvalidTokenException("token已过期!"));}OAuth2Authentication oAuth2Authentication = this.tokenStore.readAuthentication(accessToken);if (oAuth2Authentication == null) {return Mono.error(new InvalidTokenException("无效的token!"));} else {return Mono.just(oAuth2Authentication);}})).cast(Authentication.class);}
}

SecurityConfig类

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {/*** JWT的鉴权管理器*/@Autowiredprivate ReactiveAuthorizationManager<AuthorizationContext> accessManager;@Autowiredprivate RequestAuthenticationEntryPoint requestAuthenticationEntryPoint;@Autowiredprivate RequestAccessDeniedHandler requestAccessDeniedHandler;/*** 系统参数配置*/@Autowiredprivate SysParameterConfig sysConfig;/*** token校验管理器*/@Autowiredprivate ReactiveAuthenticationManager tokenAuthenticationManager;@Autowiredprivate CorsFilter corsFilter;@BeanSecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception{//认证过滤器,放入认证管理器tokenAuthenticationManagerAuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(tokenAuthenticationManager);authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());http.httpBasic().disable().csrf().disable().authorizeExchange()//白名单直接放行.pathMatchers(ArrayUtil.toArray(sysConfig.getIgnoreUrls(),String.class)).permitAll()//其他的请求必须鉴权,使用鉴权管理器.anyExchange().access(accessManager)//异常处理.and().exceptionHandling().authenticationEntryPoint(requestAuthenticationEntryPoint).accessDeniedHandler(requestAccessDeniedHandler).and()// 跨域过滤器.addFilterAt(corsFilter, SecurityWebFiltersOrder.CORS)//token的认证过滤器,用于校验token和认证.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);return http.build();}
}

RequestAccessDeniedHandler

/*** 自定义返回结果:没有权限访问时*/
@Component
public class RequestAccessDeniedHandler implements ServerAccessDeniedHandler {@Overridepublic Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.OK);response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);String body= JSONUtil.toJsonStr(new ResultMsg(1005,"无权限访问",null));DataBuffer buffer =  response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));return response.writeWith(Mono.just(buffer));}
}

GlobalAuthenticationFilter

/*** 全局过滤器,对token的拦截,解析token放入header中,便于下游微服务获取用户信息* 分为如下几步:*  1、白名单直接放行*  2、校验token*  3、读取token中存放的用户信息*  4、重新封装用户信息,加密成功json数据放入请求头中传递给下游微服务*/
@Component
@Slf4j
public class GlobalAuthenticationFilter implements GlobalFilter, Ordered {/*** JWT令牌的服务*/@Autowiredprivate TokenStore tokenStore;/*** 系统参数配置*/@Autowiredprivate SysParameterConfig sysConfig;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String requestUrl = exchange.getRequest().getPath().value();//1、白名单放行,比如授权服务、静态资源.....if (checkUrls(sysConfig.getIgnoreUrls(),requestUrl)){return chain.filter(exchange);}//2、 检查token是否存在String token = getToken(exchange);if (StringUtils.isBlank(token)) {return invalidTokenMono(exchange);}//3 判断是否是有效的tokenOAuth2AccessToken oAuth2AccessToken;try {//解析token,使用tokenStoreoAuth2AccessToken = tokenStore.readAccessToken(token);Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();System.out.println(additionalInformation);//取出用户身份信息String user_name = additionalInformation.get("user_name").toString();//获取用户权限List<String> authorities = (List<String>) additionalInformation.get("authorities");//将用户名和权限进行Base64加密JSONObject jsonObject=new JSONObject();jsonObject.put("user_name", user_name);jsonObject.put("authorities",authorities);String base = Base64.encode(jsonObject.toJSONString());// ServerHttpRequest 中的 mutate 方法是用于创建一个修改后的请求对象的方法,而不改变原始请求对象。这个方法是为了在处理请求过程中创建一个新的请求对象,以便进行一些修改或增强。ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header("token",base).build();ServerWebExchange build = exchange.mutate().request(tokenRequest).build();return chain.filter(build);} catch (InvalidTokenException e) {//解析token异常,直接返回token无效return invalidTokenMono(exchange);}}@Overridepublic int getOrder() {return 0;}/*** 对url进行校验匹配*/private boolean checkUrls(List<String> urls,String path){AntPathMatcher pathMatcher = new AntPathMatcher();for (String url : urls) {if (pathMatcher.match(url,path))return true;}return false;}/*** 从请求头中获取Token*/private String getToken(ServerWebExchange exchange) {String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");if (StringUtils.isBlank(tokenStr)) {return null;}String token = tokenStr.split(" ")[1];if (StringUtils.isBlank(token)) {return null;}return token;}/*** 无效的token*/private Mono<Void> invalidTokenMono(ServerWebExchange exchange) {return buildReturnMono(ResultMsg.builder().code(1004).msg("无效的token").build(), exchange);}private Mono<Void> buildReturnMono(ResultMsg resultMsg, ServerWebExchange exchange) {ServerHttpResponse response = exchange.getResponse();byte[] bits = JSON.toJSONString(resultMsg).getBytes(StandardCharsets.UTF_8);DataBuffer buffer = response.bufferFactory().wrap(bits);response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add("Content-Type", "application/json;charset:utf-8");return response.writeWith(Mono.just(buffer));}
}

SysParameterConfig

@ConfigurationProperties(prefix = "oauth2.cloud.sys.parameter")
@Data
@Component
public class SysParameterConfig {/*** 白名单*/private List<String> ignoreUrls;
}

4. 测试

 

代码链接:Gateway+Springsecurity+OAuth2.0+JWT实现分布式统一认证授权资源-CSDN文库 

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

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

相关文章

Golang 中如何实现 Set

在Go编程中&#xff0c;数据结构的选择对解决问题至关重要。本文将探讨如何在 GO 中实现 set 和 bitset 两种数据结构&#xff0c;以及它们在Go中的应用场景。 Go 的数据结构 Go 内置的数据结构并不多。工作中&#xff0c;我们最常用的两种数据结构分别是 slice 和 map&#…

K8S--安装Nginx

原文网址&#xff1a;K8S--安装Nginx-CSDN博客 简介 本文介绍K8S安装Nginx的方法。 1.创建Nginx目录及配置文件 mkdir -p /work/devops/k8s/app/nginx/{config,html} 在config目录下创建nginx.conf配置文件&#xff0c;内容如下&#xff1a; # events必须要有 events {wo…

jdk的安装和Tomcat的安装

jdk的安装 双击jdk&#xff0c;然后一路下一步 公共JRE可以关闭&#xff0c;没多大用&#xff0c;反而会占用内存 计算机–>属性–>高级系统设置–>环境变量 系统变量–新建 JAVA_HOMEjdk的存放路径 修改path 在path的最后面添加&#xff08;&#xff1b;%JAVA_H…

即插即用篇 | UniRepLKNet:用于音频、视频、点云、时间序列和图像识别的通用感知大卷积神经网络 | DRepConv

大卷积神经网络(ConvNets)近来受到了广泛研究关注,但存在两个未解决且需要进一步研究的关键问题。1)现有大卷积神经网络的架构主要遵循传统ConvNets或变压器的设计原则,而针对大卷积神经网络的架构设计仍未得到解决。2)随着变压器在多个领域的主导地位,有待研究ConvNets…

【漏洞复现】SpringBlade export-user接口SQL注入漏洞

文章目录 前言声明一、SpringBlade系统简介二、漏洞描述三、影响版本四、漏洞复现五、修复建议 前言 SpringBlade 是一个由商业级项目升级优化而来的微服务架构 采用Spring Boot 2.7 、Spring Cloud 2021 等核心技术构建&#xff0c;完全遵循阿里巴巴编码规范。提供基于React和…

PowerShell install 一键部署grafana

grafana 前言 Grafana 是一款开源的数据可视化和监控仪表盘工具。它提供了丰富的数据查询、可视化和报警功能,可用于实时监控、数据分析和故障排除等领域。 通过 Grafana,您可以连接到各种不同的数据源,包括时序数据库(如 Prometheus、InfluxDB)和关系型数据库(如 MySQ…

Halcon基于形状的模板匹配inspect_shape_model

Halcon基于形状的模板匹配 基于形状的匹配&#xff0c;就是使用目标对象的轮廓形状来描述模板。Halcon中有操作助手&#xff0c;可以直观 地进行形状模板匹配的参数选择以及效果测试。如果使用算子编写&#xff0c;步骤如下。 &#xff08;1&#xff09;从参考图像上选择检测的…

c++:string相关的oj题(415. 字符串相加、125. 验证回文串、541. 反转字符串 II、557. 反转字符串中的单词 III)

文章目录 1. 415. 字符串相加题目详情代码1思路1代码2思路2 2. 125. 验证回文串题目详情代码1&#xff08;按照要求修改后放到新string里&#xff09;思路1代码2(利用双指针/索引)思路2 3. 541. 反转字符串 II题目详情代码1思路1 4. 557. 反转字符串中的单词 III题目详情代码1&…

CocoaPods的安装和使用

前言 本篇文章讲述CocoaPods的安装和使用 安装cocoaPods 如果电脑没有安装过cocoaPods&#xff0c;需要先安装&#xff0c;使用下面的命令&#xff1a; sudo gem install cocoapods输入密码后开始安装&#xff0c;需要等待。。。但是我这里报错了。 The last version of d…

nexus清理docker私库

下载nexus-cli客户端&#xff0c;并非必须下载到服务器&#xff0c;理论上只要能访问到nexus就行 wget https://s3.eu-west-2.amazonaws.com/nexus-cli/1.0.0-beta/linux/nexus-cli这个链接下载不了了&#xff0c;末尾有资源下载&#xff0c;里面包含了完整包和脚本&#xff0…

即插即用篇 | YOLOv8 引入 SENetv2 | 多套版本配合使用

卷积神经网络(CNNs)通过提取空间特征并在基于视觉的任务中实现了最先进的准确性,彻底改变了图像分类。所提出的压缩激励网络模块收集输入的通道表示。多层感知机(MLP)从数据中学习全局表示,在大多数用于学习图像提取特征的图像分类模型中起到关键作用。在本文中,我们引入…

npm ,yarn 更换使用国内镜像源,阿里源,清华大学源

在平时开发当中&#xff0c;我们经常会使用 Npm&#xff0c;yarn 来构建 web 项目。但是npm默认的源的服务器是在国外的&#xff0c;如果没有梯子的话。会感觉特别特别慢&#xff0c;所以&#xff0c;使用国内的源是非常有必要的。 Nnpm&#xff0c; yarn 常用命令 常用命令&am…

Appium 环境配置

Appium 是一个开源的、跨平台的测试框架&#xff0c;可以用来测试 Native App、混合应用、移动 Web 应用&#xff08;H5 应用&#xff09;等&#xff0c;也是当下互联网企业实现移动自动化测试的重要工具。Appium 坚持的测试理念&#xff1a; •无需用户对 App 进行任何修改或…

opencv#27模板匹配

图像模板匹配原理 例如给定一张图片&#xff0c;如上图大矩阵所示&#xff0c;然后给定一张模板图像&#xff0c;如上图小矩阵。 我们在大图像中去搜索与小图像中相同的部分或者是最为相似的内容。比如我们在图像中以灰色区域给出一个与模板图像尺寸大小一致的区域&#xff0c;…

软考系分之计算机网络规划设计、综合布线、RAID和网络存储等

文章目录 1、概要2、网络的三层模型3、综合布线系统4、廉价磁盘冗余阵列&#xff08;RAID&#xff09;5、网络存储6、总结 1、概要 本篇重点介绍计算机网络中的网络规划设计、综合布线、RAID和网络存储。 2、网络的三层模型 三层模型分为核心层、汇聚层和接入层&#xff0c;接…

数组A[m+n]中存放了两个线性表(a1,a2,.....am)和(b1,b2.....bn),将数组中的两个线性表的位置互换,要求空间复杂度为1

要求空间复杂度为O(1)&#xff0c;那么不可以借助辅助数组来完成此操作 算法思路&#xff1a;可先将此数组逆置变成bn,......b1,am,....,a1&#xff0c;然后分别逆转两个线性表的数据元素 算法实现 1、定义一个函数&#xff0c;该函数的功能是可以对一个数组的任意连续的部分进…

UDP和TCP代理协议有什么区别?哪个更好

在互联网的世界里&#xff0c;数据传输的方式有很多种&#xff0c;其中 UDP 和 TCP 是两种常见的传输协议。而代理协议则是为了在网络中传输数据时提供安全、稳定和高效的传输环境。那么&#xff0c;UDP 和 TCP 代理协议有什么区别呢&#xff1f;哪个更好呢&#xff1f;接下来&…

内网穿透的应用-使用Docker搭建一个Wiki.Js知识库系统并实现分享他人远程创作

文章目录 1. 安装Docker2. 获取Wiki.js镜像3. 本地服务器打开Wiki.js并添加知识库内容4. 实现公网访问Wiki.js5. 固定Wiki.js公网地址 不管是在企业中还是在自己的个人知识整理上&#xff0c;我们都需要通过某种方式来有条理的组织相应的知识架构&#xff0c;那么一个好的知识整…

力扣移掉k位数字402

Problem: 402. 移掉 K 位数字 给你一个以字符串表示的非负整数 num 和一个整数 k &#xff0c;移除这个数中的 k 位数字&#xff0c;使得剩下的数字最小。请你以字符串形式返回这个最小的数字。 示例 1 &#xff1a; 给你一个以字符串表示的非负整数 num 和一个整数 k &…

阿里云 SAE 2.0 正式商用:极简易用、百毫秒弹性效率,降本 40%

作者&#xff1a;黛忻 本文主要介绍阿里云 Serverless 应用引擎&#xff08;以下简称 SAE &#xff09;如何帮助企业跨越技术鸿沟&#xff0c;从传统应用架构无感升级到 Serverless 架构&#xff0c;以更高效、更经济的方式进行转型&#xff0c;快速进入云原生快车道&#xff0…