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

news2024/11/25 18:51:39

目录

背景

版本

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/647554.html

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

相关文章

简单实用的快递查询工具,支持批量查询

大家如果遇到快递一直没到的情况&#xff0c;是不是会很心急呢&#xff1f;这时候就要去查询一下快递具体的运输轨迹了&#xff0c;看看自己的快递运输到哪里了。今天小编给大家推荐一款快递信息查询软件&#xff1a;“固乔快递查询助手”&#xff0c;这款软件能够自动识别单号…

管理类联考——英语——知识篇——考研英语熟词生义

考研英语熟词生义 A 1.abandon【生义】放松&#xff0c;沉溺 2.abide【【生义】容忍&#xff0c;经受住 3.abroad【生义】广为流传&#xff0c;传开 4.abrupt【生义】陡峭的&#xff0c;险峻的 5.absent【生义】漫不经心的 6.absolute【【生义】基本的 7.absorb【生义】…

Redis 发布和订阅详细介绍

发布和订阅 发布和订阅是什么 一句话&#xff1a;Redis 发布订阅(pub/sub) 是一种消息通信模式&#xff1a;发送者(pub) 发送消息&#xff0c;订阅者(sub) 接收消息 Redis 客户端可以订阅任意数量的频道 一图胜千言 1、客户端订阅频道示意图 2、当给这个频道发布消息后&am…

[CKA]考试之网络策略NetworkPolicy

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 在现有的namespace fubar中创建一个名为allow-port-from-namespace的新Netwo…

云原生时代崛起的编程语言Go远程调用gRPC实战

文章目录 概述定义背景特点四种服务方法 实战环境配置proto文件简单RPCToken认证服务器流式RPC客户端流式RPC双向流式RPC 概述 定义 gRPC 官网地址 https://grpc.io/ 源码release最新版本v1.55.1 gRPC 官网文档地址 https://grpc.io/docs/ gRPC 源码地址 https://github.com/g…

【vue3】13-前端路由-Vue-Router的详解: 从入门到掌握

前端路由 前端路由的发展历程认识前端路由&#xff1a;后端路由阶段前后端分离阶段 Vue-Router的使用vue-router的基本使用vue-router的使用过程基本使用的细节补充 路由懒加载分包处理路由的其他属性动态路由路由嵌套路由的编程式导航动态管理路由对象路由导航守卫钩子 前端路…

【毕业N年系列】 毕业第五年

距离上一篇 【毕业N年系列】已经过去一年时间了。没想到自己当初的一个简单的想法竟然能坚持到第五年&#xff0c;希望自己能坚持下去吧&#xff0c;在耄耋古稀之年依然能回看自己的人生。当然本来想着今天能早点下班回来写这篇文章&#xff0c;显然又没有实现&#xff0c;加班…

termux安卓手机 无线adb调试安卓手机

调试机安转termux app Apk下载 安装 https://f-droid.org/packages/com.termux 打开termux 挂载内部存储到~/storage 目录 $ temux-setup-storage 换源加快下载与更新速度 termux-change-repo 选 Main 然后 清华 下载基础软件 pkg install git vim perl nodejs-lts wget…

竞赛无人机搭积木式编程(三)---用户自定义航点自动飞行功能(全局定位,指哪打哪)

竞赛无人机搭积木式编程&#xff08;三&#xff09;---用户自定义航点自动飞行功能&#xff08;全局定位&#xff0c;指哪打哪&#xff09; 无名小哥 2023年6月10日 用户通过对前面两讲中全国大学生电子设计竞赛真题植保无人机&#xff08;2021&#xff09;、送货无人机&#…

新人如何学习性能测试?一文5个步骤带你入门性能测试!

写在前面&#xff1a;测试岗位细化后分为很多&#xff0c;功能测试、性能测试、自动化测试、测试开发&#xff0c;这些都叫测试。性能测试在所有测试分工中&#xff0c;地位和薪资都比较高&#xff0c;可见下方图片&#xff1a; 中级第3条&#xff0c;高级第1条&#xff0c;都是…

深度学习入门——感知机

1 感知机是什么 感知机接收多个输入信号&#xff0c;输出一个信号。这里所说的“信号”可以想象成电流或河流那样具备“流动性”的东西。像电流流过导线&#xff0c;向前方输送电子一样&#xff0c;感知机的信号也会形成流&#xff0c;向前方输送信息。但是&#xff0c;和实际…

Java集合-Collection Map

文章目录 概念IterableCollection接口List接口ArrayListVectorLinkedListArrayList 和 LinkedList 比较 SetHashSet LinkedHashSetMapHashMap 概念 1.集合主要是两组:单列集合(Collection) , 双列集合(Map) 2.Collection 接口有两个重要的子接口 List ,Set . 他们的实现子类都…

SWIG介绍和使用

官网&#xff1a;https://www.swig.org/ github&#xff1a;https://github.com/swig SWIG 是一种软件开发工具&#xff0c;可将用 C 和 C 编写的程序与各种高级编程语言连接起来。 SWIG 与不同类型的目标语言一起使用&#xff0c;包括常见的脚本语言&#xff0c;如 Javascri…

redis集群-Redis(六)

上篇文章介绍了哨兵架构以及集群搭建。 哨兵架构&redisCluster-Redis&#xff08;五&#xff09;https://blog.csdn.net/ke1ying/article/details/131178880 redisCluster 接着上篇文章继续讲解&#xff0c;前面一个linux服务器已经有了8001和8004节点&#xff0c;现在继…

八大典型的黑盒测试方法已来袭,快快接住!

前言 有了软件缺陷的暴露&#xff0c;我们就需要通过各种软件测试的方法来查找出软件的漏洞&#xff0c;编写出测试用例&#xff0c;及时修改bug。 在下面的这篇文章中&#xff0c;我们将谈论八大典型的黑盒测试方法&#xff0c;一起来学习⑧&#x1f4a1; 一、等价类划分法…

【Python实战】Python采集王者皮肤图片

前言 我们上一篇介绍了&#xff0c;如何采集王者最低战力&#xff0c;本文就来给大家介绍如何采集王者皮肤&#xff0c;买不起皮肤&#xff0c;当个桌面壁纸挺好的。下面&#xff0c;我和大家介绍如何获取数据。 环境使用 python 3.9pycharm 模块使用 requests 模块介绍 re…

c++返回值使用引用的注意事项

c返回值使用引用的注意事项 问题原因返回函数的引用形参另外一个问题解决方法 问题 引用变量b接受到的值为无意义的数字。 原因 int& b returnYingyong();相当于&#xff1a; int i2 3; int& b i2; 销毁 i2;将局部变量作为返回值&#xff0c;导致引用b数据无意义…

【LInux】进程间通信(共享内存)

文章目录 system V共享内存shmget&#xff08;创建共享内存&#xff09;ftok&#xff08;key&#xff09;ipcs指令shmctl&#xff08;销毁共享内存&#xff09;shmat代码 共享内存 system V共享内存 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间&am…

新能源突围,需要发力被忽视的中端品牌战场

作者 |张祥威 编辑 |德新 今年2月&#xff0c;受特斯拉等竞品降价影响&#xff0c;深蓝SL03销量下滑至4000台。深蓝的市场部上报长安中国&#xff0c;十天后开始降价&#xff0c;3月SL03销量重回正轨。 在巨头和新势力之间灵活腾挪&#xff0c;深蓝向外界展示了爆款制造的素质…

网络安全知识点梳理(期末不挂科)

网络安全复习知识点太多梳理不清晰&#xff1f;&#x1f44f;&#x1f3fb;&#x1f44f;&#x1f3fb;&#x1f44f;&#x1f3fb; 本文一篇帮你梳理清晰&#xff0c;内容覆盖整个大学网络安全知识点&#x1f44c;&#x1f3fb; 干货满满不看后悔&#x1f44d;&#x1f44…