Shiro rights management framework (III): initialization process Shiro permission filter, and implementation principles

Benpian is the third in the series Shiro, Shiro of filter initialization process and implementation principle. Shiro URL based privilege control is achieved by Filter herein, this from our injected ShiroFilterFactoryBean began to start, look at the source code search filter realization of the principle of Shiro.


Initialization process

ShiroFilterFactoryBean realized FactoryBean interfaces, then Spring initialization time is bound to call ShiroFilterFactoryBean of getObject () Gets instance, while ShiroFilterFactoryBean at this time to do a series of initialization operations.

About FactoryBean introduction and implementation in addition also wrote a journal: https://www.guitu18.com/post/2019/04/28/33.html

In getObject () will call createInstance (), initialization related things are here, come uncommented code stickers and check the relevant code.

    protected AbstractShiroFilter createInstance() throws Exception {
        SecurityManager securityManager = getSecurityManager();
        FilterChainManager manager = createFilterChainManager();
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);
        return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

First, there we get the parameters of the injection well in ShiroConfig the SecurityManager, to stress again that this is a core component in Shiro. Then create a FilterChainManager, this class At the name is used to manage the implementation and operation of the filter chain, we look at its creation methods createFilterChainManager ().

    protected FilterChainManager createFilterChainManager() {
        DefaultFilterChainManager manager = new DefaultFilterChainManager();
        Map<String, Filter> defaultFilters = manager.getFilters();
        for (Filter filter : defaultFilters.values()) {
            applyGlobalPropertiesIfNecessary(filter);
        }
        Map<String, Filter> filters = getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            for (Map.Entry<String, Filter> entry : filters.entrySet()) {
                String name = entry.getKey();
                Filter filter = entry.getValue();
                applyGlobalPropertiesIfNecessary(filter);
                if (filter instanceof Nameable) {
                    ((Nameable) filter).setName(name);
                }
                manager.addFilter(name, filter, false);
            }
        }
        Map<String, String> chains = getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue();
                manager.createChain(url, chainDefinition);
            }
        }
        return manager;
    }

The first step in a new DefaultFilterChainManager, its construction in the filters and methods filterChains two member variables are initialized to a LinkedHashMap order to maintain the insertion, before adding some filters built Shiro call addDefaultFilters ().

    public DefaultFilterChainManager() {
        this.filters = new LinkedHashMap<String, Filter>();
        this.filterChains = new LinkedHashMap<String, NamedFilterList>();
        addDefaultFilters(false);
    }
    protected void addDefaultFilters(boolean init) {
        for (DefaultFilter defaultFilter : DefaultFilter.values()) {
            addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
        }
    }

Here enumeration list of all instances in Shiro built-in filter.

public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);
}

The above code is omitted, enumerated types listed above corresponds exactly to the first to Shiro Shiro built some filters mentioned, these filters are initialized here is just added to the execution and the chain filter, each filter has a different function, we used the fact that only two front.

img

Back to the previous step, after DefaultFilterChainManager initialization is complete, each traversing a default filter and calls applyGlobalPropertiesIfNecessary () to set up some global properties.

    private void applyGlobalPropertiesIfNecessary(Filter filter) {
        applyLoginUrlIfNecessary(filter);
        applySuccessUrlIfNecessary(filter);
        applyUnauthorizedUrlIfNecessary(filter);
    }

In this method calls three methods, three methods are the same logic, are set loginUrl, successUrl and unauthorizedUrl, we'll look at the first applyLoginUrlIfNecessary ().

    private void applyLoginUrlIfNecessary(Filter filter) {
        String loginUrl = getLoginUrl();
        if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) {
            AccessControlFilter acFilter = (AccessControlFilter) filter;
            String existingLoginUrl = acFilter.getLoginUrl();
            if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {
                acFilter.setLoginUrl(loginUrl);
            }
        }
    }

See that this is the method name to set loginUrl, if we configured loginUrl, then replace the value set for us will AccessControlFilter the default loginUrl, the default is loginUrl /login.jsp. The latter two methods for the same reason, we set all the parameters of replacement in, but the third authentication failure jump URL default value is null.

Continue to go back, Map <String, Filter> filters = getFilters (); here is to get our custom filters, the default is empty, if we configure custom filters, then adds it to filters in. So far Shiro filters included with built-in filters and filter all of our configuration.

Next, traversing filterChainDefinitionMap, this is what we filterChainDefinitionMap injected into the ShiroConfig the blocking rules configuration. Here is to create a create a filter chain execution filter rules according to our configuration.

    public void createChain(String chainName, String chainDefinition) {
        String[] filterTokens = splitChainDefinition(chainDefinition);
        for (String token : filterTokens) {
            String[] nameConfigPair = toNameConfigPair(token);
            addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
        }
    }

chainName is we configure the filter path, chainDefinition is the path corresponding filter, usually we are all one to one configuration, such as: filterMap.put("/login", "anon");, but seeing the way we know that a path can actually be filtered by passing ["filter1","filter2"...]configure multiple filter. Here will be a step by step to configure the filter chain execution path based on our configurations filtration and filter mappings.

    public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
        Filter filter = getFilter(filterName);
        applyChainConfig(chainName, filter, chainSpecificFilterConfig);
        NamedFilterList chain = ensureChain(chainName);
        chain.add(filter);
    }

Start filters according filterName acquire a corresponding filter, and ensureChain () will start filterChains acquired NamedFilterList according chainName, not obtain created and added to a filterChains then returns.

    protected NamedFilterList ensureChain(String chainName) {
        NamedFilterList chain = getChain(chainName);
        if (chain == null) {
            chain = new SimpleNamedFilterList(chainName);
            this.filterChains.put(chainName, chain);
        }
        return chain;
    }

Since the filter and the filter path is one to many relationship, so ensureChain () returned NamedFilterList is actually a property has a name called the List <Filter>, this name is saved filter path, List holds the filter of our configuration. After obtaining NamedFilterList added thereto in the filter, such filtration mapping between the path and the filter is initialized as well.

Thus, createInstance createFilterChainManager () () in the execution considered complete, it returns an instance FilterChainManager. Then after this injection PathMatchingFilterChainResolver FilterChainManager, which is a chain of filter performs parser.

The method PathMatchingFilterChainResolver much, the most important thing is the getChain () method.

    public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
        FilterChainManager filterChainManager = getFilterChainManager();
        if (!filterChainManager.hasChains()) {
            return null;
        }
        String requestURI = getPathWithinApplication(request);
        for (String pathPattern : filterChainManager.getChainNames()) {
            if (pathMatches(pathPattern, requestURI)) {
                return filterChainManager.proxy(originalChain, pathPattern);
            }
        }
        return null;
    }

See in these two parameters ServletRequest and ServletResponse parameter is not feeling particularly warm, familiar things finally saw the point, one can tell with certainty about the request. Yes, we will call the server for each request method to perform matching filter chain path filter based on the URL of the request, return the matched filter corresponding to filter.

This method filterChainManager.getChainNames () Returns a collection execution path according to the filter chain arranged to generate our configuration is performed with the same chain sequence sequentially generated in our configuration. From the foregoing we can see, is initialized to a LinkedHashMap will filterChains constructor DefaultFilterChainManager in. So you want to mention a large range of filters on the back is the truth in my first post Shiro notes, if the first match of the filter path is /**that behind the filter never match is not on.


The principle filter

So this getChain () How is it called? Since it is an HTTP request from Tomcat it is certainly over, when a request arrives Tomcat, Tomcat call in the form of a series Filter the chain of responsibility, OncePerRequestFilter is one of many Filter. It implements doFilter () method call its own abstract method doFilterInternal (), this method is implemented in its subclass AbstractShiroFilter in.

img

PathMatchingFilterChainResolver.getChain () is a step by step been called in doFilterInternal () called in.

    protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, 
                                    final FilterChain chain) throws ServletException, IOException {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
            final Subject subject = createSubject(request, response);
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    executeChain(request, response, chain);
                    return null;
                }
            });
    }

Here first obtain acquisition filter, and then executed.

    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        FilterChain chain = getExecutionChain(request, response, origChain);
        chain.doFilter(request, response);
    }

Get the filter as follows.

    protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
        FilterChain chain = origChain;
        FilterChainResolver resolver = getFilterChainResolver();
        if (resolver == null) {
            return origChain;
        }
        FilterChain resolved = resolver.getChain(request, response, origChain);
        if (resolved != null) {
            chain = resolved;
        } else {
        }
        return chain;
    }

By getFilterChainResolver () to get the filter to perform the above-mentioned chain PathMatchingFilterChainResolver parser, and then call it getChain () Gets matched filter, the filter is finally performed executeChain () in.


Starting address: https://www.guitu18.com/post/2019/08/01/45.html

to sum up

Shiro frame in our configuration ShiroFilterFactoryBean when initializing do a lot of initialization, we will add a step to configure the filter rules corresponding filter to the filter chain execution, the execution chains are ultimately put into execution chain parser . When a request arrives Tomcat, the process performed by the Filter chain of responsibility in Tomcat, the final Shiro AbstractShiroFilter.doFilter defined () is executed, then it will go to the chain retrieval is performed parser, the parser get filtration through execution chain and perform, thus achieving the URL filtering based on permissions.

End of this article, this can be considered a light source into an understanding of what Shiro Shiro filter initialization and execution process, compared to Spring source Shiro source of a lot to be easy to understand, so it is not around the Spring. Every time I see the source code, I have below this feeling, especially to see the Spring source when this feeling is particularly strong:

img

We configured the frame and called, will always be floating on the water of the little, no point in, you never know what the bottom is a monster. The better frame package, the less emerge, the more hidden part.

For example SpringBoot, how can a main method to start a project, web.xml it, application.properties it; SpringMVC Why do you need to be able to achieve a @RequestMapping call from a URL to the method; Why Shiro need only be able to achieve @RequiresPermissions method-level access control.

The more you learn the more feel the less you know, this is a very contradictory but real feeling. Do not say the study, the next level above the last method to write an access control, time to be determined, just because of the recent encounter in the project database optimization related issues, want to go see MySQL.

Guess you like

Origin www.cnblogs.com/guitu18/p/11315195.html