spring securitySpring SecuritySpring

Spring Security原理篇(三) HttpSecuri

2018-09-17  本文已影响2792人  怪诞140819

1.初始化HttpSecurity对象

从前面的文章中,我们已经提到在WebSecurityConfigurerAdapter的初始化方法init()中,通过getHttp()方法获取到了HttpSecurity的对象,我们再来看一下init()这个方法的源代码

    /**
     * @param web
     * @throws Exception
     */
    public void init(final WebSecurity web) throws Exception {
        final HttpSecurity http = getHttp();
        web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
            public void run() {
                FilterSecurityInterceptor securityInterceptor = http
                        .getSharedObject(FilterSecurityInterceptor.class);
                web.securityInterceptor(securityInterceptor);
            }
        });
    }

这个方法的具体作用已经在上一篇文章中说过,先构建HttpSecurity对象,然后通过WebSecurity对象的addSecurityFilterChainBuilder()方法添加到securityFilterChainBuilders的List中,最后用来组件过滤器链。

1.1 WebSecurityConfigurerAdaptergetHttp()方法

protected final HttpSecurity getHttp() throws Exception {
        //如果已经存在HttpSecurity 对象,则返回
        if (http != null) {
            return http;
        }
        //这里主要还是关于异常的一些处理,这里我们最后的文章统一再说,先给自己留个坑,先猜测一下吧
        DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
                .postProcess(new DefaultAuthenticationEventPublisher());
    localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
           //构建AuthenticationManager对象,这个对象管理认证,后面我们再说
        AuthenticationManager authenticationManager = authenticationManager();
        authenticationBuilder.parentAuthenticationManager(authenticationManager);

            //创建共享对象
        Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
       
        //构建HttpSecurity 需要用到authenticationBuilder,sharedObjects
        http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
                sharedObjects);

        //允许默认配置的时候
        if (!disableDefaults) {
            // @formatter:off
            http
                .csrf().and()
                .addFilter(new WebAsyncManagerIntegrationFilter())
                .exceptionHandling().and()
                .headers().and()
                .sessionManagement().and()
                .securityContext().and()
                .requestCache().and()
                .anonymous().and()
                .servletApi().and()
                .apply(new DefaultLoginPageConfigurer<>()).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??
        configure(http);
        return http;
    }

1.2 HttpSecurity 类的大体理解

1.2.1 HttpSecurity的类图

HttpSecurity类图
public final class HttpSecurity extends
        AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
        implements SecurityBuilder<DefaultSecurityFilterChain>,
        HttpSecurityBuilder<HttpSecurity> 

我们可以知道调用HttpSecuritybuild()方法的时候返回的就是DefaultSecurityFilterChain的对象,当然具体的还要看HttpSecurityperformBuild()方法

1.2.2HttpSecurity的属性

     //从变量名就可以看到是请求匹配过滤的配置信息    
    private final RequestMatcherConfigurer requestMatcherConfigurer;

    //过滤器列表?
    private List<Filter> filters = new ArrayList<>();

    //匹配任何请求的匹配器
    private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;

    //过滤器比较.....啥啥啥不知道
    private FilterComparator comparator = new FilterComparator();
public final class AnyRequestMatcher implements RequestMatcher {
    public static final RequestMatcher INSTANCE = new AnyRequestMatcher();

    public boolean matches(HttpServletRequest request) {
        //直接return true?那就是说有请求都匹配
        return true;
    }

    @Override
    @SuppressWarnings("deprecation")
    public boolean equals(Object obj) {
        return obj instanceof AnyRequestMatcher
                || obj instanceof org.springframework.security.web.util.matcher.AnyRequestMatcher;
    }

    @Override
    public int hashCode() {
        return 1;
    }

    @Override
    public String toString() {
        return "any request";
    }

    private AnyRequestMatcher() {
    }
}

这个类继承的接口只有matches方法,方法的注释上写的很明白,如果匹配规则就返回true,否则返回false,AnyRequestMatcher永远返回true,说明匹配任何请求。如果需要查看接口定义,可以自行查看类RequestMatcher的源代码,因为简单,节省空间。

*2.comparatorFilterComparator的对象。然而Filter比较器类的定义也是比较简单的,此处还是不引入源代码了,因为后面我们还要讲解,里面很重要的定义了一些我们不知不觉用着的东西。FilterComparator实现Comparator接口,作为比较器,我们只看一下他的一个方法就可以了

public int compare(Filter lhs, Filter rhs) {
    Integer left = getOrder(lhs.getClass());
    Integer right = getOrder(rhs.getClass());
    return left - right;
}

这个getOrder就是Filter上定义Order的数字,还有一种形式就是addFilter这个的。反正就是获取到在过滤器列表中的顺序,不过当然不是连续的顺序

    public HttpSecurity addFilter(Filter filter) {
        Class<? extends Filter> filterClass = filter.getClass();
        if (!comparator.isRegistered(filterClass)) {
            throw new IllegalArgumentException(
                    "The Filter class "
                            + filterClass.getName()
                            + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
        }
        this.filters.add(filter);
        return this;
    }

1.2.3 HttpSecurity的部分方法

public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,
            AuthenticationManagerBuilder authenticationBuilder,
            Map<Class<? extends Object>, Object> sharedObjects) {
        super(objectPostProcessor);
        Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
        setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
        for (Map.Entry<Class<? extends Object>, Object> entry : sharedObjects
                .entrySet()) {
            setSharedObject((Class<Object>) entry.getKey(), entry.getValue());
        }
        ApplicationContext context = (ApplicationContext) sharedObjects
                .get(ApplicationContext.class);
        this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
    }

除了设置了共享对象之外,唯一值得一提的就是ApplicationContext 作为共享对象传递进来了,哈哈,毕竟spring security再牛逼,再spring面前还是得装装小媳妇的。至于requestMatcherConfigurer还是要在稍后的篇幅重点讲一下的,毕竟太重要了,不论对于我们使用还是要理解这个过程,都不可获取。

protected DefaultSecurityFilterChain performBuild() throws Exception {
        Collections.sort(filters, comparator);
        return new DefaultSecurityFilterChain(requestMatcher, filters);
}

1.2.4 HttpSecurity实现HttpSecurityBuilder的方法

public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
    comparator.registerAfter(filter.getClass(), afterFilter);
    return addFilter(filter);
}
* <ul>
     * <li>{@link ChannelProcessingFilter}</li>
     * <li>{@link ConcurrentSessionFilter}</li>
     * <li>{@link SecurityContextPersistenceFilter}</li>
     * <li>{@link LogoutFilter}</li>
     * <li>{@link X509AuthenticationFilter}</li>
     * <li>{@link AbstractPreAuthenticatedProcessingFilter}</li>
     * <li><a href="{@docRoot}/org/springframework/security/cas/web/CasAuthenticationFilter.html">CasAuthenticationFilter</a></li>
     * <li>{@link UsernamePasswordAuthenticationFilter}</li>
     * <li>{@link ConcurrentSessionFilter}</li>
     * <li>{@link OpenIDAuthenticationFilter}</li>
     * <li>{@link org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter}</li>
     * <li>{@link org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter}</li>
     * <li>{@link ConcurrentSessionFilter}</li>
     * <li>{@link DigestAuthenticationFilter}</li>
     * <li>{@link org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter}</li>
     * <li>{@link BasicAuthenticationFilter}</li>
     * <li>{@link RequestCacheAwareFilter}</li>
     * <li>{@link SecurityContextHolderAwareRequestFilter}</li>
     * <li>{@link JaasApiIntegrationFilter}</li>
     * <li>{@link RememberMeAuthenticationFilter}</li>
     * <li>{@link AnonymousAuthenticationFilter}</li>
     * <li>{@link SessionManagementFilter}</li>
     * <li>{@link ExceptionTranslationFilter}</li>
     * <li>{@link FilterSecurityInterceptor}</li>
     * <li>{@link SwitchUserFilter}</li>
     * </ul>

1.2.4 HttpSecurity配置的部分方法

这部分还是参考一下源代码吧,太多太多了,其实在我们使用的时候也可以参考,因为这些方法的注释上都给出了具体的例子,下面简单的看一下吧,就比如formLogin()这个方法

    /**
     *
     * 指定支持基于表单的身份验证. If
     * 若果没有指定loginPage()这个配置,那么将使用默认的登录页面
     *
     * <h2>Example Configurations</h2>
     * 默认的登录的URL为 /login
    
     * <pre>
     * &#064;Configuration
     * &#064;EnableWebSecurity
     * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     *
     *  &#064;Override
     *  protected void configure(HttpSecurity http) throws Exception {
     *      http.authorizeRequests().antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;).and().formLogin();
     *  }
     *
     *  &#064;Override
     *  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     *      auth.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;);
     *  }
     * }
     * </pre>
     *
     * The configuration below demonstrates customizing the defaults.
     *
     * <pre>
     * &#064;Configuration
     * &#064;EnableWebSecurity
     * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     *
     *  &#064;Override
     *  protected void configure(HttpSecurity http) throws Exception {
     *      http.authorizeRequests().antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;).and().formLogin()
     *              .usernameParameter(&quot;username&quot;) // default is username
     *              .passwordParameter(&quot;password&quot;) // default is password
     *              .loginPage(&quot;/authentication/login&quot;) // default is /login with an HTTP get
     *              .failureUrl(&quot;/authentication/login?failed&quot;) // default is /login?error
     *              .loginProcessingUrl(&quot;/authentication/login/process&quot;); // default is /login
     *                                                                      // with an HTTP
     *                                                                      // post
     *  }
     *
     *  &#064;Override
     *  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     *      auth.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;);
     *  }
     * }
     * </pre>
     *
     * @see FormLoginConfigurer#loginPage(String)
     *
     * @return the {@link FormLoginConfigurer} for further customizations
     * @throws Exception
     */
    public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
        return getOrApply(new FormLoginConfigurer<>());
    }

2 从配置到Filter

我们还是需要看一下HttpSecurity的配置最后是怎样影响到我们的过滤器执行的
我们还是用两个简单的例子来看一下工作原理

2.1 formLogin()的原理

2.1.1 formLogin配置的例子

我们也不需要自己手写一个例子出来,然后说一大堆,我们直接可以拿到方法上面注释的例子来看一下就可以了,只是简单的替换掉了转义的字符和一点点英文的注释

 下面的配置演示了自定义默认值。
@Configuration
@EnableWebSecurity
 public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {  
    http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
                .usernameParameter("username") //默认的用户名的参数 username
                .passwordParameter("password") // 默认的密码的参数 password
                .loginPage("/authentication/login") // 默认请求地址 /login with an HTTP get
                .failureUrl("/authentication/login?failed") //默认失败地址 /login?error
                .loginProcessingUrl("/authentication/login/process"); // default is /login                                                                      // with an HTTP
                                                                        // post
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
    }
 }

上面的例子做了一下几件事情

2.1.2 formLogin背后的实现原理

//formlogin()方法
public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
    //T1这里的FormLoginConfigurer和getOrApply方法我们都不知道是啥玩意儿
    return getOrApply(new FormLoginConfigurer<>());
}

//getOrApply()方法
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
            C configurer) throws Exception {
        //T2 这里的意思是去查看是否有存在了
        C existingConfig = (C) getConfigurer(configurer.getClass());
        if (existingConfig != null) {
            return existingConfig;
        }
           //T3 不存在的情况下就去调用apply()这个方法
        return apply(configurer);
    }

//apply()方法
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
            throws Exception {
         //T4
        configurer.addObjectPostProcessor(objectPostProcessor);
        //T5    
        configurer.setBuilder((B) this);
        //T6    
        add(configurer);
        return configurer;
    }

2.1.3 FormLoginConfigurer

public FormLoginConfigurer() {
        super(new UsernamePasswordAuthenticationFilter(), null);
        usernameParameter("username");
        passwordParameter("password");
    }

调用父类构造方法的时候创建了UsernamePasswordAuthenticationFilter,然后复制给了一个叫做authFilter的变量,所以我们知道,其实在创建FormLoginConfigurer的时候,他自己就已经有了一个叫做UsernamePasswordAuthenticationFilter

@Override
    public void configure(B http) throws Exception {
        PortMapper portMapper = http.getSharedObject(PortMapper.class);
        if (portMapper != null) {
            authenticationEntryPoint.setPortMapper(portMapper);
        }

        RequestCache requestCache = http.getSharedObject(RequestCache.class);
        if (requestCache != null) {
            this.defaultSuccessHandler.setRequestCache(requestCache);
        }

        authFilter.setAuthenticationManager(http
                .getSharedObject(AuthenticationManager.class));
        authFilter.setAuthenticationSuccessHandler(successHandler);
        authFilter.setAuthenticationFailureHandler(failureHandler);
        if (authenticationDetailsSource != null) {
            authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
        }
        SessionAuthenticationStrategy sessionAuthenticationStrategy = http
                .getSharedObject(SessionAuthenticationStrategy.class);
        if (sessionAuthenticationStrategy != null) {
            authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
        }
        RememberMeServices rememberMeServices = http
                .getSharedObject(RememberMeServices.class);
        if (rememberMeServices != null) {
            authFilter.setRememberMeServices(rememberMeServices);
        }
        F filter = postProcess(authFilter);
        http.addFilter(filter);
    }

这段代码大部分的都是在设置一些Filter执行需要的属性。这个和具体的这个配置到底是完成什么样的功能有关系,然后我们最需要关心的只有下面这行代码

http.addFilter(filter);

也就是说最后往HttpSecurity的List<Filter> filters列表中添加了一个Filter对象

private void configure() throws Exception {
        Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

        for (SecurityConfigurer<O, B> configurer : configurers) {
            configurer.configure((B) this);
        }
    }

至于说这个configurers属性我们前面说过每一个配置后面调用apply()方法都会添加到这个列表中,这里不再赘述

2.2 直接添加过滤器

上面添加过滤器是通过对HttpSecurity方法的调用实现配置,最后添加过滤器,然而直接添加过滤器就更加简单,下面只通过一个简单的例子来说一下

2.2.1 addFilterAfter方法

直接看一下源代码就行,因为实在太简单了

public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
    comparator.registerAfter(filter.getClass(), afterFilter);
    return addFilter(filter);
}
public HttpSecurity addFilter(Filter filter) {
        Class<? extends Filter> filterClass = filter.getClass();
        if (!comparator.isRegistered(filterClass)) {
            throw new IllegalArgumentException(
                    "The Filter class "
                            + filterClass.getName()
                            + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
        }
        this.filters.add(filter);
        return this;
    }

我们可以看到只是先确定过滤器的顺序,然后看一下过滤器是否注册了,然后就会添加到我们的filters这个变量。

3 总结

杂乱无章的几乎介绍了这个类的所有的代码,我们总结一下这个类吧
*HttpSecurity最终可以得到一个DefaultSecurityFilterChain通过的是build()方法

上一篇下一篇

猜你喜欢

热点阅读