gateway 整合 spring security oauth2

微服务分布式认证授权方案

在分布式授权系统中,授权服务要独立成一个模块做统一授权,无论客户端是浏览器,app或者第三方,都会在授权服务中获取权限,并通过网关访问资源

OAuth2的四种授权模式

授权码模式

授权服务器将授权码(AuthorizationCode)转经浏览器发送给client,客户端拿着授权码向授权服务器索要访问access_token,这种模式是四种模式中最安全的一种模式。一般用于Web服务器端应用或第三方的原生App调用资源服务的时候

密码模式

资源拥有者将用户名、密码发送给客户端,客户端拿着资源拥有者的用户名、密码向授权服务器请求令牌(access_token),密码模式使用较多,适应于第一方的单页面应用以及第一方的原生App

客户端模式

客户端向授权服务器发送自己的身份信息,并请求令牌(access_token),确认客户端身份无误后,将令牌(access_token)发送给client,这种模式是最方便但最不安全的模式。因此这就要求我们对client完全的信任,而client本身也是安全的。因此这种模式一般用来提供给我们完全信任的服务器端服务

简化模式

资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,授权服务器将授权码将令牌(access_token)以Hash的形式存放在重定向uri的fargment中发送给浏览器

 spring security 部分请看:spring security 认证授权

OAuth2的配置

  • 添加配置类,添加@EnableAuthorizationServer 注解标注这是一个认证中心
  • 继承 AuthorizationServerConfigurerAdapter
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {/*** 令牌存储策略*/@Autowiredprivate TokenStore tokenStore;/*** 客户端存储策略,这里使用内存方式,后续可以存储在数据库*/@Autowiredprivate ClientDetailsService clientDetailsService;/*** Security的认证管理器,密码模式需要用到*/@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtAccessTokenConverter jwtAccessTokenConverter;@Autowiredprivate OAuthServerAuthenticationEntryPoint authenticationEntryPoint;@Autowiredprivate DataSource dataSource;/*** 配置客户端详情,并不是所有的客户端都能接入授权服务*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {//TODO 暂定内存模式,后续可以存储在数据库中,更加方便clients.inMemory()//客户端id.withClient("myjszl")//客户端秘钥.secret(new BCryptPasswordEncoder().encode("123"))//资源id,唯一,比如订单服务作为一个资源,可以设置多个.resourceIds("res1")//授权模式,总共四种,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");}/*** 令牌管理服务的配置*/@Beanpublic AuthorizationServerTokenServices tokenServices() {DefaultTokenServices services = new DefaultTokenServices();//客户端端配置策略services.setClientDetailsService(clientDetailsService);//支持令牌的刷新services.setSupportRefreshToken(true);//令牌服务services.setTokenStore(tokenStore);//access_token的过期时间services.setAccessTokenValiditySeconds(60 * 60 * 24 * 3);//refresh_token的过期时间services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);//设置令牌增强,使用JwtAccessTokenConverter进行转换services.setTokenEnhancer(jwtAccessTokenConverter);return services;}/*** 授权码模式的service,使用授权码模式authorization_code必须注入*/@Beanpublic AuthorizationCodeServices authorizationCodeServices() {return new JdbcAuthorizationCodeServices(dataSource);//todo 授权码暂时存在内存中,后续可以存储在数据库中
//        return new InMemoryAuthorizationCodeServices();}/*** 配置令牌访问的端点*/@Override@SuppressWarnings("ALL")public void configure(AuthorizationServerEndpointsConfigurer endpoints) {//将自定义的授权类型添加到tokenGranters中List<TokenGranter> tokenGranters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));tokenGranters.add(new MobilePwdGranter(authenticationManager, tokenServices(), clientDetailsService,new DefaultOAuth2RequestFactory(clientDetailsService)));endpoints//设置异常WebResponseExceptionTranslator,用于处理用户名,密码错误、授权类型不正确的异常.exceptionTranslator(new OAuthServerWebResponseExceptionTranslator())//授权码模式所需要的authorizationCodeServices.authorizationCodeServices(authorizationCodeServices())//密码模式所需要的authenticationManager.authenticationManager(authenticationManager)//令牌管理服务,无论哪种模式都需要.tokenServices(tokenServices())//添加进入tokenGranter.tokenGranter(new CompositeTokenGranter(tokenGranters))//只允许POST提交访问令牌,uri:/oauth/token.allowedTokenEndpointRequestMethods(HttpMethod.POST);}/*** 配置令牌访问的安全约束*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) {//自定义ClientCredentialsTokenEndpointFilter,用于处理客户端id,密码错误的异常OAuthServerClientCredentialsTokenEndpointFilter endpointFilter = new OAuthServerClientCredentialsTokenEndpointFilter(security,authenticationEntryPoint);endpointFilter.afterPropertiesSet();security.addTokenEndpointAuthenticationFilter(endpointFilter);security.authenticationEntryPoint(authenticationEntryPoint)//开启/oauth/token_key验证端口权限访问.tokenKeyAccess("permitAll()")//开启/oauth/check_token验证端口认证权限访问.checkTokenAccess("permitAll()");//一定不要添加allowFormAuthenticationForClients,否则自定义的OAuthServerClientCredentialsTokenEndpointFilter不生效
//                .allowFormAuthenticationForClients();}
}

主要配置点:

  • 先配置客户端详细信息ClientDetailService   configure(ClientDetailsServiceConfigurer clients)
  • 在配置令牌访问端点(需要先配置令牌管理服务和tokenStore表示令牌的存放和管理策略)configure(AuthorizationServerEndpointsConfigurer endpoints)  配置加入令牌管理服务和其他oauth2.0需要的管理器和service
  • 最后配置访问令牌的约束条件,configure(AuthorizationServerSecurityConfigurer security)定义哪些端点可以访问 

spring security的配置 

  • 定义配置类,添加@EnableWebSecurity 注解
  • 继承WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate SmsCodeSecurityConfig smsCodeSecurityConfig;/*** 加密算法*/@BeanPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http//注入自定义的授权配置类.apply(smsCodeSecurityConfig).and().authorizeRequests()//注销的接口需要放行.antMatchers("/oauth/logout").permitAll().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();}
}

配置内容:

  • 定义加密算法,定义查询用户信息方案,在oauth2中密码模式会用到授权管理器
  • 定义访问策略,哪些放行,哪些需要授权 

JWT令牌配置 

不透明令牌则是令牌本身不存储任何信息,比如一串UUID,InMemoryTokenStore就类似这种

因此资源服务拿到这个令牌必须调调用认证授权服务的接口进行令牌的校验,高并发的情况下延迟很高,性能很低

透明令牌本身就存储这部分用户信息,比如JWT,资源服务可以调用自身的服务对该令牌进行校验解析,不必调用认证服务的接口去校验令牌

JwtAccessTokenConverter令牌增强类,用于JWT令牌和OAuth身份进行转换,内部生成JWT令牌,封装进入OAuth2AccessToken对象返回

在gateway中也需要有jwt令牌和tokenstore配置,代码相同

/*** * 令牌的配置*/
@Configuration
public class AccessTokenConfig {/*** 令牌的存储策略*/@Beanpublic TokenStore tokenStore() {//使用JwtTokenStore生成JWT令牌return new JwtTokenStore(jwtAccessTokenConverter());}/*** JwtAccessTokenConverter* TokenEnhancer的子类,在JWT编码的令牌值和OAuth身份验证信息之间进行转换。* TODO:后期可以使用非对称加密*/@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter converter = new JwtAccessTokenEnhancer();// 设置秘钥converter.setSigningKey(TokenConstant.SIGN_KEY);/** 设置自定义得的令牌转换器,从map中转换身份信息* fix(*):修复刷新令牌无法获取用户详细信息的问题*/converter.setAccessTokenConverter(new JwtEnhanceAccessTokenConverter());return converter;}/*** JWT令牌增强,继承JwtAccessTokenConverter* 将业务所需的额外信息放入令牌中,这样下游微服务就能解析令牌获取*/public static class JwtAccessTokenEnhancer extends JwtAccessTokenConverter {/*** 重写enhance方法,在其中扩展*/@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {Object principal = authentication.getUserAuthentication().getPrincipal();if (principal instanceof SecurityUser){//获取userDetailService中查询到用户信息SecurityUser user=(SecurityUser)principal;//将额外的信息放入到LinkedHashMap中LinkedHashMap<String,Object> extendInformation=new LinkedHashMap<>();//设置用户的userIdextendInformation.put(TokenConstant.USER_ID,user.getUserId());//添加到additionalInformation((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(extendInformation);}return super.enhance(accessToken, authentication);}}
}

gateway认证管理器 

JwtAuthenticationManager,需要实现ReactiveAuthenticationManager这个接口。

认证管理的作用就是获取传递过来的令牌,对其进行解析、验签、过期时间判定 

/*** * JWT认证管理器,主要的作用就是对携带过来的token进行校验,比如过期时间,加密方式等* 一旦token校验通过,则交给鉴权管理器进行鉴权*/
@Component
@Slf4j
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);}
}

gateway 鉴权管理器

经过认证管理器JwtAuthenticationManager认证成功后,就需要对令牌进行鉴权,如果该令牌无访问资源的权限,则不允通过

JwtAccessManager,实现ReactiveAuthorizationManager

/**** 鉴权管理器 用于认证成功之后对用户的权限进行鉴权* TODO 此处的逻辑:从redis中获取对应的uri的权限,与当前用户的token的携带的权限进行对比,如果包含则鉴权成功*      企业中可能有不同的处理逻辑,可以根据业务需求更改鉴权的逻辑**/
@Slf4j
@Component
@Deprecated
public class JwtAccessManager implements ReactiveAuthorizationManager<AuthorizationContext> {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Overridepublic Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {//从Redis中获取当前路径可访问角色列表URI uri = authorizationContext.getExchange().getRequest().getURI();Object value = redisTemplate.opsForHash().get(SysConstant.OAUTH_URLS, uri.getPath());List<String> authorities = Convert.toList(String.class,value);//认证通过且角色匹配的用户可访问当前路径return mono//判断是否认证成功.filter(Authentication::isAuthenticated)//获取认证后的全部权限.flatMapIterable(Authentication::getAuthorities).map(GrantedAuthority::getAuthority)//如果权限包含则判断为true.any(authorities::contains).map(AuthorizationDecision::new).defaultIfEmpty(new AuthorizationDecision(false));}}

gateway security oauth2.0配置

  • 添加@EnableWebFluxSecurity注解,由于spring cldou gateway使用的Flux,因此需要使用@EnableWebFluxSecurity注解开启
  • 配置SecurityWebFilterChain添加跨域认证异常鉴权配置
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {/*** JWT的鉴权管理器*/@Autowiredprivate ReactiveAuthorizationManager<AuthorizationContext> accessManager;/*** token过期的异常处理*/@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)//鉴权的异常处理,权限不足,token失效.and().exceptionHandling().authenticationEntryPoint(requestAuthenticationEntryPoint).accessDeniedHandler(requestAccessDeniedHandler).and()// 跨域过滤器.addFilterAt(corsFilter, SecurityWebFiltersOrder.CORS)//token的认证过滤器,用于校验token和认证.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);return http.build();}
}

gateway全局过滤器

校验token,并将通过认证授权的请求转发下层服务,并将token信息放到请求头中


/***  1、白名单直接放行*  2、校验token*  3、读取token中存放的用户信息*  4、重新封装用户信息,加密成功json数据放入请求头中传递给下游微服务*/
@Component
@Slf4j
public class GlobalAuthenticationFilter implements GlobalFilter, Ordered {/*** JWT令牌的服务*/@Autowiredprivate TokenStore tokenStore;@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 系统参数配置*/@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();//令牌的唯一IDString jti=additionalInformation.get(TokenConstant.JTI).toString();/**查看黑名单中是否存在这个jti,如果存在则这个令牌不能用****/Boolean hasKey = stringRedisTemplate.hasKey(SysConstant.JTI_KEY_PREFIX + jti);if (hasKey)return invalidTokenMono(exchange);//取出用户身份信息String user_name = additionalInformation.get("user_name").toString();//获取用户权限List<String> authorities = (List<String>) additionalInformation.get("authorities");//从additionalInformation取出userIdString userId = additionalInformation.get(TokenConstant.USER_ID).toString();JSONObject jsonObject=new JSONObject();jsonObject.put(TokenConstant.PRINCIPAL_NAME, user_name);jsonObject.put(TokenConstant.AUTHORITIES_NAME,authorities);//过期时间,单位秒jsonObject.put(TokenConstant.EXPR,oAuth2AccessToken.getExpiresIn());jsonObject.put(TokenConstant.JTI,jti);//封装到JSON数据中jsonObject.put(TokenConstant.USER_ID, userId);//将解析后的token加密放入请求头中,方便下游微服务解析获取用户信息String base64 = Base64.encode(jsonObject.toJSONString());//放入请求头中ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header(TokenConstant.TOKEN_NAME, base64).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(ResultCode.INVALID_TOKEN.getCode()).msg(ResultCode.INVALID_TOKEN.getMsg()).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));}
}

本文参照项目:spring-security-oauth2 

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

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

相关文章

【密码学】全同态加密张量运算库解读 —— TenSEAL

项目地址&#xff1a;https://github.com/OpenMined/TenSEAL 论文地址&#xff1a;https://arxiv.org/pdf/2104.03152v2 TenSEAL 是一个在微软 SEAL 基础上构建的用于对张量进行同态加密操作的开源Python库&#xff0c;用于在保持数据加密的状态下进行机器学习和数据分析。 Ten…

CSS基础—网页布局(重点!)

1、两列布局 &#xff08;1&#xff09;概念 经典两列布局是指一种网页布局方式&#xff0c;其中一列宽度固定&#xff0c;另一列宽度自适应。‌ 这种布局方式在网页设计中非常常见&#xff0c;因为它能够提供良好的视觉效果和用户体验。 如图所示&#xff1a; 页面顶部放置一…

网络搜索引擎Shodan(4)

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 声明&#xff1a;本文主要用作技术分享&#xff0c;所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险&#xff0c;并遵循相关法律法规。 感谢泷…

【JavaEE】【多线程】volatile,wait/notify

目录 一、volatile关键字1.1 内存可见性1.2 volatile解决内存可见性问题 二、wait和notify2.1 wait2.2 notify2.3 使用例子2.3.1 例子12.3.2 例子二 一、volatile关键字 volatile可以保证内存可见性&#xff0c;只能修饰变量。 1.1 内存可见性 在前面介绍线程不安全原因时介…

大数据开发扩展shell 笔记

大数据开发扩展shell 此笔记来自尚硅谷 学习目标 1 熟悉shell脚本的原理和使用 2 熟悉shell的编程语法 第一节 Shell概述 1&#xff09;Linux提供的Shell解析器有&#xff1a; [atguiguhadoop101 ~]$ cat /etc/shells /bin/sh/bin/bash/sbin/nologin/bin/dash/bin/tcsh/b…

JCSA-Journal of Consumer Affairs

文章目录 一、征稿简介二、重要信息三、服务简述四、投稿须知五、联系咨询 一、征稿简介 二、重要信息 期刊官网&#xff1a;https://ais.cn/u/3eEJNv 三、服务简述 Journal of Consumer Affairs由美国消费者利益委员会&#xff08;ACCI&#xff09;拥有&#xff0c;成立于1…

淘宝商品详情的“侦探游戏”:如何用API接口揭开数据的面纱

在这个充满神秘数据的电商世界里&#xff0c;淘宝商品详情就像是一个个隐藏的宝藏&#xff0c;等待着我们去发掘。而API接口&#xff0c;就是我们的“侦探工具”&#xff0c;帮助我们快速揭开这些宝藏的面纱。今天&#xff0c;我们就来一场幽默的“侦探游戏”&#xff0c;看看如…

炒股VS炒CSGO游戏装备,哪个更好做

这个项目&#xff0c;赚个10%都是要被嫌弃的 虽然天天都在抒发自己对股市的看法&#xff0c;但自己自始至终也没有买进任何一支股票。之所以对这个话题感兴趣&#xff0c;着实是因为手上的游戏搬砖项目也是国际性买卖&#xff0c;跟国际形势&#xff0c;国际汇率挂钩&#xff…

C++线程池手写实现

1.Thread类的封装 封装Thread类&#xff0c;使其可以直接在外部调用对象的start,detach,join和cancel等方法来实现对线程的操作 1.1代码 //Thread.h// // Created by crab on 2024/10/20. //#ifndef THREAD_H #define THREAD_H#include <pthread.h>class Thread { pub…

使用 Docker 管理完整项目:Java、Vue、Redis 和 Nginx 的一站式部署

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…

[大模型学习推理]资料

https://juejin.cn/post/7353963878541361192 lancedb是个不错的数据库&#xff0c;有很多学习资料 https://github.com/lancedb/vectordb-recipes/tree/main/tutorials/Multi-Head-RAG-from-Scratch 博主讲了很多讲解&#xff0c;可以参考 https://juejin.cn/post/7362789…

js纯操作dom版购物车(实现购物车功能)

代码&#xff1a; <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title>&l…

Linux 进程间通信_匿名管道

1.程间通信目的 : 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源。 通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某种事件&#xff08;如…

vue3+vite 部署npm 包

公司需要所以研究了一下怎么部署安装&#xff0c;比较简单 先下载个vue项目 用vue脚手架安装vue3项目 删掉App.vue 中的其它组件 npm create vuelatest 开始写自定义组件 新建一个el-text 组件, name是重点&#xff0c;vue3中也得这么导出name属性 <script> export de…

JAVA-----线程池

目录 1.简单理解一下线程池 2.线程池参数 3.Executors ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 1.简单理解一下线程池 线程池&#xff0c;就是用来存储线程的&#xff0c;然后如果有任务传进线程池…

PortQry下载安装使用教程(超详细),Windows测试UDP端口

《网络安全自学教程》 PortQry是微软官方提供的一款TCP/IP连接「排障工具」&#xff0c;用来「检查」TCP/UDP「端口状态」。 平时检查端口状态&#xff0c;最常用的是telnet&#xff0c;但它是基于TCP协议的&#xff0c;无法检测「UDP端口」&#xff0c;这篇文章教大家如何在W…

Flink-cdc Schema Evolution 详解

Flink-cdc Schema Evolution 详解 github原文 glimpse flink-cdc-3 glimpse 源码基于 ~/project/flink_src/flink-cdc master !4 ❯ git remote -v origin https://github.com/apache/flink-cdc.git (fetch) origin https://github.com/apache/flink-cdc.git (push) ~/p…

介绍一款Java开发的企业接口管理系统和开放平台

介绍一款Java开发的企业接口管理系统和开放平台&#xff0c;YesApi接口管理平台Java版本。该系统基于Java开发&#xff0c;可以帮助企业进行统一接口管理、API接口开放&#xff0c;以及用于PaaS系统和SaaS产品平台的快速二次开发和搭建。 一、系统概述 YesApi接口大师&#x…

ClickHouse 5节点集群安装

ClickHouse 5节点集群安装 在此架构中&#xff0c;配置了五台服务器。其中两个用于托管数据副本。其他三台服务器用于协调数据的复制。在此示例中&#xff0c;我们将创建一个数据库和表&#xff0c;将使用 ReplicatedMergeTree 表引擎在两个数据节点之间复制该数据库和表。 官…

RHCE作业二

1.要求&#xff1a; 配置nginx服务通过ip访问多网站 2. 1关闭防火墙 2创建ip 3配置 4创建文件 5测试