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