【spring-security】spring-security4安全框架配置详解

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。


下面将为大家讲解spring-security的具体配置

1.在pom.xml中添加maven坐标

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.0.3.RELEASE</version>
</dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>4.0.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.0.3.RELEASE</version>
</dependency>

2.在web.xml中添加如下配置

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

注意filter-name一定要写成springSecurityFilterChain。在DelegatingFilterProxy类init时,会获取filter-name,然后通过filter-name去spring中获取代理的bean。

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
    Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
    if (isTargetFilterLifecycle()) {
        delegate.init(getFilterConfig());
    }
    return delegate;
}

而spring-security的配置是由HttpSecurityBeanDefinitionParser解析器解析,每一个http都会被解析成一个SecurityFilterChain都添加到FilterChainProxy中的filterChains中。而且该FilterChainProxy会以springSecurityFilterChain注册到spring的bean中。

public BeanDefinition parse(Element element, ParserContext pc) {
        CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
        pc.pushContainingComponent(compositeDef);
        registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
        BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition("org.springframework.security.filterChains");
        List filterChains = (List)listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
        filterChains.add(this.createFilterChain(element, pc));
        pc.popAndRegisterContainingComponent();
        return null;
    }
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
    if(!pc.getRegistry().containsBeanDefinition("org.springframework.security.filterChainProxy")) {
            RootBeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
            listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
            pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, "org.springframework.security.filterChains"));
            BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
            fcpBldr.getRawBeanDefinition().setSource(source);
            fcpBldr.addConstructorArgReference("org.springframework.security.filterChains");
            fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
            AbstractBeanDefinition fcpBean = fcpBldr.getBeanDefinition();
            pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, "org.springframework.security.filterChainProxy"));
            pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");
        }
    }

3.spring-security配置文件讲解

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-4.0.xsd ">

    <!-- 静态资源,不用权限 -->
    <http pattern="/resources/**" security="none"/>
    <http pattern="/verify/**" security="none"/>
    <http pattern="/user/login.htm" security="none"/>
    <http pattern="/user/register.*" security="none"/>
    <http pattern="/favicon.ico" security="none"/>

    <http use-expressions="true" auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint">
        <intercept-url pattern="/**" access="authenticated"/>
        <!--<form-login login-page="/user/login.htm" login-processing-url="/login.json" username-parameter="userName"
                    default-target-url="/user/index.htm" always-use-default-target="true"
                    authentication-success-handler-ref="authenticationSuccessHandler"
            authentication-failure-handler-ref="authenticationFailureHandler"/>-->
        <logout invalidate-session="true" logout-url="/logout" logout-success-url="/"/>
        <csrf disabled="true"/>
        <custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"/>
    </http>

    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="daoAuthenticationProvider" />
    </authentication-manager>

    <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <!-- 是否顯示用戶名不存在信息 -->
        <beans:property name="hideUserNotFoundExceptions" value="false"/>
        <beans:property name="userDetailsService" ref="userDetailsService"/>
        <beans:property name="passwordEncoder" ref="md5Encoder"/>
        <beans:property name="saltSource" ref="saltSource"/>
    </beans:bean>

    <beans:bean id="md5Encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />

    <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <beans:constructor-arg name="loginFormUrl" value="/user/login.htm" />
    </beans:bean>

    <!-- 配置自定义过滤器 -->
    <beans:bean id="loginFilter" class="com.test.security.LoginFilter">
        <beans:property name="filterProcessesUrl" value="/login.json"/>
        <beans:property name="usernameParameter" value="userName"/>
        <beans:property name="authenticationManager" ref="authenticationManager"/>
        <beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
        <beans:property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
    </beans:bean>
</beans:beans>

程序在启动的时候会遍历解析spring-security配置文件,当命名空间是<http>的时候就使用HttpSecurityBeanDefinitionParser类来解析。

private BeanReference createFilterChain(Element element, ParserContext pc) {
        boolean secured = !"none".equals(element.getAttribute("security"));
        if(!secured) {
            if(!StringUtils.hasText(element.getAttribute("pattern")) && !StringUtils.hasText("request-matcher-ref")) {
                pc.getReaderContext().error("The \'security\' attribute must be used in combination with the \'pattern\' or \'request-matcher-ref\' attributes.", pc.extractSource(element));
            }

            for(int var15 = 0; var15 < element.getChildNodes().getLength(); ++var15) {
    
    
                if(element.getChildNodes().item(var15) instanceof Element) {
                    pc.getReaderContext().error("If you are using <http> to define an unsecured pattern, it cannot contain child elements.", pc.extractSource(element));
                }
            }

            return this.createSecurityFilterChainBean(element, pc, Collections.emptyList());
        } else {
            BeanReference portMapper = this.createPortMapper(element, pc);
            RuntimeBeanReference portResolver = this.createPortResolver(portMapper, pc);
            ManagedList authenticationProviders = new ManagedList();
            BeanReference authenticationManager = this.createAuthenticationManager(element, pc, authenticationProviders);
            boolean forceAutoConfig = isDefaultHttpConfig(element);
            HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
            AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
            httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
            httpBldr.setEntryPoint(authBldr.getEntryPointBean());
            httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
            authenticationProviders.addAll(authBldr.getProviders());
            ArrayList unorderedFilterChain = new ArrayList();
            unorderedFilterChain.addAll(httpBldr.getFilters());
            unorderedFilterChain.addAll(authBldr.getFilters());
            unorderedFilterChain.addAll(this.buildCustomFilterList(element, pc));
            Collections.sort(unorderedFilterChain, new OrderComparator());
            this.checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
            ManagedList filterChain = new ManagedList();
            Iterator var13 = unorderedFilterChain.iterator();

            while(var13.hasNext()) {
                OrderDecorator od = (OrderDecorator)var13.next();
                filterChain.add(od.bean);
            }

            return this.createSecurityFilterChainBean(element, pc, filterChain);
        }
    }

上面是解析源代码。当发现security="none"的时候,则创建一个DefaultFilterChain添加到FilterChainProxy的filterChains属性中。当没有security="none"则使用else中的代码。特别注意HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);最后添加到filterChains中。

spring-security执行则是依据请求的URL获得过滤器链。然后依次执行。

private void doFilterInternal(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        FirewalledRequest fwRequest = firewall
                .getFirewalledRequest((HttpServletRequest) request);
        HttpServletResponse fwResponse = firewall
                .getFirewalledResponse((HttpServletResponse) response);

        List<Filter> filters = getFilters(fwRequest);

        if (filters == null || filters.size() == 0) {
            if (logger.isDebugEnabled()) {
                logger.debug(UrlUtils.buildRequestUrl(fwRequest)
                        + (filters == null ? " has no matching filters"
                                : " has an empty filter list"));
            }

            fwRequest.reset();

            chain.doFilter(fwRequest, fwResponse);

            return;
        }

        VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
        vfc.doFilter(fwRequest, fwResponse);
    }
/**
     * Returns the first filter chain matching the supplied URL.
     *
     * @param request the request to match
     * @return an ordered array of Filters defining the filter chain
     */
    private List<Filter> getFilters(HttpServletRequest request) {
        for (SecurityFilterChain chain : filterChains) {
            if (chain.matches(request)) {
                return chain.getFilters();
            }
        }

        return null;
    }
public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException {
            if (currentPosition == size) {
                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                            + " reached end of additional filter chain; proceeding with original chain");
                }

                // Deactivate path stripping as we exit the security filter chain
                this.firewalledRequest.reset();

                originalChain.doFilter(request, response);
            }
            else {
                currentPosition++;

                Filter nextFilter = additionalFilters.get(currentPosition - 1);

                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                            + " at position " + currentPosition + " of " + size
                            + " in additional filter chain; firing Filter: '"
                            + nextFilter.getClass().getSimpleName() + "'");
                }

                nextFilter.doFilter(request, response, this);
            }
        }

整个spring-security理解起来其实并不难。但要融入到程序中合理使用,还需要多多运用。多多练习。
这里写图片描述

猜你喜欢

转载自blog.csdn.net/u013412066/article/details/50667960