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.mzph.cn/bicheng/58110.shtml

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

相关文章

(三)行为模式:11、模板模式(Template Pattern)(C++示例)

目录 1、模板模式含义 2、模板模式的UML图学习 3、模板模式的应用场景 4、模板模式的优缺点 5、C实现的实例 1、模板模式含义 模板模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;将某些步骤…

故障诊断 | CNN-ResNets滚动轴承故障诊断实例代码

故障诊断 | CNN-ResNets滚动轴承故障诊断实例代码 目录 故障诊断 | CNN-ResNets滚动轴承故障诊断实例代码效果一览基本介绍程序设计参考资料 效果一览 基本介绍 CNN-ResNets&#xff08;卷积神经网络-残差网络&#xff09;在滚动轴承故障诊断中是一种常用的方法。这种方法利用…

Docker 实践与应用举例教程:从入门到精通

Docker 实践与应用举例教程&#xff1a;从入门到精通 引言 在现代软件开发中&#xff0c;Docker 已成为一种不可或缺的工具。它通过容器化技术简化了应用的部署、管理和扩展&#xff0c;极大地提高了开发和运维的效率。本文将详细介绍 Docker 的基本概念、安装步骤、常用命令…

arm ubuntu22.04 安装es7.16.2

1、更新软件包 sudo apt update && sudo apt upgrade -y 2、安装jdk11 sudo apt install openjdk-11-jdk -y 安装查看版本 java -version 输出应该是这样的 openjdk version "11.0.11" 2021-04-20 OpenJDK Runtime Environment (build 11.0.119-Ub…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-22

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-22 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-22目录1. PoisonedRAG: Knowledge corruption attacks to retrieval-augmented generation of large language models摘要创新点…

Java程序设计:spring boot(8)——API ⽂档构建⼯具 - Swagger2

目录 1 环境整合配置 2 Swagger2 常⽤注解说明 2.1 Api 2.2 ApiOperation 2.3 ApiImplicitParams 2.4 ApiResponses 2.5 ApiModel 3 用户模块注解配置 3.1 Controller 使用注解 3.2 JavaBean 使用注解 4 Swagger2 接⼝⽂档访问 由于 Spring Boot 能够快速开发、便捷…

duilib的应用 在双屏异分辨率的显示器上 运行显示不出来

背景&#xff1a;win11&#xff0c;duilib应用&#xff0c;双显示器&#xff0c;两台分辨率相同&#xff0c;分别设置不同的缩放以后&#xff0c;应用运行以后&#xff0c;程序闪一下消失或者程序还在&#xff0c;但是UI显示不出来。 原因 窗口风格设置不合理&#xff0c;所以…

2024.10.9华为留学生笔试题解

第一题无线基站名字相似度 动态规划 考虑用动态规划解决 char1=input().strip() char2=input().strip() n,m=len(char1),len(char2) dp=[[0]*(m+1) for _ in range(n+1)] #dp[i][j]定义为以i-1为结尾的char1 和以 j-1为结尾的char2 的最短编辑距离 setA = set(wirel@com) set…

如何用mmclassification训练多标签多分类数据

这里使用的源码版本是 mmclassification-0.25.0 训练数据标签文件格式如下&#xff0c;每行的空格前面是路径&#xff08;图像文件所在的绝对路径&#xff09;&#xff0c;后面是标签名&#xff0c;因为特殊要求这里我的每张图像都记录了三个标签每个标签用“,”分开&#xff0…

论文笔记(五十)Segmentation-driven 6D Object Pose Estimation

Segmentation-driven 6D Object Pose Estimation 文章概括摘要1. 引言2. 相关工作3. 方法3.1 网络架构3.2 分割流3.3 回归流3.4 推理策略 4. 实验4.1 评估 Occluded-LINEMOD4.1.1 与最先进技术的比较4.1.2 不同融合策略的比较4.1.3 与人体姿态方法的比较 4.2 在YCB-Video上的评…

linux指令笔记

bash命令行讲解 lyt &#xff1a;是用户名 iZbp1i65rwtrfbmjetete2b2Z :这个是主机名 ~ &#xff1a;这个是当前目录 $ &#xff1a;这个是命令行提示符 每个指令都有不同的功能&#xff0c;大部分指令都可以带上选项来实现不同的效果。 一般指令和选项的格式&#xff1a;…

ClickHouse 3节点集群安装

ClickHouse 简介 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 官方网站&#xff1a;https://clickhouse.com/ 项目地址&#xff1a;https://github.com/ClickHouse/ClickHouse 横向扩展集群介绍 此示例架构旨在提供可扩展性。它包括三个节点&#xff…

【undefined reference to xxx】zookeeper库编译和安装 / sylar项目ubuntu20系统编译

最近学习sylar项目&#xff0c;编译项目时遇到链接库不匹配的问题&#xff0c;记录下自己解决问题过程&#xff0c;虽然过程很艰难&#xff0c;但还是解决了&#xff0c;以下内容供大家参考&#xff01; undefined reference to 问题分析 项目编译报错 /usr/bin/ld: ../lib/lib…

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

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

聊一聊 C#中有趣的 SourceGenerator生成器

一&#xff1a;背景 1. 讲故事 前些天在看 AOT的时候关注了下 源生成器&#xff0c;挺有意思的一个东西&#xff0c;今天写一篇文章简单的分享下。 二&#xff1a;源生成器探究之旅 1. 源生成器是什么 简单来说&#xff0c;源生成器是Roslyn编译器给程序员开的一道口子&#xf…

单体架构VS微服务架构

单体架构&#xff1a;一个包含有所有功能的应用程序 优点&#xff1a;架构简单、开发部署简单缺点&#xff1a;复杂性高、业务功能多、部署慢、扩展差、技术升级困难 如上示意图&#xff0c;应用前端页面&#xff0c;后台所有模块功能都放在一个应用程序中&#xff0c;并部署在…

Safari 中 filter: blur() 高斯模糊引发的性能问题及解决方案

目录 引言问题背景&#xff1a;filter: blur() 引发的问题产生问题的原因分析解决方案&#xff1a;开启硬件加速实际应用示例性能优化建议常见的调试工具与分析方法 引言 在前端开发中&#xff0c;CSS滤镜&#xff08;如filter: blur()&#xff09;的广泛使用为页面带来了各种…

使用上下文管理器和 `yield` 实现基于 Redis 的任务锁定机制

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

预训练 BERT 使用 Hugging Face 和 PyTorch 在 AMD GPU 上

Pre-training BERT using Hugging Face & PyTorch on an AMD GPU — ROCm Blogs 2024年1月26日&#xff0c;作者&#xff1a;Vara Lakshmi Bayanagari. 这篇博客解释了如何从头开始使用 Hugging Face 库和 PyTorch 后端在 AMD GPU 上为英文语料(WikiText-103-raw-v1)预训练…

Qgis 开发初级 《ToolBox》

Qgis 有个ToolBox 的&#xff0c;在Processing->ToolBox 菜单里面&#xff0c;界面如下。 理论上Qgis这里面的工具都是可以用脚本或者C 代码调用的。界面以Vector overlay 为例子简单介绍下使用方式。Vector overlay 的意思是矢量叠置分析&#xff0c;和arcgis软件类似的。点…