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
相关文章:
gateway 整合 spring security oauth2
微服务分布式认证授权方案 在分布式授权系统中,授权服务要独立成一个模块做统一授权,无论客户端是浏览器,app或者第三方,都会在授权服务中获取权限,并通过网关访问资源 OAuth2的四种授权模式 授权码模式 授权服务器将授…...
Unity3D学习FPS游戏(1)获取素材、快速了解三维模型素材(骨骼、网格、动画、Avatar、材质贴图)
前言:最近重拾Unity,准备做个3D的FPS小游戏,这里以官方FPS案例素材作为切入。 导入素材和素材理解 安装Unity新建项目新建文件夹和Scene如何去理解三维模型素材找到模型素材素材预制体结构骨骼和网格材质(Material)、…...
Eclipse Java 构建路径
Eclipse Java 构建路径 Eclipse 是一款广受欢迎的集成开发环境(IDE),特别适用于 Java 开发。在 Eclipse 中,构建路径(Build Path)是指编译器在编译项目时搜索类(.class)文件和源代码(.java)文件的路径。正确设置构建路径对于确保项目能够顺利编译和运行至关重要。 …...
FileLink跨网文件摆渡系统:重构跨网文件传输新时代
在数字化浪潮的推动下,企业对于数据的高效利用和安全管理提出了前所未有的要求。面对不同网络环境间的文件传输难题,传统方法往往显得力不从心,不仅效率低下,还存在极大的安全隐患。而FileLink跨网文件摆渡系统的出现,…...
macOS下QuickTime player+Blackhole录视频只录制系统声音
Blackhole是一个虚拟的音频驱动程序,免费的 安装方法: 方法1:通过homebrew安装 前提:你的系统中自己安装了homebrew,没有安装用方法2 系统终端执行下面的命令中的一个: brew install blackhole-2ch 或…...
Vscode + EIDE +CortexDebug 调试Stm32(记录)
{// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid830387"version": "0.2.0","configurations": [{"cwd": "${workspaceRoot…...
qt QApplication详解
一、概述 QApplication是Qt应用程序的基础类,负责设置和管理应用的环境。它的主要功能包括:初始化应用程序、管理事件循环、处理命令行参数、提供全局设置(如样式和调色板)以及创建和管理主窗口。通常在main函数中创建QApplicati…...
C++ 图像处理框架
在 C 中,有许多优秀的图像处理框架可以用来进行图像操作、计算机视觉、图像滤波等任务。以下是一些常用的 C 图像处理框架,每个框架都有其独特的特性和适用场景: 1. OpenCV OpenCV(Open Source Computer Vision Library…...
基于知识图谱的美食推荐系统
想象一下,每次打开应用,它都能为你量身推荐最符合你口味的美食,不需要再为“今天吃什么?”烦恼。这听起来是不是非常吸引人?今天就给大家介绍一个适合做毕业设计的创新项目——基于知识图谱的美食推荐系统!…...
记录:网鼎杯2024赛前热身WEB01
目录扫描,发现上传点,判断可能存在文件上传漏洞,并根据文件后缀判断网站开发语言为php 编写蚁剑一句话木马直接上传 蚁剑连接 这里生成 的flag是随机的,因为烽火台反作弊会随机生成环境,在一顿查找后,在hom…...
java 提示 避免用Apache Beanutils进行属性的copy。
避免用Apache Beanutils进行属性的copy。 Inspection info: 避免用Apache Beanutils进行属性的copy。 说明:Apache BeanUtils性能较差,可以使用其他方案比如Spring BeanUtils, Cglib BeanCopier。 TestObject a new TestObject(); TestObject b new Te…...
autMan框架对接Kook机器人
一、创建kook机器人 KOOK 二、获取机器人token 三、填写autMan参数并重启 四、将机器人加入服务器 五、效果图 回复...
RK3568平台(camera篇)UVC AICamera集成
一.客供AIcamera集成思路 主板端:目前在RK主板上预留了一个USB接口,使用USB接口来连接供应商提供的UVC摄像头。 供应商UVC摄像头:目前供应商提供的usbcamera是rv1126,基于usb接口跟主板端相连接。 其实使用供应商提供的camera,里面的大部分功能供应商已经是做好的,里面…...
【mod分享】极品飞车10魔改模组,全新UI,全新道路,全新建筑,高清植被,全新的道路围栏,全新的天空,体验另一种速度与激情
各位好,今天小编给大家带来一款新的高清重置魔改MOD,本次高清重置的游戏叫《极品飞车10卡本峡谷》。 《极品飞车10:卡本峡谷》该游戏可选择四个模式:生涯、快速比赛、挑战赛、多人连线游戏模式(已不可用)&…...
[实时计算flink]数据摄入YAML作业快速入门
实时计算Flink版基于Flink CDC,通过开发YAML作业的方式有效地实现了将数据从源端同步到目标端的数据摄入工作。本文介绍如何快速构建一个YAML作业将MySQL库中的所有数据同步到StarRocks中。 前提条件 已创建Flink工作空间,详情请参见开通实时计算Flink版…...
CMOS 图像传感器:像素寻址与信号处理
CMOS image sensor : pixel addressing and signal processing CMOS image sensor 对于寻址和信号处理有三种架构 pixel serial readout and processingcolumn parallel readout and processingpixel parallel readout and processing 其中,图 (b) column paralle…...
React Native 项目使用Expo模拟器运行iOS和Android
iOS没有连接设备: 确保你已经用 USB 线将你的 iOS 设备连接到了你的 Mac。 设备未信任: 如果你的设备是第一次连接到 Mac,可能需要在设备上信任这台计算机。通常,当你连接设备时,设备上会弹出一个对话框,…...
鸿蒙-键盘弹出时 promptAction.showToast 被遮盖
可以设置弹窗显示模式showMode,支持显示在应用之上。 参考代码: promptAction.showToast({ message: Message Info, duration: 2000, showMode: promptAction.ToastShowMode.TOP_MOST }); 文档中心...
十一、pico+Unity交互开发教程——手指触控交互(Poke Interaction)
一、XR Poke Interactor 交互包括发起交互的对象(Interactor)和可被交互的对象(Interactable)。XR Interaction Toolkit提供了XR Poke Interactor脚本用于实现Poke功能。在LeftHand Controller和RightHand Controller物体下创建名…...
Request2:Post请求和Json
百度翻译拿到自己想看的数据,下图查看请求到数据的请求 preview提前看下 取出对应的RequestUrl ,看出来要使用的话得用post请求 #!/usr/bin/env python # -*- coding:utf-8 -*- import requests import json if __name__ "__main__":#1.指定…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
