SpringSecurity | spring security oauth2.0 配置源码分析(二)
2018-01-03 18:50
731 查看
继上一篇《SpringSecurity | spring security oauth2.0 配置源码分析(一)》简单的分析配置之后,今天从源码的角度来分析配置是如何生效的,Oauth2.0如何和 Spring Security 整合的。
1)先看下Spring Security中 HttpSecurity配置:
在上一篇配置讲解中,我们提到了oauth2两个注解配置:
继续跟进这两个注解,找到两个关键类:
AuthorizationServerSecurityConfiguration,ResourceServerConfiguration
这两个类是 spring security接口SecurityConfigurer的子类,在项目启动的时候,spring security 会初始化SecurityConfigurer的子类,所以在加载自身配置 WebSecurityConfig 的同时,会将这两个oauth2的配置类一并初始化,然后针对配置类进行加载对应的配置。
断点可以很清晰看到,配置集合 configures中有如下三个配置类:
其中第一个和第三个属于oauth2的配置。
接下来重点说一下这三个配置类。
首先这里采用了模板模式,抽象了部分相同的逻辑,也就是构造httpSecurity对象的逻辑。来具体看一下,在父类WebSecurityConfigurerAdapter 代码如下:
上述httpSecurity的链式调用其实是在构造configurer,一个configurer对应一个过滤器,构造configurer就是为了后面构造过滤器。所有的configurer被添加到集合configurers 中,集合定义在httpSecurity的父类AbstractConfiguredSecurityBuilder,如下:
添加 configurer 逻辑如下:
以上是公共逻辑,分别看三个实现类的实现:
上面一行关键代码
来看一下client 开启的方式,在AuthorizationServerSecurityConfigurer的配置方法
首先
跟进上面添加过滤器的方法
以上主要是 构造oauth2的 configurer,用来生成oauth2请求过滤器,最后添加到httpSecurity过滤器集合中。
上面比较关键的就是 .authorizeRequests().antMatchers(“/oauth/token”).permitAll(),这里是配置 /oauth/token 请求不进行过滤,委托给 oauth2来处理,这点很重要。
上述代码最关键的一行是,configurer.configure(http); 这里是整合spring security的关键,将授权逻辑委托给spring security,具体就是我们自己的整合配置了,上一篇博客已经贴出来了,代码如下:
最终httpSecurity对象中的 configurers集合如下:
可以看到,oauth2的configurer已经加到了该对象中,每个configurer都对应一个过滤器。
下一篇会对oauth2 和 spring security认证授权源码进行分析。
1)先看下Spring Security中 HttpSecurity配置:
在上一篇配置讲解中,我们提到了oauth2两个注解配置:
//配置授权资源路径 @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
//配置认证服务 @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter
继续跟进这两个注解,找到两个关键类:
AuthorizationServerSecurityConfiguration,ResourceServerConfiguration
这两个类是 spring security接口SecurityConfigurer的子类,在项目启动的时候,spring security 会初始化SecurityConfigurer的子类,所以在加载自身配置 WebSecurityConfig 的同时,会将这两个oauth2的配置类一并初始化,然后针对配置类进行加载对应的配置。
断点可以很清晰看到,配置集合 configures中有如下三个配置类:
其中第一个和第三个属于oauth2的配置。
接下来重点说一下这三个配置类。
首先这里采用了模板模式,抽象了部分相同的逻辑,也就是构造httpSecurity对象的逻辑。来具体看一下,在父类WebSecurityConfigurerAdapter 代码如下:
protected final HttpSecurity getHttp() throws Exception { if (http != null) { return http; } DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor .postProcess(new DefaultAuthenticationEventPublisher()); localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = authenticationManager(); authenticationBuilder.parentAuthenticationManager(authenticationManager); Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects(); http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects); if (!disableDefaults) { //可以看到and()分割后的每段配置,实际上都是在HttpSecuirty中添加一个过滤器, //而每个过滤器对应一个 configurer,将过滤器加入到容器中的前提,是先将configurer初始化。 //这点对下面配置很重要。 http .csrf().and() .addFilter(new WebAsyncManagerIntegrationFilter()) .exceptionHandling().and() .headers().and() .sessionManagement().and() .securityContext().and() .requestCache().and() .anonymous().and() .servletApi().and() .apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and() .logout(); // @formatter:on ClassLoader classLoader = this.context.getClassLoader(); List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader); for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) { http.apply(configurer); } } //这里调用三个子类各自对应的配置,采用了模板模式 configure(http); return http; }
上述httpSecurity的链式调用其实是在构造configurer,一个configurer对应一个过滤器,构造configurer就是为了后面构造过滤器。所有的configurer被添加到集合configurers 中,集合定义在httpSecurity的父类AbstractConfiguredSecurityBuilder,如下:
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
添加 configurer 逻辑如下:
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(objectPostProcessor); configurer.setBuilder((B) this); //将各个过滤器配置添加到 configurers集合中 add(configurer); return configurer; }
以上是公共逻辑,分别看三个实现类的实现:
1)AuthorizationServerSecurityConfiguration
protected void configure(HttpSecurity http) throws Exception { //构造oauth2的 configurer,用来生成oauth2请求过滤器 AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer(); FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping(); http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping); //这里会初始化子类的配置 configure(configurer); //将configurer添加到httpSecurity过滤器集合中 http.apply(configurer); String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token"); String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key"); String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token"); if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) { UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService); } // @formatter:off http .authorizeRequests() .antMatchers(tokenEndpointPath).fullyAuthenticated() .antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess()) .antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess()) .and() .requestMatchers() .antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); // @formatter:on http.setSharedObject(ClientDetailsService.class, clientDetailsService); }
上面一行关键代码
configure(configurer);会调用子类的配置,也就是我们自定义的配置,具体如下:
@Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { //这里主要是开启客户端登录方式 oauthServer.allowFormAuthenticationForClients(); //这里添加自定义的用户认证过滤器,如果不配置的话,在TokenEndpoint中的`/oauth/token`方法中还是会进行一次密码认证的(前提是匹配到对应的granter) oauthServer.addTokenEndpointAuthenticationFilter(new SecurityTokenEndpointAuthenticationFilter(authenticationManager, oAuth2RequestFactory)); }
来看一下client 开启的方式,在AuthorizationServerSecurityConfigurer的配置方法
configure中:
首先
this.allowFormAuthenticationForClients = true;,通过该变量开启,如果开启了allowFormAuthenticationForClients,则进行Client 过滤器的配置,具体代码如下:
@Override public void configure(HttpSecurity http) throws Exception { frameworkEndpointHandlerMapping(); //在这里判断该变量 if (allowFormAuthenticationForClients) { //添加过滤器到 httpSecurity 对象中 clientCredentialsTokenEndpointFilter(http); } for (Filter filter : tokenEndpointAuthenticationFilters) { http.addFilterBefore(filter, BasicAuthenticationFilter.class); } http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); if (sslOnly) { http.requiresChannel().anyRequest().requiresSecure(); } }
跟进上面添加过滤器的方法
clientCredentialsTokenEndpointFilter(http);:
private ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter(HttpSecurity http) { //创建被添加的过滤器,也就是 Client认证过滤器 ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter = new ClientCredentialsTokenEndpointFilter( frameworkEndpointHandlerMapping().getServletPath("/oauth/token")); clientCredentialsTokenEndpointFilter .setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); //设置Client认证失败后的处理逻辑,交给oauth2的OAuth2AuthenticationEntryPoint对象,该对象只有认证失败时才会执行。 OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint(); authenticationEntryPoint.setTypeName("Form"); authenticationEntryPoint.setRealmName(realm); clientCredentialsTokenEndpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint); clientCredentialsTokenEndpointFilter = postProcess(clientCredentialsTokenEndpointFilter); //添加过滤器,这里该过滤器被添加到BasicAuthenticationFilter之前,如果失败就直接返回。 http.addFilterBefore(clientCredentialsTokenEndpointFilter, BasicAuthenticationFilter.class); return clientCredentialsTokenEndpointFilter; }
以上主要是 构造oauth2的 configurer,用来生成oauth2请求过滤器,最后添加到httpSecurity过滤器集合中。
2)WebSecurityConfig
这个相对简单,就是httpSecurity对象的常见配置,属于spring security的配置内容:@Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off http.requestMatchers().antMatchers(HttpMethod.OPTIONS, "/**") .and() .authorizeRequests().antMatchers("/oauth/token").permitAll() .and() .csrf().disable() .headers() .cacheControl().and() .xssProtection().disable() .frameOptions().sameOrigin(); // @formatter:on }
上面比较关键的就是 .authorizeRequests().antMatchers(“/oauth/token”).permitAll(),这里是配置 /oauth/token 请求不进行过滤,委托给 oauth2来处理,这点很重要。
3)ResourceServerConfiguration
这个oauth2关于资源的配置,主要配置如下:@Override protected void configure(HttpSecurity http) throws Exception { ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer(); ResourceServerTokenServices services = resolveTokenServices(); if (services != null) { //配置token服务 resources.tokenServices(services); } else { if (tokenStore != null) { resources.tokenStore(tokenStore); } else if (endpoints != null) { resources.tokenStore(endpoints.getEndpointsConfigurer().getTokenStore()); } } if (eventPublisher != null) { resources.eventPublisher(eventPublisher); } for (ResourceServerConfigurer configurer : configurers) { //设置resourceId configurer.configure(resources); } http.authenticationProvider(new AnonymousAuthenticationProvider("default")) .exceptionHandling() .accessDeniedHandler(resources.getAccessDeniedHandler()).and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .csrf().disable(); http.apply(resources); if (endpoints != null) { http.requestMatcher(new NotOAuthRequestMatcher(endpoints.oauth2EndpointHandlerMapping())); } for (ResourceServerConfigurer configurer : configurers) { //整合spring security的关键,将授权逻辑委托给spring security configurer.configure(http); } if (configurers.isEmpty()) { http.authorizeRequests().anyRequest().authenticated(); } }
上述代码最关键的一行是,configurer.configure(http); 这里是整合spring security的关键,将授权逻辑委托给spring security,具体就是我们自己的整合配置了,上一篇博客已经贴出来了,代码如下:
@Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http.requestMatchers().antMatchers("/**") .and() .authorizeRequests() .anyRequest().authenticated() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { public <O extends FilterSecurityInterceptor> O postProcess(O fsi) { //这里设置访问权限控制类,和资源查找类,都是spring security中的bean fsi.setAccessDecisionManager(accessDecisionManager()); fsi.setSecurityMetadataSource(securityMetadataSource()); return fsi; } }); // @formatter:on }
4)总结
上面的源码主要是项目启动时spring security和oauth2配置的加载,可以看出,二者的整合离不开一个重要对象:httpSecurity,spring security和oauth2的配置都是在初始化和构造该对象,然后构造过滤器。最终httpSecurity对象中的 configurers集合如下:
可以看到,oauth2的configurer已经加到了该对象中,每个configurer都对应一个过滤器。
下一篇会对oauth2 和 spring security认证授权源码进行分析。
相关文章推荐
- SpringSecurity | spring security oauth2.0 配置源码分析(一)
- SpringSecurity | 源码分析篇 (一) Spring Security认证过程
- 【OpenCV图像处理入门学习教程一】OpenCV2 + 3的安装教程与VS2013的开发环境配置 + JPEG压缩源码分析与取反运算修改
- spring security源码分析之core包
- nginx 源码分析 1 - 配置解析
- Spring Security3源码分析-认证授权分析
- MyBatis 源码分析——配置信息
- spring源码分析-配置文件加载过程
- u-boot源码配置原理分析
- Spring Security源码分析七:Spring Security 记住我
- Heritrix1.14源码分析(3) 修改配置文件order.xml加快你的抓取速度
- mybatis从配置读取到打开连接的源码分析
- 混淆的艺术-(苍井空变凤姐)Proguard源码分析(三)Proguard配置解析~上
- spring security配置与分析
- Spring Security源码分析二:Spring Security授权过程
- SpringMVC源码分析(一)从配置文件说起
- CI源码分析(一)—config配置文件模块
- Spring Security 源码分析(四):Spring Social实现微信社交登录
- Spring Security源码分析十一:Spring Security OAuth2整合JWT
- 初学u-boot之源码配置编译过程分析