[spring-security] spring-security4 security framework configuration details

Spring Security is a security framework that can provide declarative security access control solutions for Spring-based enterprise application systems. It provides a set of beans that can be configured in the Spring application context, making full use of Spring IoC, DI (Inversion of Control, DI: Dependency Injection Dependency Injection) and AOP (Aspect-Oriented Programming) functions to provide The declarative security access control function reduces the work of writing a large amount of repetitive code for the security control of enterprise systems.


The following will explain the specific configuration of spring-security

1. Add maven coordinates in pom.xml

<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. Add the following configuration in 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>

Note that filter-name must be written as springSecurityFilterChain. When the DelegatingFilterProxy class is init, the filter-name will be obtained, and then the proxy bean will be obtained in spring through the filter-name.

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

The configuration of spring-security is parsed by the HttpSecurityBeanDefinitionParser parser, and each http will be parsed into a SecurityFilterChain and added to the filterChains in FilterChainProxy. And the FilterChainProxy will be registered in the spring bean as springSecurityFilterChain.

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 configuration file explanation

<?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>

When the program starts, it will traverse and parse the spring-security configuration file. When the namespace is, <http>it will use the HttpSecurityBeanDefinitionParser class to parse.

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);
        }
    }

The above is to parse the source code. When found security="none", create a DefaultFilterChain and add it to the filterChains property of FilterChainProxy. When there is none security="none", use the code in else. Pay special attention HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);to adding to filterChains at the end.

The execution of spring-security obtains the filter chain according to the requested URL. Then execute in sequence.

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);
            }
        }

The whole spring-security is actually not difficult to understand. But to integrate it into the program and use it reasonably, it needs a lot of use. Practice a lot.
Write picture description here

Guess you like

Origin blog.csdn.net/u013412066/article/details/50667960