Shiro source code 1: Where do I come from, where am I going

Let’s start with shiro’s basic configuration 1: make a class roughly named shiroConfig with a bunch of @bean configurations, one of which looks like this:

@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(CommadminConfig commadminConfig, LoginFilterUrlConfig loginFilterUrlConfig, SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> map = new LinkedHashMap<String, String>(); map.putAll(loginFilterUrlConfig.getLoginFilterUrlMap()); / / login shiroFilterFactoryBean. SetLoginUrl ("/auth login "); / / homepage shiroFilterFactoryBean setSuccessUrl ("/index "); / / error pages, certification through jump shiroFilterFactoryBean. SetUnauthorizedUrl ("/error "); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); Map<String, Filter> filterMap = new LinkedHashMap<String, Filter>(); StatelessAuthcFilter statelessAuthcFilter = new StatelessAuthcFilter(); statelessAuthcFilter.setCicadaLoginUrl(commadminConfig.getLoginUrl()); statelessAuthcFilter.setAnonTinyService(commadminConfig.getAnonTinyService()); filterMap.put("statelessAuthc", statelessAuthcFilter); shiroFilterFactoryBean.setFilters(filterMap); return shiroFilterFactoryBean; }Copy the code

As shown in the figure above, this configuration bean returns a shiroFilterFactoryBean. This configuration bean shows several things: the @bean is managed by Spring, that is, when the Spring container starts, the bean is loaded. This is spring’s knowledge category, not a speak b: shiroFilterFactoryBean. SetFilterChainDefinitionMap (map); A map was added

   login-filter-url-map:
    "[/auth/login]": anon
    "[/auth/logout]": anon
    "[/user/register]": anon
    "[/test/**]": anon
    "[/web/gateway.do]": statelessAuthc
    "[/**]": statelessAuthc                                                   
Copy the code

This map is actually the configuration of application.yml. Where is it? C: filterMap. Put (” statelessAuthc, “statelessAuthcFilter); The statelessAuthcFilter has a method that looks like this. It intercepts the requested token, checks whether it is expired, and finally authenticates the requested username and password

@Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { if (isLoginRequest(request, response)) { return true; } //1. Obtain the token. The token is returned to the front-end after login. HttpServletRequest httpServletRequest = (HttpServletRequest) request; if (httpServletRequest.getRequestURI().endsWith("/login.htm")) { return true; } String originToken = JasyptUtils.decrypt(token); String[] infoArray = originToken.split(CommAdminConstants.SEPEARTOR); if (null == infoArray || infoArray.length < 3) { LOG.error("stateless-authc-filter-token-parse-fail, token is " + token); onLoginFail(response); return false; } String userName = infoArray[0]; String userPwd = infoArray[1]; String expireTimeStr = infoArray[2]; //2. Verify whether the token expiration time is expired. long tokenExpiredTime = Long.valueOf(expireTimeStr); if (tokenExpiredTime < System.currentTimeMillis()) { RequestContext.clear(); LOG.warn("stateless-authc-filter-token-expired, token is " + token); onLoginFail(response); return false; } RequestContext.RequestParams data = new RequestContext.RequestParams(); data.setUserName(userName); RequestContext.set(data); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName, userPwd); try { getSubject(request, response).login(usernamePasswordToken); } catch (Exception e) { LOG.error("stateless-authc-filter-manu-login-fail,token is " + token, e); onLoginFail(response); return false; } return true; }Copy the code

Add a filter shiroFilterFactoryBean. SetFilters (filterMap);

First of all, the shiroFilterFactoryBean is a configuration bean in Shiro

public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {

    private static transient final Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class);

    private SecurityManager securityManager;

    private Map<String, Filter> filters;

    private List<String> globalFilters;

    private Map<String, String> filterChainDefinitionMap; //urlPathExpression_to_comma-delimited-filter-chain-definition

    private String loginUrl;
    private String successUrl;
    private String unauthorizedUrl;
Copy the code

He achieved FactoryBean BeanPostProcessor, realized the FactoryBean we know that in the spring, there will be a getObject method implementation, and it is the BeanPostProcessor spring inside a rear processor, If it is commonly realized BeanPostProcessor, spring will at the time of before and after the bean initialization, will, in turn, calls the postProcessBeforeInitialization, postProcessAfterInitialization method, So let’s see if the shiroFilterFactoryBean has any of these methods implemented and what they do: I want you to look at the source code and try to keep the question in mind. Why does shiro use the url suffix that is not blocked when we configure it in application.yml? Is this url suffix inherently related to shiro

Shiro source code 2: build url suffix and filter association relationship

The getObject method of the ShiroFilterFactoryBean begins

 public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }
Copy the code

When is this method executed? When I talk about springBoot’s startup process later, this method is actually crucial, because the method it calls later will associate the url suffix with the filter, Advanced methods to createInstance org. Apache. Shiro. Spring. Web. ShiroFilterFactoryBean# createInstance FilterChainManager manager = createFilterChainManager();

protected FilterChainManager createFilterChainManager() { DefaultFilterChainManager manager = new DefaultFilterChainManager(); Map<String, Filter> defaultFilters = manager.getFilters(); // Apply global Settings if necessary: for (Filter Filter: defaultFilters.values()) { applyGlobalPropertiesIfNecessary(filter); } //Apply the acquired and/or configured filters: 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); } //'init' argument is false, since Spring-configured filters should be initialized //in Spring (i.e. 'init-method=blah') or implement InitializingBean: manager.addFilter(name, filter, false); } } // set the global filters manager.setGlobalFilters(this.globalFilters); //build up the chains: Map<String, String> chains = getFilterChainDefinitionMap(); // This map is the application. Yml url suffix and the corresponding key value pair with the value if (! CollectionUtils.isEmpty(chains)) { for (Map.Entry<String, String> entry : chains.entrySet()) { String url = entry.getKey(); String chainDefinition = entry.getValue(); Manager.createchain (url, chainDefinition); } } // create the default chain, to match anything the path matching would have missed manager.createDefaultChain("/**"); // TODO this assumes ANT path matching, which might be OK here return manager; }Copy the code

Map

defaultFilters = manager.getFilters(); First, where did this thing come from? What is it? org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#getFilters
,>

  public Map<String, Filter> getFilters() {
        return filters;
    }
Copy the code

private Map<String, Filter> filters; A look is a collection of the map, then DefaultFilterChainManager this class must have the relevant put added methods, or what method to construct incoming code, such as assigning a map, use CTRL + F a search, found the following code

protected void addFilter(String name, Filter filter, boolean init, boolean overwrite) { Filter existing = getFilter(name); if (existing == null || overwrite) { if (filter instanceof Nameable) { ((Nameable) filter).setName(name); } if (init) { initFilter(filter); } this.filters.put(name, filter); }}Copy the code

CTRL, addFilter, see where this method is called

protected void addDefaultFilters(boolean init) { for (DefaultFilter defaultFilter : DefaultFilter.values()) { addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false); }}Copy the code

The addDefaultFilters method looks like it’s iterating through some defaultfilter.values (). So what is the DefaultFilter?

public enum DefaultFilter {

    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    authcBearer(BearerHttpAuthenticationFilter.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),
    invalidRequest(InvalidRequestFilter.class);
Copy the code

We found that DefaultFilter is an enumeration type, and look at the enumeration type and the corresponding value, are you familiar with the feeling, are there any values? Yml is configured to avoid interception, such as anon

"[/auth/login]": anon
Copy the code

Anonymousfilter. class

defaultFilters = manager.getfilters (); This is the default filter in Shiro
,>

Map

filters = getFilters(); What is this? com.alipay.cxbiz.open.commadmin.web.conf.ShiroConfig#shiroFilterFactoryBean
,>

 filterMap.put("statelessAuthc", statelessAuthcFilter);
        shiroFilterFactoryBean.setFilters(filterMap);
Copy the code

This filter map is actually put in when we customize shiroConfig. Of course, there are more filters in this map than we put in this place. Anyway, the filters in this map are custom filters such as statelessAuthcFilter

Manager.addfilter (name, filter, false); In the org. Apache. Shiro. Web. Filter. MGT. DefaultFilterChainManager

Map<String, Filter> filters; 
Copy the code

So far: Shiro’s default filter and programmer defined filter sources are clear

Then analysis the Map < String, the String > chains = getFilterChainDefinitionMap (); What is this map, and kv are of type String, the same way, in getFilterChainDefinitionMap inside to look for, Where in com.alipay.cxbiz.open.com madmin. Web. Conf. ShiroConfig# shiroFilterFactoryBean

 shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
Copy the code

This is where it was added. As mentioned earlier, this map is actually a collection of interception-free urls configured in application.yml

login-filter-url-map:
    "[/auth/login]": anon
    "[/auth/tiny/login]": anon
    "[/auth/logout]": anon
    "[/user/register]": anon
    "[/templtest/**]": anon
    "[/commadmin/**]": anon
    "[/cicada/**]": anon
    "[/web/gateway.do]": statelessAuthc
    "[/**]": statelessAuthc
Copy the code

It’s a collection of these things, and then it goes down

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);
            }
        }
Copy the code

So he’s iterating through the map and getting the URL and the value “[/auth/login]”: anon and by reference, /auth/login is the URL and anon is the value

Org, apache shiro. Web. Filter. MGT. DefaultFilterChainManager# createChain remove redundant code, the core code below, add a default filter InvalidRequestFilter first

public void createChain(String chainName, String chainDefinition) { if (! CollectionUtils.isEmpty(globalFilterNames)) { globalFilterNames.stream().forEach(filterName -> addToChain(chainName, filterName)); [] filterTokens = splitChainDefinition(chainDefinition); for (String token : filterTokens) { String[] nameConfigPair = toNameConfigPair(token); addToChain(chainName, nameConfigPair[0], nameConfigPair[1]); }}Copy the code

org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#addToChain(java.lang.String, java.lang.String, java.lang.String)

public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) { if (! StringUtils.hasText(chainName)) { throw new IllegalArgumentException("chainName cannot be null or empty."); } Filter filter = getFilter(filterName); If (filter == null) {throw new IllegalArgumentException("There is no filter with name '" + filterName + "' to apply to chain [" + chainName + "] in the pool of available Filters. Ensure a " + "filter with that name/path has first been registered with the addFilter method(s)."); } applyChainConfig(chainName, filter, chainSpecificFilterConfig); NamedFilterList chain = ensureChain(chainName); chain.add(filter); }Copy the code

Filter = getFilter(filterName); FilterName is the anon value of “[/auth/login]”: anon, from which a Filter is obtained

NamedFilterList chain = ensureChain(chainName); chain.add(filter);

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

    public NamedFilterList getChain(String chainName) {
        return this.filterChains.get(chainName);
    }
Copy the code

Shiro url = shiro url = shiro url = Shiro url = Shiro url = Shiro url = Shiro url = Shiro url = Shiro url = Shiro url Then it’s just a request to see if you can match these filters, and then you choose different filters to execute, and then the chain looks like this

Shiro source code three: brother go which filter

org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal

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

FilterChain chain = getExecutionChain(request, response, origChain); org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain

for (String pathPattern : filterChainManager.getChainNames()) { if (pathPattern ! = null && ! DEFAULT_PATH_SEPARATOR.equals(pathPattern) && pathPattern.endsWith(DEFAULT_PATH_SEPARATOR)) { pathPattern = pathPattern.substring(0, pathPattern.length() - 1); } // If the path does match, then pass on to the subclass implementation for specific checks: if (pathMatches(pathPattern, requestURI)) { if (log.isTraceEnabled()) { log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + Encode.forHtml(requestURI) + "]. " + "Utilizing corresponding filter chain..." ); } return filterChainManager.proxy(originalChain, pathPattern); }}Copy the code

Loop through the chainName (/auth/login) to see if it matches the url suffix we requested. If so, extract the corresponding filterChain. The chain contains multiple filters

public FilterChain proxy(FilterChain original, String chainName) {
        NamedFilterList configured = getChain(chainName);
        if (configured == null) {
            String msg = "There is no configured chain under the name/key [" + chainName + "].";
            throw new IllegalArgumentException(msg);
        }
        return configured.proxy(original);
    }
Copy the code

Go back to chain.dofilter (request, response) above;

org.apache.shiro.web.servlet.ProxiedFilterChain#doFilter this.filters.get(this.index++).doFilter(request, response, this); Loop through the filter in the filterChain corresponding to this URL

org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter org.apache.shiro.web.servlet.AdviceFilter#doFilterInternal

public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { Exception exception = null; try { boolean continueChain = preHandle(request, response); if (log.isTraceEnabled()) { log.trace("Invoked preHandle method. Continuing chain? : [" + continueChain + "]"); } if (continueChain) { executeChain(request, response, chain); } postHandle(request, response); }Copy the code

org.apache.shiro.web.filter.PathMatchingFilter#preHandle

private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
                                           String path, Object pathConfig) throws Exception {

            return onPreHandle(request, response, pathConfig);
}
Copy the code

AnonymousFilter onPreHandle AnonymousFilter’s onPreHandle returns true by default, That is, return true for the user validation, no validation

public class AnonymousFilter extends PathMatchingFilter { @Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) { return true; }}Copy the code

If it’s something that needs to be checked, Is take the AccessControlFilter this filter org. Apache. Shiro. Web. Filter. AccessControlFilter# onAccessDenied (javax.mail. Servlet. ServletRequest, javax.servlet.ServletResponse, java.lang.Object)

protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return onAccessDenied(request, response);
    }
Copy the code

Eventually to, this is our custom StatelessAuthcFilter madmin. Com.alipay.cxbiz.open.com web. The auth. StatelessAuthcFilter# onAccessDenied

Shiro source code four: certification process

After thousands of difficulties and dangers, Tang Seng and his disciples finally reached the Buddha tathagata, at the foot of lingshan, to accept the test of tathagata, predict how things will happen, Please listen to the following decomposition com.alipay.cxbiz.open.com madmin. Web. Auth. StatelessAuthcFilter# onAccessDenied in this method, we have to verify the legitimacy of the token, Verify user name and password on the basis of valid token. How does shiro play this

UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName, userPwd);
this.getSubject(request, response).login(usernamePasswordToken);
Copy the code

I’m going to wrap userName and userPwd into a class called UsernamePasswordToken, This class is the class of shiro org.. Apache shiro. Subject. Support. DelegatingSubject# login now, The elder brothers began to carry the UsernamePasswordToken to accept test org.. Apache shiro. MGT. DefaultSecurityManager# login

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
        AuthenticationInfo info;
        try {
            info = authenticate(token);
        } catch (AuthenticationException ae) {
            try {
                onFailedLogin(token, ae, subject);
            } catch (Exception e) {
                if (log.isInfoEnabled()) {
                    log.info("onFailedLogin method threw an " +
                            "exception.  Logging and propagating original AuthenticationException.", e);
                }
            }
            throw ae; //propagate
        }

        Subject loggedIn = createSubject(token, info, subject);

        onSuccessfulLogin(token, info, loggedIn);

        return loggedIn;
    }
Copy the code

Info = authenticate(token); org.apache.shiro.authc.AbstractAuthenticator#authenticate

try { info = doAuthenticate(token); If (info == null) {// If null, String MSG = "No Account information found for authentication token [" + token + "] by this "+ "Authenticator instance. Please check that it is configured correctly."; throw new AuthenticationException(msg); }}Copy the code

org.apache.shiro.authc.pam.ModularRealmAuthenticator#doSingleRealmAuthentication org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo

info = doGetAuthenticationInfo(token);
Copy the code

Finally, we reached our custom authentication method UserRealm#doGetAuthenticationInfo com.alipay.cxbiz.open.commadmin.web.auth.UserRealm#doGetAuthenticationInfo

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken userToken = (UsernamePasswordToken)authenticationToken; String userName = userToken.getUsername(); UserEntity user = null; if (userName.startsWith("2088")) { user = new UserEntity(); user.setUserName(userName); user.setPassword(this.commadminConfig.getTinyAppSalt()); } else { user = this.userService.findUserByName(userName); } if (user == null) { return null; } else { SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, user.getPassword(), this.getName()); return simpleAuthenticationInfo; }}Copy the code

This place will be according to the incoming userName to database query user information user = this. UserService. FindUserByName (userName); If the user does not return null. The org, apache shiro. Realm. AuthenticatingRealm# getAuthenticationInfo password comparison in this method, the token is incoming, the info is based on a userName to database queries

if (info ! = null) { assertCredentialsMatch(token, info); }Copy the code

org.apache.shiro.realm.AuthenticatingRealm#assertCredentialsMatch org.apache.shiro.authc.credential.SimpleCredentialsMatcher#doCredentialsMatch

public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenCredentials = getCredentials(token);
        Object accountCredentials = getCredentials(info);
        return equals(tokenCredentials, accountCredentials);
    }
Copy the code

If the password is incorrect, an exception is reported. If the user returns null, an exception is reported. In both cases, authentication fails. At this point, the certification process is completed

Shiro source code 5: Authorization process

It is usually in the code to determine whether the user has this permission

Subject subject = SecurityUtils.getSubject(); If (subject.hasrole ("admin")) {if(subject.hasrole ("admin")) {if(subject.hasrole ("admin")) {if(subject.hasrole ("admin")) {if(subject.hasrole ("admin")) {Copy the code

or

@requirespermissions ("sys:user:user") public List< user > listUser() {//Copy the code
public boolean hasRole(String roleIdentifier) {
        return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier);
    }
Copy the code
public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
        AuthorizationInfo info = getAuthorizationInfo(principal);
        return hasRole(roleIdentifier, info);
    }
Copy the code

org.apache.shiro.realm.AuthorizingRealm#getAuthorizationInfo

 info = doGetAuthorizationInfo(principals);
Copy the code

Then our custom UserRealm# doGetAuthorizationInfo access permissions to the com.alipay.cxbiz.open.com madmin. Web. The auth. UserRealm# doGetAuthorizationInfo

   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String)principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(this.userService.queryRoles(username));
        return authorizationInfo;
    }
Copy the code

Shiro source 6: How is the getObject method in the ShiroFilterFactoryBean called

 public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }
Copy the code
@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(CommadminConfig commadminConfig, LoginFilterUrlConfig loginFilterUrlConfig, SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> map = new LinkedHashMap<String, String>(); map.putAll(loginFilterUrlConfig.getLoginFilterUrlMap()); / / login shiroFilterFactoryBean. SetLoginUrl ("/auth login "); / / homepage shiroFilterFactoryBean setSuccessUrl ("/index "); / / error pages, certification through jump shiroFilterFactoryBean. SetUnauthorizedUrl ("/error "); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); Map<String, Filter> filterMap = new LinkedHashMap<String, Filter>(); StatelessAuthcFilter statelessAuthcFilter = new StatelessAuthcFilter(); statelessAuthcFilter.setCicadaLoginUrl(commadminConfig.getLoginUrl()); statelessAuthcFilter.setAnonTinyService(commadminConfig.getAnonTinyService()); filterMap.put("statelessAuthc", statelessAuthcFilter); shiroFilterFactoryBean.setFilters(filterMap); return shiroFilterFactoryBean; }Copy the code

This is where the @Bean tag is, and spring, when the container is initialized, is in springBoot, which is in this method

protected void onRefresh() { super.onRefresh(); // Initialize spring try {createWebServer(); } / / restart container catch (Throwable ex) {throw new ApplicationContextException (" Unable to start the web server, "ex); }}Copy the code

The class will be scanned and converted into a beanDefinition, which is stored in the Spring container as a map. In this method,

 protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
   return new ServletContextInitializerBeans(getBeanFactory());
}
Copy the code

BeanFactory = beanFactory = beanFactory = beanFactory = beanFactory = beanFactory = beanFactory = beanFactory = beanFactory If not, think of beans as special class-mapped objects

org.springframework.boot.web.servlet.ServletContextInitializerBeans#addAsRegistrationBean

protected <T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
      RegistrationBeanAdapter<T> adapter) {
   addAsRegistrationBean(beanFactory, type, type, adapter);
}
Copy the code

org.springframework.boot.web.servlet.ServletContextInitializerBeans#addAsRegistrationBea The following is to get all the beanName that implements javax.servlet.Filter. Go to the Spring container to get the bean, which is the stuff in the Tomcat container, which will get all the javax.servlet.Filter beans when it starts

private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type, Class<B> beanType, RegistrationBeanAdapter<T> adapter) { List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen); for (Entry<String, B> entry : entries) { String beanName = entry.getKey(); // Get beanName B bean = entry.getValue(); if (this.seen.add(bean)) { // One that we haven't already seen RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size()); int order = getOrder(bean); registration.setOrder(order); this.initializers.add(type, registration); if (logger.isTraceEnabled()) { logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order=" + order + ", resource=" + getResourceDescription(beanName, beanFactory)); }}}}Copy the code

During the getBean process, Will determine if the bean is a factoryBean org. Springframework. Beans. Factory. Support. AbstractBeanFactory# getObjectForBeanInstance Is called if the bean is a factoryBean

At this point, the ShiroFilterFactoryBean calls getObject

Shiro source code 7: ShiroFilterFactoryBean

The BeanPostProcessor is a spring processor that is designed for spring extensions. For details, you can refer to spring’s source code

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Filter) {
            log.debug("Found filter chain candidate filter '{}'", beanName);
            Filter filter = (Filter) bean;
            applyGlobalPropertiesIfNecessary(filter);
            getFilters().put(beanName, filter);
        } else {
            log.trace("Ignoring non-Filter bean '{}'", beanName);
        }
        return bean;
    }
Copy the code

As we said before, in the custom filter except we are in

Com.alipay.cxbiz.open.com madmin. Web. Conf. ShiroConfig# shiroFilterFactoryBean is outside of the filter, the method to add it or have other filterfilterMap.put(“statelessAuthc”, statelessAuthcFilter); GetFilters ().put(beanName, filter); getFilters().put(beanName, filter); In, so that explains why we need BeanPostProcessor, right

Write at the end: source code tips

At the end, shiro’s source code is not difficult, but there are a few thoughts that come from looking at the source code. In fact, these thoughts are the most important ones, and they are the ones that we may use in real projects

1: Shiro makes good use of the springBoot web container. When the web container is started, it will look for the javax.servlet.Filter bean. In the getBean process, it will determine, The bean is not a factoryBean to call its getObject method. Shiro uses the factoryBean feature to complete a mapping between filter and URL

public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }
Copy the code

2: the second is the filter of the chain of responsibility pattern, in fact, in the development of the usual projects, can be used, for example, the store’s discount coupons, I completely can according to user’s some points, holiday breaks, their conditions to determine walk not to walk a filter, or walk a branch, to realize our page can be extended

3: The idea of converting it into different filters is that we configure the checkproof free URL suffix in application.yml

4: So whether it’s in the authorization area, whether it’s in the authorization area, we know that the bottom layer is going to look for some type of bean, so he’s going to collect this AuthorizingRealm type bean, loop through his doGetAuthenticationInfo method to do the authentication, In fact, this idea exists in many places, including the spring source code in the post-processor, beanPostProcessor, is this idea, can bea good extension of our program

public class UserRealm extends AuthorizingRealm {
Copy the code

Well, that’s all for today. If you want to study technology together, you can leave a comment below