Spring Security Oauth2.1 最新版 1.1.0 整合 gateway 完成授权认证(基于 springboot 3.1)

news2024/11/28 10:59:53

目录

背景

版本

Spring Boot 3.1

Spring Authorization Server 1.1.0官方文档

基础

spring security

OAuth2.0

模块构成

授权方式

集成过程

官方demo

代码集成

依赖

授权服务AuthorizationServerConfig配置

重要组件

测试

查看授权服务配置

访问授权服务

授权

回调

获取 access_token

获取用户信息

个性化改造

集成GateWay

代办事项 

sql脚本


背景

基于 Spring Cloud Alibaba  架构下,需要一个统一授权中心,与 gateway 配合使用实现微服务的授权与认证,下面主要介绍整个集成过程,基于springboot3.1最新版

版本

Spring Boot 3.1

最新发布的springboot3.1版本对 oauth2 提供了默认的支持,可以引用下面的依赖来快速构建,为了体验新版本特性,我这边切换到了 3.1版本

Spring Boot 3.1 提供了一个 spring-boot-starter-oauth2-authorization-server 启动器,可以支持 Spring Authorization Server 的自动配置,轻松配置基于 Servlet 的 OAuth2 授权服务器,同时@EnableAuthorizationServer这些注解也早已废弃

Spring Authorization Server 1.1.0官方文档

Spring Authorization Server

基础

spring security

关于springsecurity的基础知识,之前写过一篇 springboot 与 Spring Security 集成的基于 jwt的授权的,可以看下面的

(296条消息) springboot 2.7整合spring security 5.7整合jwt实现用户登录注册与鉴权全记录_ricardo.M.Yu的博客-CSDN博客

OAuth2.0

 OAuth2.0可以提供一个统一的认证服务。主要模块如下:

模块构成

  • Resource owner(资源拥有者):拥有该资源的服务或用户,如我们自己或者资源网站
  • Authorization server(认证服务器):即用来认证与颁发令牌(如token)的服务
  • Resource server(资源服务器):拥有资源的服务,如我们要访问的网站
  • Client(客户端):即访问的客户端,如我们自己用的访问网站

授权方式

  • 授权码模式(authorization_code):最正规的模式,客户端先将用户导向认证服务器,登录后获取授权码,然后进行授权,最后根据授权码获取访问令牌
  • 刷新模式(refresh_token):用刷新码获取
  • 客户端模式(client_credentials):第三方应用自己本身需要获取资源

详见  AuthorizationGrantType 这个类

下面的密码模式已经被废弃

  • 密码模式(resource owner password credentials):直接带用户名和密码去向认证服务器申请令牌

集成过程

我下面会分为三个阶段逐次递进改造,

  • 第一阶段:官方demo演示与组件讲解测试
  • 第二阶段:个性化改造
  • 第三阶段:集成 springcloud gateway 完成分布式授权改造

官方demo

代码集成

依赖

只需要下面的这一个依赖,前提springboot 版本为 3.1

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.0</version>
    <relativePath/>
</parent>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>

授权服务AuthorizationServerConfig配置

spring 官方在快速开始里面给出了下面的默认最小配置,

Getting Started (spring.io)

我先粘下来再介绍,代码结构大概这样,一共两个配置类

 AuthorizationServerConfig


@Configuration
public class AuthorizationServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
            throws Exception {
        //针对 Spring Authorization Server 最佳实践配置
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());	// Enable OpenID Connect 1.0

        http
                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -> exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        )
                )
                // Accept access tokens for User Info and/or Client Registration
                .oauth2ResourceServer((resourceServer) -> resourceServer
                        .jwt(Customizer.withDefaults()));

        return http.build();
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("oidc-client")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .redirectUri("http://www.baidu.com")
                .redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
                .postLogoutRedirectUri("http://127.0.0.1:8080/")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope("message.read")
                .scope("message.write")
                .scope("all")
                // 设置 Client 需要页面审核授权
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();

        return new InMemoryRegisteredClientRepository(oidcClient);
    }

    /**
     * 默认发放令牌
     * @return
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }
}

DefaultSecurityConfig

@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class DefaultSecurityConfig {


    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .requestMatchers(new AntPathRequestMatcher("/actuator/**"),
                                new AntPathRequestMatcher("/oauth2/**"),
                                new AntPathRequestMatcher("/**/*.json"),
                                new AntPathRequestMatcher("/**/*.html")).permitAll()
                        .anyRequest().authenticated()
                )
                .cors(Customizer.withDefaults())
                .csrf((csrf) -> csrf.disable())
//                .httpBasic(Customizer.withDefaults())
//				// Form login handles the redirect to the login page from the
//				// authorization server filter chain
                .formLogin(Customizer.withDefaults())
        ;

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

}

分别介绍下这几个@Bean配置,也是 AuthorizationServer 的几个重要概念。

重要组件

  • SecurityFilterChain -> authorizationServerSecurityFilterChain: Spring Security的过滤器链,用于协议端点的。

  • SecurityFilterChain -> defaultSecurityFilterChain: Spring Security的过滤器链,用于Spring Security的身份认证

  • UserDetailsService :主要进行用户身份验证

  • RegisteredClientRepository:主要用于管理客户端

  • JWKSource:用于签名访问令牌

  • KeyPair: 启动时生成的带有密钥的KeyPair实例,用于创建上面的JWKSource

  • JwtDecoder:JwtDecoder的一个实例,用于解码已签名的访问令牌

  • AuthorizationServerSettings:用于配置Spring Authorization Server的AuthorizationServerSettings实例。

测试

为了方便测试,上面的配置中,客户端的回调地址我已经改成了 百度的,授权方式用授权码模式,认证方式用client_secret_basic

服务启动,端口为9000

查看授权服务配置

地址:

调用  http://127.0.0.1:9000/.well-known/openid-configuration

 后,查看地址配置如下:其实就是每个请求的url

详细的是下面

{

    "issuer": "http://127.0.0.1:9000",

    "authorization_endpoint": "http://127.0.0.1:9000/oauth2/authorize",

    "device_authorization_endpoint": "http://127.0.0.1:9000/oauth2/device_authorization",

    "token_endpoint": "http://127.0.0.1:9000/oauth2/token",

    "token_endpoint_auth_methods_supported": [

        "client_secret_basic",

        "client_secret_post",

        "client_secret_jwt",

        "private_key_jwt"

    ],

    "jwks_uri": "http://127.0.0.1:9000/oauth2/jwks",

    "userinfo_endpoint": "http://127.0.0.1:9000/userinfo",

    "end_session_endpoint": "http://127.0.0.1:9000/connect/logout",

    "response_types_supported": [

        "code"

    ],

    "grant_types_supported": [

        "authorization_code",

        "client_credentials",

        "refresh_token",

        "urn:ietf:params:oauth:grant-type:device_code"

    ],

    "revocation_endpoint": "http://127.0.0.1:9000/oauth2/revoke",

    "revocation_endpoint_auth_methods_supported": [

        "client_secret_basic",

        "client_secret_post",

        "client_secret_jwt",

        "private_key_jwt"

    ],

    "introspection_endpoint": "http://127.0.0.1:9000/oauth2/introspect",

    "introspection_endpoint_auth_methods_supported": [

        "client_secret_basic",

        "client_secret_post",

        "client_secret_jwt",

        "private_key_jwt"

    ],

    "subject_types_supported": [

        "public"

    ],

    "id_token_signing_alg_values_supported": [

        "RS256"

    ],

    "scopes_supported": [

        "openid"

    ]

}

访问授权服务

浏览器地址栏输入

http://localhost:9000/oauth2/authorize?response_type=code&client_id=oidc-client&scope=message.read openid&redirect_uri=http://www.baidu.com

用这个请求来模拟客户端,实际开发中,其实是先访问资源服务,由资源服务来拼接这几个参数来重定向到授权服务的,参数意义如下,这些参数都是需要再上面RegisteredClientRepository配置过的

  • response_type:这个意思是相应的方式为code码
  • client_id:即客户端的id,即上面配置中在 RegisteredClientRepository 配置的
  • scope:请求授权范围,也需要在上面的配置中
  • redirect_uri:授权通过后,重定向回来的地址

输入完上面的地址后,会重定向到下面这个登录页面,

我们输入上面配置好的用户名密码:

user

password

点击登录 

授权

登录过后,会到下面这个授权页面,点击授权范围,然后点击 submit

回调

授权通过后,授权服务回调到了百度的地址,然后附带这我们的授权码,如下图

获取 access_token

拿到授权码之后,可以用postman测试来获取 access_token

测试接口参数

Header

请求体 

http://localhost:9000/oauth2/token?grant_type=authorization_code&code=ajdNNIj8EiLjgw3OS8yu2q8n3XXCAb6cPY5LRsOHyRlAAB1ENKdmy8M4JBkJ8PrU-3K9QdpAZtyKg8QP5q0EHN2mR1k532FQUKz1ObSuH3EuSFy5LVzut9z1QVPuefoA&redirect_uri=http://www.baidu.com

curl命令如下

curl --location --request POST 'http://localhost:9000/oauth2/token?grant_type=authorization_code&code=a_lOQegEwElR09Sj6auVpBdYGgnhhK0uz1Uks286ei_zkbyDFKII2uf7gMIF7CU4cLN8ZEY3EsSq9jMAZ-Rmtmlq5pI6KPB95LMQg9fFirFg2wWjdd5PEwQLMEogY9B6&redirect_uri=http%3A%2F%2Fwww.baidu.com' \
--header 'Authorization: Basic b2lkYy1jbGllbnQ6c2VjcmV0'

 参数说明:

  • grant_type:即授权方式,authorization_code即授权码模式
  • code:即授权码,上面重定向到百度给我们的授权码
  • redirect_uri:重定向的url

  • header中的 Authorization参数:因为我们用的客户端认证方式 为  client_secret_basic ,这个需要传参,还有一些其他的认证方式,具体参数说明如下
  • client_secret_basic: 将 clientId 和 clientSecret 通过 ‘:’ 号拼接,( clientId 和 clientSecret 都在上面配置中,)并使用 Base64 进行编码得到一串字符,再在前面加个 注意有个 Basic   前缀(Basic后有一个空格), 即得到上面参数中的 Basic b2lkYy1jbGllbnQ6c2VjcmV0
  • client_secret_post :clientId 和 clientSecret 放到表单去发送请求。如下图:

 使用我们的 client_secret_basic 方式传参,接口调用结果:

已经正常拿到了 access_token。

完整的过滤器执行顺序,控制台输出

获取用户信息

获取用户信息接口为  /userinfo,注意需要有 opid 的授权范围,需要传参的值为 上面获取到的access_token,并在前面拼上  Bearer 

参数说明

Authorization:值格式为 Bearer + ${access_token}, 注意 Bearer  后面附带空格

curl命令

curl --location --request POST 'http://127.0.0.1:9000/userinfo' \
--header 'Authorization: Bearer eyJraWQiOiI4ZDc5YTIwNi1kOWZhLTQ5NWQtODJkMi1iMzk2MjQwNGQ4YmIiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiYXVkIjoib2lkYy1jbGllbnQiLCJuYmYiOjE2ODY3MzM4MTYsInNjb3BlIjpbIm9wZW5pZCIsIm1lc3NhZ2UucmVhZCJdLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjkwMDAiLCJleHAiOjE2ODY3MzQxMTYsImlhdCI6MTY4NjczMzgxNn0.AiGV5LIl8a4_7a7L2gbR61sjvHVLW4dZ6cElAwsWZnp-P7ocQT119KIASTPv138MU6ZK2_aF_-ER5FKaFQVSOj10Fy_Gv9PXa2ExrzTajfkPtA_t63jCcazzllaVWY4QIVD4fU8hPe6zDwjNOOX8R7hJFu2qtZ8V3bhzTlC0M4XWDAQ0goymYrAnVq8BR6hRm5-pY4nMCUZPFCeEFqGnl68EGRzosdSQeuRd-PtzB837i-C7lxqIjs4Y5hZ9mQw3R1zfa0WoP2KeN8K3WjyTIYd9PvrLIFCB5Zhj54sdNpZTy7wwC-oCVzwFFCEkgY-vprfgk4e4sZ10Lx60j--fHA' \
--header 'Cookie: JSESSIONID=7B10DA37A285902E4AEE4586AC181343'

效果如下:

默认返回的只有用户名,其他的数据,需要我们来重写一些东西获取

 过滤器执行链

BearerTokenAuthenticationFilter: 检验token

AuthenticationEntryPointFailureHandler

AuthenticationFailureHandler

OidcUserInfoEndpointFilter

个性化改造

正在改造中。。。

集成GateWay

正在集成中。。。

关于 Spring Cloud Alibaba 的基础环境搭建,可以看下面的文章,很详细,本文主要介绍集成OAuth2的过程

Spring Cloud Alibaba 最新版本整合完整使用及与各中间件集成(基于Spring Boot 3.0.x)_ricardo.M.Yu的博客-CSDN博客

代办事项 

现在需要做的改造如下:

1、新建授权服务,集成 oauth2-authorization-server ,即auth模块做授权中心

2、修改业务模块,集成 oauth2-client,即做资源中心与客户端

3、修改网关模块,即 gateway 做相关修改

Oauth2主要结构

OAuth2AuthorizationEndpointFilter: 针对 /login 或自行请求 授权码的处理器

OAuth2TokenEndpointFilter:针对获取 token 时的处理器

ProviderManager:

OAuth2ClientAuthenticationFilter

OAuth2TokenEndpointFilter

ClientSecretAuthenticationProvider

DelegatingAuthenticationConverter

OAuth2AuthorizationCodeAuthenticationProvider

OAuth2AuthorizationEndpointFilter

UsernamePasswordAuthenticationFilter

sql脚本

我直接整理好了

/*
IMPORTANT:
    If using PostgreSQL, update ALL columns defined with 'blob' to 'text',
    as PostgreSQL does not support the 'blob' data type.
*/
CREATE TABLE oauth2_authorization (
    id varchar(100) NOT NULL,
    registered_client_id varchar(100) NOT NULL,
    principal_name varchar(200) NOT NULL,
    authorization_grant_type varchar(100) NOT NULL,
    authorized_scopes varchar(1000) DEFAULT NULL,
    attributes blob DEFAULT NULL,
    state varchar(500) DEFAULT NULL,
    authorization_code_value blob DEFAULT NULL,
    authorization_code_issued_at timestamp DEFAULT NULL,
    authorization_code_expires_at timestamp DEFAULT NULL,
    authorization_code_metadata blob DEFAULT NULL,
    access_token_value blob DEFAULT NULL,
    access_token_issued_at timestamp DEFAULT NULL,
    access_token_expires_at timestamp DEFAULT NULL,
    access_token_metadata blob DEFAULT NULL,
    access_token_type varchar(100) DEFAULT NULL,
    access_token_scopes varchar(1000) DEFAULT NULL,
    oidc_id_token_value blob DEFAULT NULL,
    oidc_id_token_issued_at timestamp DEFAULT NULL,
    oidc_id_token_expires_at timestamp DEFAULT NULL,
    oidc_id_token_metadata blob DEFAULT NULL,
    refresh_token_value blob DEFAULT NULL,
    refresh_token_issued_at timestamp DEFAULT NULL,
    refresh_token_expires_at timestamp DEFAULT NULL,
    refresh_token_metadata blob DEFAULT NULL,
    user_code_value blob DEFAULT NULL,
    user_code_issued_at timestamp DEFAULT NULL,
    user_code_expires_at timestamp DEFAULT NULL,
    user_code_metadata blob DEFAULT NULL,
    device_code_value blob DEFAULT NULL,
    device_code_issued_at timestamp DEFAULT NULL,
    device_code_expires_at timestamp DEFAULT NULL,
    device_code_metadata blob DEFAULT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE oauth2_authorization_consent (
    registered_client_id varchar(100) NOT NULL,
    principal_name varchar(200) NOT NULL,
    authorities varchar(1000) NOT NULL,
    PRIMARY KEY (registered_client_id, principal_name)
);

CREATE TABLE oauth2_registered_client (
    id varchar(100) NOT NULL,
    client_id varchar(100) NOT NULL,
    client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
    client_secret varchar(200) DEFAULT NULL,
    client_secret_expires_at timestamp DEFAULT NULL,
    client_name varchar(200) NOT NULL,
    client_authentication_methods varchar(1000) NOT NULL,
    authorization_grant_types varchar(1000) NOT NULL,
    redirect_uris varchar(1000) DEFAULT NULL,
    post_logout_redirect_uris varchar(1000) DEFAULT NULL,
    scopes varchar(1000) NOT NULL,
    client_settings varchar(2000) NOT NULL,
    token_settings varchar(2000) NOT NULL,
    PRIMARY KEY (id)
);


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

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

相关文章

渗透测试与自动化安全测试工具比较

应用程序安全性并不新鲜&#xff0c;但它在需求、复杂性和深度方面正迅速增长。随着网络犯罪自疫情爆发以来增长了近600%&#xff0c;越来越多的SaaS企业开始争相保护他们的应用程序。即使那些运行最新端点保护的系统也面临重大漏洞。 然而随之而来的一个问题是&#xff1a;即…

【javaweb+springboot】旅游网页面设计(主购物车功能)——前后端分离+服务端客户端增删改查(完整代码+文档)

一、项目背景 由于疫情原因&#xff0c;张家界旅游业受到很大的影响&#xff0c;为了促进旅游业的发展&#xff0c;吸引更多游客来到张家界旅游&#xff0c;帮助游客更好地了解张家界&#xff0c;创建张家界旅游网&#xff0c;推进旅游发展大会的开展&#xff0c;展示当地风土人…

商城系统功能有哪些?

商城系统是一种以电子商务为基础的技术工具&#xff0c;为企业涉足电子商务提供了完整的解决方案。商城系统不仅可以帮助企业降低成本&#xff0c;提高效率&#xff0c;还可以实现全方位的在线营销&#xff0c;为企业争取更多的竞争优势&#xff0c;如SHOP、Magento等一系列成熟…

EBU5476 Microprocessor System Design 知识点总结_3 Assembly

Assembly 汇编语法。 顺序结构 label ; 可省略&#xff0c;用于跳转到此位置助记符 operand1, operand2, … ; CommentsMOV r1, #0x01 ; 数据0x01放入r1 MOV r1, #A ; 数据A的ascii码放入r1 MOV R0, R1 ; move R1 into R0 MOVS R0, R1 ; move R1 i…

当 GraphQL 遇上图数据库,便有了更方便查询数据的方式

人之初&#xff0c;性本鸽。 大家好&#xff0c;我叫储惠龙&#xff08;实名上网&#xff09;&#xff0c;你可以叫我小龙人&#xff0c;00 后一枚。目前从事后端开发工作。 今天给大家带来一个简单的为 NebulaGraph 提供 GraphQL 查询支持的 DEMO&#xff0c;为什么是简单的…

职业教育机构转线上时,选择平台要注意哪些方面?

职业教育是提升技能和知识的重要途径&#xff0c;有效的职业教育能够帮助培养和发展人才&#xff0c;相比较线下面授课程相比&#xff0c;在线直播的教学&#xff0c;可以节省较大成本&#xff0c;那么在选型直播平台时&#xff0c;要注意哪些方面呢&#xff1f; 1.需要实现高清…

记录一次使用__dirname和./引出的bug

JS项目中 保存本地生成的图片时使用的路径:__dirname“/waitToFinishTask.png"。 但是在获取这张图片的时候我使用的是“./waitToFinishTask.png”。 从而抛出异常&#xff1a;Error: ENOENT, No such file or directory ./waitToFinishTask.png 找了好久都不知道为什么会…

【无标题】windows下使用cmake编译c++

好久没有更新博客了 最近在做c相关的&#xff0c;编译起来确实很痛苦。 所以心血来潮&#xff0c;继续更新一下 主要还是一些跨平台的库&#xff0c;比如zlib、libpng、opencv、ffmpeg 编译工具使用mingw作为主要编译环境支持&#xff0c;使用msys进行编译。 一、下载mingw…

利用etcd实现分布式锁

python etcd3模块的lock使用 观察lock的加解锁影响 在python中已经自带了分布式锁的实现方式&#xff0c;下面我们尝试一下加锁与解锁的流程 在运行该demo同时也对lock对应的key进行watch&#xff0c;观察其变化&#xff0c;注意python-etcd3在实现分布式锁的时候&#xff0…

前端终止请求的三种方式(ajax、axios)

一、原生ajax终止请求 1、abort() ​ XMLHttpRequest.abort() 方法用于终止 XMLHttpRequest 对象的请求&#xff0c;该方法没有参数&#xff0c;也没有返回值。当调用该方法时&#xff0c;如果对应 XMLHttpRequest 对象的请求已经被发送并且正在处理中&#xff0c;则会中止该…

一个专科生的 Python 转行之路,虽然很难,但如今月薪1w,一切值得

一个专科生的 Python 转行之路&#xff0c;虽然很难&#xff0c;但如今月薪1w&#xff0c;一切值得 相信每个转 IT 的人, 大部分是兴趣驱动。然而我并不是, 只能说是不反感。一开始接触编程, 是一位同事&#xff0c;他会 java &#xff0c;也会一点前端。 印象最深刻的一次&…

前端如何处理「并发」问题?

&#x1f431; 个人主页&#xff1a;不叫猫先生&#xff0c;公众号&#xff1a;前端舵手 &#x1f64b;‍♂️ 作者简介&#xff1a;2022年度博客之星前端领域TOP 2&#xff0c;前端领域优质作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步…

Avalon 学习系列(四)—— 循环遍历

Avalon2 的 ms-for 绑定集齐了 ms-repeat, ms-each, ms-with 的所有功能&#xff0c; 更加好用&#xff0c; 性能也提升了很多。 Avalon 不需要 vue 或 react 那样使用 key 属性来提高性能&#xff0c;内部已经帮你搞定了。 循环数组 ms-for 循环数组示例&#xff1a; <…

运维圣经:勒索病毒应急响应指南

目录 勒索病毒简介 常见勒索病毒种类 WannaCry Globelmposter Crysis/ Dharma 攻击特点 应急响应方法指南 一. 隔离被感染的服务器/主机 二. 排查业务系统 三. 确定勒索病毒种类&#xff0c; 进行溯源分析 四. 恢复数据和业务 五. 清理加固 病毒清理及加固 感染文…

零基础也能懂的python办公自动化教程,从此上班摸鱼轻轻松松

前言 如今Python在自动化办公领域的表现越来越亮眼&#xff0c;受到了很多非IT的职场人士的推崇&#xff0c;也引得更多的人去了解、学习Python。但是很多初学者都会面临这么一个困惑&#xff1a;想把Python应用在工作中&#xff0c;却不知从何下手&#xff01;&#xff08;资…

【音视频处理】音频编码AAC详解,低码率提高音质?

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们介绍音频编码格式AAC。 AAC是音频最常用的编码格式之一&#xff0c;几乎所有的播放器都支持这个编码格式。 其他音频编码格式都是类似的&#xff0c;只是某些细节存在差别&#xff0c;如压缩算法、某些音频参数存在限制…

6个ChatGPT4的最佳用途

文章目录 ChatGPT 4’s Current Limitations ChatGPT 4 的当前限制1. Crafting Complex Prompts 制作复杂的提示2. Logic Problems 逻辑问题3. Verifying GPT 3.5 Text 验证 GPT 3.5 文本4. Complex Coding 复杂编码5.Nuanced Text Transformation 细微的文本转换6. Complex Kn…

100天精通Golang(基础入门篇)——第8天:Go语言程序的流程结构和条件语句

&#x1f337; 博主 libin9iOak带您 Go to Golang Language.✨ &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &#x1f30a; 《I…

【Unity入门】25.零基础实现游戏Demo--神鸟大战怪兽

【Unity入门】零基础实现游戏Demo--神鸟大战怪兽 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 (一) 前言 经过了两个月的学习&#xff0c;我们也顺利的完成了入门课程&#xff0c;最后就用一个Demo作为我们的结课句…

动态规划算法(子序列专题1)

动态规划算法专辑之子序列问题&#xff08;1&#xff09; 本专栏将从状态定义、状态转移方程、初始化、填表顺序、返回值这五大细节来详细讲述动态规划的算法的解题思路及代码实现 一、什么是子序列 子数列&#xff0c;又称子序列&#xff0c;在数学中&#xff0c;某个序列的…