前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring Security 6.x 一文快速搞懂配置原理

Spring Security 6.x 一文快速搞懂配置原理

原创
作者头像
fullstackyang
修改于 2024-06-02 00:09:06
修改于 2024-06-02 00:09:06
1.4K0
举报

一、基本概念

Spring Security框架看似比较复杂,但说到底,框架中的各种安全功能,基本上也就是一个个Filter(javax.servlet.Filter)组成的所谓“过滤器链”实现的,这些Filter以职责链的设计模式组织起来,环环相扣,不过在刚接触Spring Security框架时不必盯着每个Filter着重去研究,我们首要的目的是学会如何对Spring Security进行配置,很多人,特别是新手,在看过官方文档中配置示例代码(如下所示)之后,在没有足够背景知识的情况下,都会对这个http.build()方法感到莫名的困惑,想要定制开发也不知从何下手,本文主要对整个Spring Security配置过程做一定的剖析,希望可以对学习Spring Sercurity框架的同学所有帮助。

版本说明:下文所贴出的各段源码均源自6.2.3版本,但其实5.7以上的各个版本,跟配置相关的代码基本相同,变动不算太大

代码语言:java
AI代码解释
复制
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(authorize -> authorize
                    .anyRequest().authenticated()
            )
            .formLogin(withDefaults())
            .httpBasic(withDefaults());
    return http.build();
}

概况地说,HttpSecurity的配置过程,主要就是向这个SecurityFilterChian中添加不同功能的Filter对象,为了方便后文理解,首先来看一下其中涉及的几个重要的接口和类(关系如下图)

接口

  • SecurityBuilder:顶层接口,定义了抽象的泛型构造器方法——build()
  • SecurityConfigurer:顶层接口,用来定义配置类的通用方法,每个Filter都是由特定的SecurityConfigurer的实现类构建出来并添加到FilterChain中的
  • SecurityFilterChain:顶层接口,即过滤器链,定义了获取List的方法,以及matches,用于判断某个请求是否满足进链的条件
  • HttpSecurityBuilder:继承SecurityBuilder,定义了构建SecurityFilterChain过程中的各种辅助方法,如添加Filter到SecurityFilterChain,获取SecurityConfigurer配置实现类等

  • AbstractSecurityBuilder:顶层的抽象父类,它没有实现build具体的逻辑,实际交由doBuild方法实现,只是用CAS对doBuild过程进行了并发控制
  • AbstractConfiguredSecurityBuilder:继承了AbstractSecurityBuilder,它内部维护了一个SecurityConfigurer的列表,实现了doBuild方法,确立了整个构建的流程
  • HttpSecurity 作为final实现类,它主要面向开发者,我们在开发过程中就是用它提供的一系列的配置入口,方便开发者对SecurityFilterChain中不同的Filter进行定制,包括添加自定义的Filter,关闭某些Filter,或扩展原来Filter的能力等等

二、基本流程

接下来,重点分析一下AbstractConfiguredSecurityBuilder类,整个构建过程围绕doBuild方法,主要分为:

  1. 初始化,包括beforeInit和init方法,其中beforeInit是扩展用的钩子方法,默认实现为空
  2. 配置,包括beforeConfigure和configure方法,其中beforeConfigure是扩展用的钩子方法,默认实现为空
  3. 构造,即performBuild方法,具体由HttpSecurity实现,主要是对Filter进行排序,并最终返回DefaultSecurityFilterChain的实例

而在AbstractConfiguredSecurityBuilder中维护了一个Map>对象,用于缓存各种SecurityConfigure的实现类,当调用init和configure时,实际上就会遍历这个Map所有configurer,依次调用对应方法,通常就是在configure方法中,将Filter加入到FilterChain中(下文详述)

代码语言:java
AI代码解释
复制
protected final O doBuild() throws Exception {
    synchronized (this.configurers) {
       this.buildState = BuildState.INITIALIZING;
       beforeInit();
       init();
       this.buildState = BuildState.CONFIGURING;
       beforeConfigure();
       configure();
       this.buildState = BuildState.BUILDING;
       O result = performBuild();
       this.buildState = BuildState.BUILT;
       return result;
    }
}
 
private void configure() throws Exception {
    Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
    for (SecurityConfigurer<O, B> configurer : configurers) {
       configurer.configure((B) this);
    }
}

那么,这些SecurityConfigure实例则是如何添加上述的Map中的?可以在HttpSecurityConfiguration找到相关的实现,源码如下

代码语言:java
AI代码解释
复制
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
    LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
    AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
          this.objectPostProcessor, passwordEncoder);
    authenticationBuilder.parentAuthenticationManager(authenticationManager());
    authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
    HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
    WebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = new WebAsyncManagerIntegrationFilter();
    webAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
    // @formatter:off
    http
       .csrf(withDefaults())
       .addFilter(webAsyncManagerIntegrationFilter)
       .exceptionHandling(withDefaults())
       .headers(withDefaults())
       .sessionManagement(withDefaults())
       .securityContext(withDefaults())
       .requestCache(withDefaults())
       .anonymous(withDefaults())
       .servletApi(withDefaults())
       .apply(new DefaultLoginPageConfigurer<>());
    http.logout(withDefaults());
    // @formatter:on
    applyCorsIfAvailable(http);
    applyDefaultConfigurers(http);
    return http;
}

可以看到在构造过程中(那段很长链式配置),已经帮我们添加了若干SecurityConfgurer实例,因此我们在使用配置SecurityFilterChain时,仅需要很少的配置就可以得到一个完整的具备基本功能的SecurityFilterChain,当然我们也可以利用这些配置项做很多定制开发。事实上,HttpSecurity大约提供了24个Filter相关的Configurer配置方法,其中11个Filter是默认加载的,整理成表格:

序号

方法

对应Filter

作用

1

headers

HeaderWriterFilter(默认加载)

用于添加Security HTTP header到response中,例如X-Frame-Options

2

cors

CorsFilter(通常在Spring MVC环境时默认加载)

用于支持跨域请求

3

sessionManagement

SessionManagementFilterConcurrentSessionFilterDisableEncodeUrlFilter(默认生效)ForceEagerSessionCreationFilter

用于管理session,如保存session,并发控制等操作

4

jee

J2eePreAuthenticatedProcessingFilter

用于支持Java EE 容器预认证

5

x509

X509AuthenticationFilter

用于支持X.509证书预认证,通常是指在浏览器中使用HTTPS协议

6

rememberMe

RememberMeAuthenticationFilter

用于在登录时记录用户登录信息,以保持登录态

7

authorizeHttpRequests

AuthorizationFilter

用于实现授权访问的逻辑

8

requestCache

RequestCacheAwareFilter(默认加载)

用于实现用户登录之后,跳转回登录之前请求的地址

9

exceptionHanding

ExceptionTranslationFilter(默认加载)

用于配置异常处理˛

10

securityContext

SecurityContextHolderFilter(默认加载)SecurityContextPersistenceFilter(旧版本,现不推荐使用)

用于加载用户的登录态信息,并保存在SecurityContextHolder中

11

servletApi

SecurityContextHolderAwareRequestFilter(默认加载)

用于将HttpServletRequest包装为Servlet3SecurityContextHolderAwareRequestWrapper,方便其他Filter使用

12

csrf

CsrfFilter(默认加载)

用于防范跨站请求伪造(CSRF)攻击

13

logout

LogoutFilter(默认加载)

用于实现登出注销逻辑

14

anonymous

AnonymousAuthenticationFilter(默认加载)

用于实现匿名登录逻辑

15

formLogin

UsernamePasswordAuthenticationFilter

用于实现用户名密码登录逻辑

16

saml2Login

Saml2WebSsoAuthenticationFilter

用于实现SAML 2.0认证协议登录逻辑

17

saml2Logout

Saml2LogoutRequestFilterSaml2LogoutResponseFilterSaml2RelyingPartyInitiatedLogoutFilter

用于实现SAML 2.0认证协议登出逻辑

18

oauth2Login

OAuth2AuthorizationRequestRedirectFilter

用于接入OAuth2.0认证协议登录逻辑,例如Github登录

19

oidcLogout

OidcBackChannelLogoutFilter

用于实现OIDC认证协议登出逻辑

20

oauth2Client

OAuth2AuthorizationRequestRedirectFilterOAuth2AuthorizationCodeGrantFilter

用于实现OAuth2.0客户端逻辑,例如授权码模式等

21

oauth2ResourceServer

BearerTokenAuthenticationFilter

用于实现OAuth2.0服务端逻辑

22

requiresChannel

ChannelProcessingFilter

用于判断哪些资源需要被保护

23

httpBasic

BasicAuthenticationFilter

用于实现HTTP基础认证的逻辑

24

passwordManagement

RequestMatcherRedirectFilter("/change-password")

用于实现在需要修改密码时,跳转页面的逻辑

25

HttpSecurityConfiguraion直接添加

WebAsyncManagerIntegrationFilter(默认加载)

用于在异步线程中,支持通过SecurityContextHolder获取认证对象Authentication

如果我们不对HttpSecurity做任何改动的话,默认得到的SecurityFilterChain是如下这样的,先了解大概,后续还针对部分重要的filter做深入分析。

代码语言:txt
AI代码解释
复制
org.springframework.security.web.session.DisableEncodeUrlFilter
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextHolderFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.web.filter.CorsFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.access.ExceptionTranslationFilter

三、SecurityConfigurer举例

这里还是以Spring Security官方文档中配置的示例代码为例,配置代码只需几行,比较优雅,这种设计是值得学习的,尽量让复杂的配置逻辑封装起来,让开发者在使用时,只需要关注业务逻辑即可

代码语言:java
AI代码解释
复制
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(authorize -> authorize
                    .anyRequest().authenticated()
            )
            .formLogin(withDefaults())
            .httpBasic(withDefaults());
    return http.build();
}

Spring Security提供了两种方式进行配置,一种就是示例代码中,即利用lambda表达式实现配置逻辑,这是5.5版本引入的,在这之前是使用无参的方法获取配置对象 ,然后进行链式的配置,如上述示例代码可以改写为

代码语言:java
AI代码解释
复制
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.
            authorizeHttpRequests().anyRequest().authenticated()
            .and().formLogin()
            .and().httpBasic();
    return http.build();

}

以authorizeHttpRequests为例看一下源码实现,可以看到两种方式大同小异,只是使用了Customer函数式接口进行了封装

代码语言:java
AI代码解释
复制
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizeHttpRequests()
       throws Exception {
    ApplicationContext context = getContext();
    return getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry();
}

public HttpSecurity authorizeHttpRequests(
       Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
       throws Exception {
    ApplicationContext context = getContext();
    authorizeHttpRequestsCustomizer
       .customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());
    return HttpSecurity.this;
}

其中getOrApply方法,用于获取到Configurer的具体实例(上文中提到过在AbstractConfiguredSecurityBuilder中维护了一个Configurers的Map,这些Configurer实例便是从这个Map获取的)

不过第二种写法,根据源码的注释,应该会在Spring Security 7版本里面移除,所以还是要适应这种Customizer参数的配置方法。

上面authorizeHttpRequests方法返回的是AuthorizeHttpRequestsConfigurer类中的AuthorizationManagerRequestMatcherRegistry对象,可以先看一下AuthorizeHttpRequestsConfigurer中的configure方法

代码语言:java
AI代码解释
复制
public void configure(H http) {
    AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
    AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
    authorizationFilter.setAuthorizationEventPublisher(this.publisher);
    authorizationFilter.setShouldFilterAllDispatcherTypes(this.registry.shouldFilterAllDispatcherTypes);
    authorizationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
    http.addFilter(postProcess(authorizationFilter));
}

这里创建了一个AuthorizationFilter,并添加到HttpSecurity的List中,而AuthorizationManagerRequestMatcherRegistry则又是一个构造器模式实现的配置类,主要功能就是配置一些权限拦截的具体逻辑,如哪些地址需要什么角色访问等,这里就不展开了。

再看一下formLogin的例子

代码语言:java
AI代码解释
复制
public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {
    formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));
    return HttpSecurity.this;
}

formLogin方法实际上创建FormLoginConfigurer的示例,该类主要用于创建UsernamePasswordAuthenticationFilter,即默认的用户名密码认证的过滤器。

其中的configure方法由父类AbstractAuthenticationFilterConfigurer实现,源码如下,虽然这个方法有点长,但基本是围绕配置UsernamePasswordAuthenticationFilter实例而展开(this.authFilter就是UsernamePasswordAuthenticationFilter的实例,它在FormLoginConfigurer的构造函数中创建出来),主要就是创建用户认证所用到的一些基本组件,例如AuthenticationManager用于封装不同的用户认证方式(如用户名密码),AuthenticationSuccessHandler用于封装认证成功后执行的操作,AuthenticationFailureHandler用于封装认证失败后执行的操作等等

代码语言:java
AI代码解释
复制
@Override
public void configure(B http) throws Exception {
    PortMapper portMapper = http.getSharedObject(PortMapper.class);
    if (portMapper != null) {
       this.authenticationEntryPoint.setPortMapper(portMapper);
    }
    RequestCache requestCache = http.getSharedObject(RequestCache.class);
    if (requestCache != null) {
       this.defaultSuccessHandler.setRequestCache(requestCache);
    }
    this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
    this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
    if (this.authenticationDetailsSource != null) {
       this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
    }
    SessionAuthenticationStrategy sessionAuthenticationStrategy = http
       .getSharedObject(SessionAuthenticationStrategy.class);
    if (sessionAuthenticationStrategy != null) {
       this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
    }
    RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
    if (rememberMeServices != null) {
       this.authFilter.setRememberMeServices(rememberMeServices);
    }
    SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
    if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
       SecurityContextRepository securityContextRepository = securityContextConfigurer
          .getSecurityContextRepository();
       this.authFilter.setSecurityContextRepository(securityContextRepository);
    }
    this.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
    F filter = postProcess(this.authFilter);
    http.addFilter(filter);
}

通过上面两个源码示例,可以看到配置Filter的过程其实并不复杂,当我们在研究Spring Security不同过滤器功能时,可以参考源码中configure的配置过程,分析它们有哪些配置项,这些配置点主要能提供什么样能力的等,从而打开思路,快速实现不同的定制需求。

四、总结

最后做一个简单的总结,如图所示:

  1. 从HttpSercurityConfiguration定义HttpSecurity的Bean对象开始,便向HttpSecurity中添加了若干SecurityConfigurer对象,另外我们可以在自定义的配置类中对其进行一些定制调整
  2. 然后当调用HttpSecurity#Build()方法时,就会将取得所有SecurityConfigurer进行遍历,依次调用对应的init和configurer方法,而在configurer方法中,创建出各种功能的Filter实例,并添加到List列表中
  3. 最后通过performBuild方法,将List进行排序,并创建出DefaultSecurityFilterChian

至此整个过滤器链的构建就完成了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
CVPR 2020 论文大盘点-实例分割篇
本文盘点CVPR 2020 所有实例分割(Instance Segmentation)相关论文(语义分割在这里,不含全景分割、医学图像分割、交互式分割等,以上将会另行总结),总计18篇,对文献进行了分类汇总,希望对大家有帮助。
CV君
2020/06/28
9720
CVPR 2020 论文大盘点-实例分割篇
CVPR 2020 论文大盘点-全景分割与视频目标分割篇
今天的内容关注于全景分割(Panoptic Segmentation)和视频目标分割(Video Object Segmentation,VOS)。
CV君
2020/06/28
9700
CVPR 2020 论文大盘点-全景分割与视频目标分割篇
CVPR 2020 论文大盘点-人体姿态估计与动作捕捉篇
本文盘点CVPR 2020 所有人体姿态估计(Human Pose Estimation)、手势识别(Gesture Recognition)、人体形状与姿态估计(Human Shape and Pose Estimation)、人体运动捕捉(Human Motion Capture)相关论文,总计27篇,对文献进行了分类汇总,希望对大家有帮助。
CV君
2020/06/28
3.6K0
CVPR 2020 论文大盘点-人体姿态估计与动作捕捉篇
CVPR 2020 论文大盘点—目标跟踪篇
最大的特点是目标跟踪技术正在向目标的像素级跟踪(video object Segmentation 视频目标分割)发展,有7篇相关文献;另外更加实用的多目标跟踪(Multi-Object Tracking)也很受关注,有7篇文章;大家常见的单目标跟踪有10篇,自动驾驶中3D目标跟踪有3篇,其他细分的跟踪详见下文。
CV君
2020/06/19
1.2K0
CVPR 2020 论文大盘点-行人检测与重识别篇
行人检测的论文不多,总计 5 篇,从内容看解决行人与行人、行人与物体间的遮挡是研究的重点。
CV君
2020/06/19
2.1K0
CVPR 2020 论文大盘点-文本图像篇
本文盘点CVPR 2020 所有文本图像(text)相关论文,主要分为手写文本和场景文本两大方向,总计16篇,对文献进行了细致的分类,大部分论文是围绕识别问题的研究。
CV君
2020/06/28
9260
CVPR 2020 论文大盘点-文本图像篇
CVPR 2020 论文大盘点-超分辨率篇
这21篇文章中方向以做图像超分辨率居多有10篇,视频超分辨率3篇,人脸超分辨率2篇,特定领域超分辨率即深度图、光场、高光谱图像超分辨率各1篇,零样本超分辨率1篇,探索专用于超分辨率的数据增广方法的1篇,最后还有一篇超分辨率技术的有趣应用:语义分割1篇。
CV君
2020/06/19
1.3K0
推荐几篇新出的 CVPR 2021开源论文,含图像分割、域适应、图像检索、视线估计等
本文分享几篇近期值得关注的 CVPR 2021 的开源论文,包括图像到图像翻译、全景分割、语义分割、域适应、图像检索、无监督学习,以及一篇关于首次对从人们相互注视的图像/视频中学习弱监督的三维视线范式(LAEO)的研究。
CV君
2021/06/08
1.2K0
推荐几篇新出的 CVPR 2021开源论文,含图像分割、域适应、图像检索、视线估计等
CVPR 2019 论文大盘点—文本图像相关篇
其中研究文本检测的最多,共 7 篇,包括已经非常知名的PSENet,还有最近异常火爆的CRAFT。
CV君
2019/12/27
6720
CVPR 2020 论文大盘点-人脸技术篇
本文盘点CVPR 2020 所有人脸相关论文,总计64篇,涵盖方向众多,传统方向比如人脸识别、检测等依旧是研究的重点,除此之外,人脸生成、编辑,人脸反欺骗也有众多工作,一方面是研究如何造假脸,一方面是如何识别造假。也是蛮有意思的。
CV君
2020/06/15
1.9K0
CVPR 2020 论文大盘点-语义分割篇
图像分割应用广泛,在CVPR 2020 论文中所占比例很高,可说是一大热门,有110多篇相关论文,本文盘点CVPR 2020 所有语义分割(Semantic Segmentation)相关论文(不含实例分割、全景分割、医学图像分割、交互式分割等,以上将会另行总结),总计37篇,对文献进行了分类汇总,希望对大家有帮助。
CV君
2020/06/28
1.9K0
CVPR 2020 论文大盘点-语义分割篇
CVPR 2020 论文大盘点-遥感与航拍影像处理识别篇
本文总结CVPR 2020 中所有遥感与航拍(Remote Sensing、Aerial Image)影像处理识别相关论文,总计 18 篇。
CV君
2020/07/09
1.5K0
CVPR 2020 论文大盘点-图像修复Inpainting篇
图片来自 https://sites.google.com/site/gachoncvip/projects/image-inpainting
CV君
2020/07/03
1.8K0
ECCV 2020 谷歌论文盘点—Poster 篇
今天盘点Poster中的谷歌论文,总计27篇,从这些论文中可看出,谷歌很重视自动驾驶,多篇论文为自动驾驶领域,目标检测、NAS、数据增广方法等也是研究的重点。
CV君
2020/09/15
8710
CV Code|计算机视觉开源周报20200501期
[1]Streaming Object Detection for 3-D Point Clouds
CV君
2020/05/20
7190
ECCV 2020论文大盘点-3D目标检测篇
[1].Generative Sparse Detection Networks for 3D Single-shot Object Detection
小白学视觉
2020/09/30
7820
ECCV 2020论文大盘点-3D目标检测篇
CVPR 2020 论文大盘点-光流篇
图片来自 https://github.com/PharrellWANG/optical-flow-visualization
CV君
2020/07/13
1.6K0
CVPR 2020 论文大盘点-目标检测篇
LiDAR-Based Online 3D Video Object Detection With Graph-Based Message Passing and Spatiotemporal Transformer Attention
CV君
2020/06/19
1.6K0
ECCV 2020 Spotlight 谷歌论文大盘点
本文继续盘点谷歌 Spotlight 中论文,看看工业界巨头都在关注什么方向,取得了哪些突破。该部分论文总计 12 篇,
CV君
2020/09/15
7150
CVPR 2020 论文大盘点-图像与视频检索篇
本文总结CVPR 2020 中所有与图像和视频检索(Image and Video Retrieval)相关论文,总计 16 篇。
CV君
2020/07/09
1.3K0
推荐阅读
相关推荐
CVPR 2020 论文大盘点-实例分割篇
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档