SpringSecurity Oauth2 - 密码模式完成身份认证获取令牌 [自定义UserDetailsService]

文章目录

    • 1. 授权服务器
    • 2. 授权类型
      • 1. Password (密码模式)
      • 2. Refresh Token(刷新令牌)
      • 3. Client Credentials(客户端凭证模式)
    • 3. AuthorizationServerConfigurerAdapter
    • 4. 自定义 TokenStore 管理令牌
      • 1. TokenStore 的作用
      • 2. CustomAuthenticationKeyGenerator
      • 3. CustomRedisTokenStore
      • 4. TokenStoreAutoConfiguration
    • 5. 自定义 UserDetailsService 获取认证用户信息
      • 1. UserDetailsService 的作用
      • 2. CustomUserDetailService
      • 3. 密码加密配置类 PasswordEncodeConfig
      • 4. 配置 CustomUserDetailService
    • 6. 配置授权服务器
    • 7. 启动项目测试获取访问令牌

在这里插入图片描述

1. 授权服务器

Spring Security OAuth2 授权服务器的作用是为各种客户端应用(如Web应用、移动应用、微服务等)提供一个集中式的身份认证和授权服务。授权服务器的主要功能是颁发、管理和验证访问令牌(Access Token)和刷新令牌(Refresh Token),从而确保只有经过授权的客户端才能访问受保护的资源。

1.管理客户端应用

授权服务器允许你注册和管理不同的客户端应用程序(例如,Web应用、移动应用、API客户端)。对于每个客户端应用,授权服务器会为其分配一个唯一的客户端ID(Client ID)和客户端密钥(Client Secret),并定义其授权范围和访问权限。

2. 颁发访问令牌

授权服务器的核心功能之一是颁发访问令牌。当客户端应用请求访问受保护的资源时,它需要先向授权服务器请求一个访问令牌。授权服务器会根据预定义的授权流程(如授权码模式、密码模式等)验证客户端的身份和权限,然后颁发一个访问令牌给客户端。客户端可以使用这个访问令牌来访问资源服务器上的受保护资源。

3. 支持多种授权模式

① 密码模式:适用于用户信任的应用,如移动应用,直接使用用户名和密码获取访问令牌。

② 客户端凭据模式:适用于服务之间的通信,不涉及用户,直接使用客户端凭据获取访问令牌。

③ 刷新令牌:在访问令牌过期时,客户端可以使用刷新令牌获取新的访问令牌,避免用户重复登录。

4. 验证访问令牌

授权服务器不仅颁发访问令牌,还负责验证访问令牌的有效性。资源服务器在收到客户端请求时,可以通过调用授权服务器的令牌检查接口(如 /oauth/check_token)来验证访问令牌的合法性和有效期,从而确保请求者的身份和权限。

5. 管理用户身份和权限

授权服务器与用户身份管理系统(如用户数据库、LDAP等)集成,负责用户的认证和权限管理。当客户端应用请求访问令牌时,授权服务器会验证用户的身份,并根据用户的角色或权限,决定是否颁发访问令牌以及授予哪些权限。

2. 授权类型

OAuth2 授权框架提供了多种授权类型,允许客户端以不同的方式获取访问令牌。每种授权类型都有不同的使用场景和适用条件。

1. Password (密码模式)

密码模式适用于在信任的应用程序中直接向 OAuth2 授权服务器提供用户的用户名和密码。这种模式适用于移动应用或服务器端应用直接与授权服务器交互的场景。用户直接将用户名和密码提供给客户端,客户端使用这些凭据向授权服务器请求访问令牌。

使用步骤

① 用户提供用户名和密码: 用户将其用户名和密码输入到客户端应用中。

② 客户端请求访问令牌: 客户端使用用户名、密码、客户端ID和客户端密钥向授权服务器请求访问令牌。

POST /oauth/token HTTP/1.1
Host: authorization-server.com
Authorization: Basic Base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencodedgrant_type=password&username=user&password=pass&scope=read

③ 服务器返回访问令牌: 授权服务器验证凭据后,返回访问令牌。

{"access_token": "abcdefg12345","token_type": "bearer","expires_in": 3600,"refresh_token": "refresh12345"
}

2. Refresh Token(刷新令牌)

刷新令牌用于获取新的访问令牌,而无需用户重新进行认证。这种模式通常与其他授权模式结合使用,例如密码模式或授权码模式。当访问令牌过期后,需要获取新的访问令牌时使用刷新令牌。适用于需要长期保持用户会话的应用,例如 Web 应用或移动应用。

使用步骤

① 客户端使用刷新令牌请求新访问令牌: 当访问令牌过期时,客户端使用刷新令牌向授权服务器请求新的访问令牌。

POST /oauth/token HTTP/1.1
Host: authorization-server.com
Authorization: Basic Base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencodedgrant_type=refresh_token&refresh_token=refresh12345

② 服务器返回新的访问令牌: 授权服务器验证刷新令牌后,返回新的访问令牌。

{"access_token": "newabcdefg12345","token_type": "bearer","expires_in": 3600,"refresh_token": "newrefresh12345"
}

3. Client Credentials(客户端凭证模式)

客户端凭证模式不涉及用户,客户端自身以其身份请求访问令牌。这种模式常用于服务端与服务端之间的通信,例如 API 网关与微服务之间的通信。适用于应用之间的服务调用,通常在后台系统中使用。用于访问与用户无关的资源,或使用应用本身的权限访问资源。

使用步骤

① 客户端请求访问令牌: 客户端使用自己的客户端ID和客户端密钥向授权服务器请求访问令牌。

bash复制代码POST /oauth/token HTTP/1.1
Host: authorization-server.com
Authorization: Basic Base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencodedgrant_type=client_credentials&scope=read

② 服务器返回访问令牌: 授权服务器验证客户端凭证后,返回访问令牌。

{"access_token": "clienttoken12345","token_type": "bearer","expires_in": 3600
}

3. AuthorizationServerConfigurerAdapter

public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {}}

1. configure(AuthorizationServerSecurityConfigurer security)

用于配置授权服务器的安全性,如 /oauth/token/oauth/authorize 等端点的安全性配置:

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// 允许客户端表单身份验证security.allowFormAuthenticationForClients()// 允许所有人访问令牌验证端点.checkTokenAccess("permitAll()")// 仅允许认证后的用户访问密钥端点.tokenKeyAccess("isAuthenticated()");
}

2. configure(ClientDetailsServiceConfigurer clients) 方法

用于配置客户端详细信息服务,这个服务用来定义哪些客户端可以访问授权服务器以及客户端的配置信息。

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 将客户端信息存储在内存中clients.inMemory()// 定义客户端 ID.withClient("client-id")// 定义客户端密钥.secret(passwordEncoder.encode("client-secret"))// 定义客户端支持的授权模式.authorizedGrantTypes("authorization_code", "password", "refresh_token", "client_credentials")// 定义客户端的作用范围.scopes("read", "write")// 设置访问令牌的有效期.accessTokenValiditySeconds(3600)// 设置刷新令牌的有效期.refreshTokenValiditySeconds(7200);
}

3. configure(AuthorizationServerEndpointsConfigurer endpoints) 方法

用于配置授权和令牌的端点,以及令牌服务、令牌存储、用户认证等相关配置。

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// 配置用于密码模式的 `AuthenticationManager`endpoints.authenticationManager(authenticationManager)// 在刷新令牌时使用此服务加载用户信息.userDetailsService(userDetailsService)// 配置令牌的存储策略,例如内存、数据库或 Redis.tokenStore(new InMemoryTokenStore());
}

4. 自定义 TokenStore 管理令牌

1. TokenStore 的作用

TokenStore 是 Spring Security OAuth2 中用于管理 OAuth2 令牌(Access Token 和 Refresh Token)的接口。它的主要作用是定义如何生成、存储、读取和删除令牌。TokenStore 的具体实现类决定了令牌的存储方式,例如存储在内存中、数据库中、Redis 中,或以 JWT 的形式进行编码。TokenStore 的主要作用:

① 生成和存储令牌:当客户端请求令牌时,TokenStore 负责生成访问令牌和刷新令牌,并将它们存储在指定的存储介质中(如内存、数据库、Redis 等)。

② 读取令牌:TokenStore 允许根据令牌的值查找和读取存储的令牌。这个功能在资源服务器或授权服务器验证令牌时非常重要。

③ 删除令牌:TokenStore 也提供了删除令牌的方法,例如在用户注销或令牌过期时,授权服务器可以删除对应的访问令牌和刷新令牌。

④ 管理令牌的生命周期:TokenStore 负责管理令牌的生命周期,包括过期时间、刷新操作等。它可以确保访问令牌和刷新令牌在其有效期内使用,并在适当的时候自动过期。

当客户端请求令牌时,授权服务器通过 TokenStore 生成并存储令牌。客户端在后续请求中携带令牌访问受保护的资源时,资源服务器通过 TokenStore 验证令牌的有效性。TokenStore 管理整个令牌生命周期,包括生成、存储、读取、刷新和删除等操作。

@Bean
public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory) {return new RedisTokenStore(redisConnectionFactory);
}

2. CustomAuthenticationKeyGenerator

在 OAuth2 的认证过程中,DefaultAuthenticationKeyGenerator 通常用于生成唯一的认证键(AuthenticationKey),该键用于标识客户端的认证请求。默认情况下,DefaultAuthenticationKeyGenerator 会基于客户端的 ID、授权类型、作用域等信息生成一个哈希值,作为认证请求的唯一标识。

CustomAuthenticationKeyGenerator 类通过在原有的键生成逻辑上添加一个随机值 UUID.randomUUID().toString() 来确保每次调用时生成的键都是唯一的,即使所有其他参数都相同。这种方法增加了键的随机性,避免了重复的认证请求生成相同的键。

public class CustomAuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {private static final String RAND = "keyGeneratorRand";@Overrideprotected String generateKey(Map<String, String> values) {// 加入一个随机的要素,保证每次调用时生成的们的hash都不一样values.put(RAND, UUID.randomUUID().toString());return super.generateKey(values);}
}

3. CustomRedisTokenStore

CustomRedisTokenStore 是一个自定义的令牌存储类,继承自 RedisTokenStoreRedisTokenStore 是 Spring Security OAuth2 提供的一个实现,用于将 OAuth2 的访问令牌和刷新令牌存储在 Redis 中。

/*** 自定义的RedisTokenStore处理*/
public class CustomRedisTokenStore extends RedisTokenStore {public CustomRedisTokenStore(RedisConnectionFactory connectionFactory) {super(connectionFactory);}// 从 Redis 中读取并返回对应的访问令牌@Overridepublic OAuth2AccessToken readAccessToken(String tokenValue) {return super.readAccessToken(tokenValue);}// 从 Redis 中移除指定的访问令牌@Overridepublic void removeAccessToken(OAuth2AccessToken accessToken) {super.removeAccessToken(accessToken);}// 使用刷新令牌来删除关联的访问令牌@Overridepublic void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {super.removeAccessTokenUsingRefreshToken(refreshToken);}
}

4. TokenStoreAutoConfiguration

@Configuration
public class TokenStoreAutoConfiguration {@Autowiredprivate RedisConnectionFactory connectionFactory;@Beanpublic TokenStore tokenStore() {// 使用redis存储tokenRedisTokenStore redisTokenStore = new CustomRedisTokenStore(connectionFactory);redisTokenStore.setAuthenticationKeyGenerator(new CustomAuthenticationKeyGenerator());return redisTokenStore;}
}

5. 自定义 UserDetailsService 获取认证用户信息

1. UserDetailsService 的作用

UserDetailsService 是 Spring Security 中的一个核心接口,用于根据用户名获取用户的详细信息。这个接口通常用于处理用户身份验证过程中的用户查找逻辑。具体来说,当用户试图登录应用程序时,Spring Security 会使用 UserDetailsService 来加载用户信息(包括用户名、密码、权限等),以便进行身份验证。

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

UserDetailsUserDetailsService 返回的核心对象,包含了用户的详细信息:

public interface UserDetails extends Serializable {// 返回用户的权限(角色)集合Collection<? extends GrantedAuthority> getAuthorities();// 返回用户的密码(通常是加密后的)String getPassword();// 返回用户的用户名String getUsername();// 指示账户是否未过期,未过期的账户可使用。boolean isAccountNonExpired();// 指示账户是否未锁定,未锁定的账户可使用。boolean isAccountNonLocked();// 指示用户的凭据是否未过期,未过期的凭据可使用。boolean isCredentialsNonExpired();// 指示用户是否已启用,已启用的用户可使用。boolean isEnabled();
}

2. CustomUserDetailService

@Service
public class CustomUserDetailService implements UserDetailsService {@Autowiredprivate UserDao userDao;@Autowiredprivate PolicyDao policyDao;@Autowiredprivate RoleDao roleDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 从数据库中查找用户UserEntity userEntity = userDao.queryUserByUserName(username);if (userEntity == null) {throw new UsernameNotFoundException("User not found with username: " + username);}// 根据用户信息查询角色信息List<RoleEntity> roleEntities = roleDao.queryRolesByUserId(userEntity.getId());List<String> roleIds = roleEntities.stream().map(RoleEntity::getId).collect(Collectors.toList());// 根据角色信息查询权限信息List<PolicyEntity> policyEntities = policyDao.queryPolicyByRoleId(roleIds);// 查询权限名称List<String> policyNames = policyEntities.stream().map(PolicyEntity::getName).collect(Collectors.toList());// 构造认证用户权限信息List<SimpleGrantedAuthority> grantedAuthorities= policyNames.stream().map(policyName -> new SimpleGrantedAuthority(policyName)).collect(Collectors.toList());// 将 UserEntity 转换为 UserDetails 对象UserDetails userDetails = User.builder().username(userEntity.getUsername()).password(userEntity.getPassword()).authorities(grantedAuthorities).accountExpired(false).accountLocked(false).disabled(false).build();return userDetails ;}
}

3. 密码加密配置类 PasswordEncodeConfig

@Configuration
public class PasswordEncodeConfig {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

4. 配置 CustomUserDetailService

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomUserDetailService userDetailsService;@Autowiredprivate PasswordEncoder passwordEncoder;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);}@Overrideprotected void configure(HttpSecurity http) throws Exception {super.configure(http);}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

6. 配置授权服务器

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate TokenStore tokenStore;@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// 用于配置授权服务器的安全性,如 /oauth/token、/oauth/authorize 等端点的安全性配置。// 允许客户端表单身份验证security.allowFormAuthenticationForClients()// 允许所有人访问令牌验证端点.checkTokenAccess("permitAll()")// 仅允许认证后的用户访问密钥端点.tokenKeyAccess("isAuthenticated");}/*** 对于每个客户端应用,授权服务器会为其分配一个唯一的客户端ID和客户端密钥,并定义其授权范围和访问权限。*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 用于配置客户端详细信息服务,这个服务用来定义哪些客户端可以访问授权服务器以及客户端的配置信息。// 将客户端信息存储在内存中,适合开发和测试环境。clients.inMemory()// 定义客户端ID.withClient("client_id")// 定义客户端密钥.secret(passwordEncoder.encode("client_secret"))// 定义客户端支持的授权模式。.authorizedGrantTypes("password","refresh_token","client_credentials")// 设置访问令牌的有效期。.accessTokenValiditySeconds(3600)// 设置刷新令牌的有效期。.refreshTokenValiditySeconds(7200)// 定义客户端的作用范围。.scopes("all");}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// 用于配置授权和令牌的端点,以及令牌服务、令牌存储、用户认证等相关配置。// 配置用于密码模式的 AuthenticationManager。endpoints.authenticationManager(authenticationManager)// 在刷新令牌时使用此服务加载用户信息。.userDetailsService(userDetailsService)// 配置令牌的存储策略,例如内存、数据库或 Redis。.tokenStore(tokenStore);}
}

7. 启动项目测试获取访问令牌

在这里插入图片描述

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

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

相关文章

springweb获取请求数据、spring中拦截器

SpringWeb获取请求数据 springWeb支持多种类型的请求参数进行封装 1、使用HttpServletRequest对象接收 PostMapping(path "/login")//post请求//spring自动注入public String login(HttpServletRequest request){ System.out.println(request.getParameter("…

J.U.C Review - CopyOnWrite容器

文章目录 什么是CopyOnWrite容器CopyOnWriteArrayList优点缺点源码示例 仿写&#xff1a;CopyOnWriteMap的实现注意事项 什么是CopyOnWrite容器 CopyOnWrite容器是一种实现了写时复制&#xff08;Copy-On-Write&#xff0c;COW&#xff09;机制的并发容器。在并发场景中&#…

半导体产业核心环节有哪些?2024年中国半导体产业研究报告大揭秘!

半导体指常温下导电性能介于导体与绝缘体之间的材料。半导体应用在集成电路、消费电子、通信系统、光伏发电、照明应用、大功率电源转换等领域。半导体产业经济则是指以半导体产品为核心的经济活动&#xff0c;包括芯片设计、制造、封装测试及应用等。它是全球经济的支柱&#…

【mysql】mysql修改sql_mode之后无法启动

现象&#xff1a;修改后mysql无法启动&#xff0c;不报错 原因&#xff1a;MySQL在8以后sql_mode已经取消了NO_AUTO_CREATE_USER这个关键字。去掉这个关键字后&#xff0c;启动就可以了 修改前&#xff1a; sql_modeSTRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR…

在线演示文稿应用PPTist本地化部署并实现无公网IP远程编辑PPT

文章目录 前言1. 本地安装PPTist2. PPTist 使用介绍3. 安装Cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统环境本地部署开源在线演示文稿应用PPTist&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问与使用该项目。 PPTist …

C#编程语言及.NET 平台快速入门指南

Office Word 不显示 Citavi 插件&#xff0c;如何修复&#xff1f;_citavi安装后word无加载项-CSDN博客 https://blog.csdn.net/Viviane_2022/article/details/128946061?spm1001.2100.3001.7377&utm_mediumdistribute.pc_feed_blog_category.none-task-blog-classify_ta…

CSS选择器:一文带你区分CSS中的伪类和伪元素!

一、伪类选择器 1、什么是伪类选择器 伪类选择器&#xff0c;顾名思义&#xff0c;是一种特殊的选择器&#xff0c;它用来选择DOM元素在特定状态下的样式。这些特定状态并不是由文档结构决定的&#xff0c;而是由用户行为&#xff08;如点击、悬停&#xff09;或元素的状态&a…

Java SpringBoot构建传统文化网,三步实现信息展示,传承文化精髓

✍✍计算机毕业编程指导师** ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java…

大道至简,大厂官网基本都走简洁化设计路线。

「大道至简」是一种设计理念&#xff0c;强调设计应该追求简洁、直观、易用&#xff0c;而不是过多的修饰和繁琐的细节。 对于大厂的官网来说&#xff0c;简洁化设计路线的选择可能有以下几个原因&#xff1a; 1. 更好的用户体验&#xff1a; 简洁的设计可以让用户更容易地理…

NTFS硬盘支持工具Paragon NTFS for Mac 15.4.44 中文破解版

Paragon NTFS for Mac 15.4.44 中文破解版是一个底层的文件系统驱动程序,专门开发用来弥合Windows和Mac OS X之间的不兼容性&#xff0c;通过在Mac OS X系统下提供对任何版本的NTFS文件系统完全的读写访问服务来弥合这种不兼容性。为您轻松解决Mac不能识别Windows NTFS文件难题…

【深度学习】线性回归的从零开始实现与简洁实现

前言 我原本后面打算用李沐老师那本《动手学深度学习》继续“抄书”&#xff0c;他们团队也免费提供了电子版(https://zh-v2.d2l.ai/d2l-zh-pytorch.pdf)。但书里涉及到代码&#xff0c;一方面展示起来不太方便&#xff0c;另一方面我自己也有很多地方看不太懂。 这让我开始思…

Pepper佩盼尔wordpress模板

Pepper佩盼尔WordPress模板是一款专为追求简洁、现代和专业外观的网站设计者和开发者打造的高品质主题。它以简站为主题&#xff0c;强调“让建网站更简单”的理念&#xff0c;旨在为用户提供一个易于使用、功能丰富的平台来构建他们的在线业务或个人网站。 模板特点包括&…

手机玩黑神话悟空二周目 GameViewer远程助你手机畅玩黑神话悟空 解锁全成就全收集

用手机摸鱼完成黑神话悟空二周目全收集、成就全解锁&#xff0c;实现随时随地玩黑神话悟空&#xff0c;你可以用网易GameViewer远程。 这款远程控制软件专为游戏玩家打造&#xff0c;不管你是上班族&#xff0c;还是学生党&#xff0c;都可以用它在手机、平板上玩黑神话悟空&am…

谈一谈JVM的GC(垃圾回收)

JVM&#xff08;Java Virtual Machine&#xff09;的GC&#xff08;Garbage Collection&#xff0c;垃圾回收&#xff09;是Java语言的一个重要特性&#xff0c;它负责自动管理内存&#xff0c;释放那些不再被使用的对象所占用的内存空间。以下是对JVM GC的详细介绍&#xff1a…

风机设计基础

目录 1、风机分类按气体出口压力&#xff08;或升压&#xff09;来进行风机分类按风机全压来进行分类 2、风机定律及效率、功率、压力计算风机轴功率与扭矩关系&#xff1a;风机全压、静压效率计算公式&#xff1a;全压、动压、静压计算公式&#xff1a; 3、风机噪声1、离散噪声…

修改jupyter notebook 默认浏览器(不动配置文件,改系统默认浏览器)

最开始把联想浏览器切到EDGE就是用的修改系统的默认浏览器。不知怎么的现在搜到的方法都是在说修改配置文件&#x1f613;。 不想动配置文件&#xff0c;平时对默认浏览器没有特殊要求的&#xff0c;可以用这个方法。 这里是把默认浏览器改成联想浏览器&#xff0c;电脑也是联…

低代码平台赋能:烟花鞭炮企业数字化转型新篇章

随着数字化转型的浪潮席卷全球&#xff0c;传统制造业正面临着前所未有的变革机遇。烟花鞭炮行业&#xff0c;作为承载深厚文化底蕴与独特工艺的传统产业&#xff0c;也不例外。近年来&#xff0c;我国政府高度重视中小企业数字化转型&#xff0c;出台了一系列扶持政策&#xf…

基于大数据的电商平台电脑销售数据分析系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 随着电子商务的蓬勃发展&#xff0c;各大电商平台积累了海量的商品数据。如何从这些数据中提取有价值的信息&#xff0c;对于商家来说至关重要。本项目利用网络爬虫技术从京东电商平台采集各类品牌…

春秋云镜(OpenSSH)·CVE-2023-51385

靶标介绍&#xff1a; OpenSSH 是使用 SSH 协议进行远程登录的连接工具。在OpenSSH 9.6版本之前的ssh中&#xff0c;如果用户名或主机名中含有shell元字符&#xff08;如 | "等&#xff09;&#xff0c;并且ssh_config中ProxyCommand、LocalCommand指令或"match exe…

Python和Java及MATLAB和CUDA显微镜导图

&#x1f3af;要点 交互式设备控制和图像处理图像背景和阴影校正可视化萤光团位置算法和读取光学图像读写转换显微镜图像生物医学细胞图像分析荧光图像算法计算亮度数据和模拟表征新型染料和缓冲液强度估计细菌图像分析扫描透射和高分辨率透射图像模拟多模态成像分割可视化透射…