Source code analysis of shiro filter

Posted by bdemo2 on Fri, 29 May 2020 18:11:48 +0200

I. configuration

<!-- Shiro Of Web filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <!-- Injection Security Manager-->
    <property name="securityManager" ref="securityManager"/>
    <!-- Set up login URL-->
    <property name="loginUrl" value="/login"/>
    <property name="filters">
        <util:map>
            <!-- register authc and sysUser-->
            <entry key="authc" value-ref="formAuthenticationFilter"/>
            <entry key="sysUser" value-ref="sysUserFilter"/>
        </util:map>
    </property>
    <property name="filterChainDefinitions">
        <value>
            /login = authc
            /logout = logout
            /authenticated = authc
            /** = user,sysUser
        </value>
    </property>
</bean>

II. Filter chain definitions where the node is injected

The setFilterChainDefinitions method of ShiroFilterFactoryBean will inject the filter chain definition as follows:

public void setFilterChainDefinitions(String definitions) {
    Ini ini = new Ini();
    ini.load(definitions);
    Section section = ini.getSection("urls");
    if (CollectionUtils.isEmpty(section)) {
        section = ini.getSection("");
    }
    this.setFilterChainDefinitionMap(section);
}

After executing the above code, the value of section is as follows:

section = {Ini$Section@5444}  size = 4
"/login" -> "authc"
"/logout" -> "logout"
"/authenticated" -> "authc"
"/**" -> "user,sysUser"

At the same time, the variable filterChainDefinitionMap of ShiroFilterFactoryBean becomes the following

filterChainDefinitionMap = {Ini$Section@5444}  size = 4
"/login" -> "authc"
"/logout" -> "logout"
"/authenticated" -> "authc"
"/**" -> "user,sysUser"

3. filters where the node is injected

public void setFilters(Map<String, Filter> filters) {
    this.filters = filters;
}

What it looks like after injection:

this = {ShiroFilterFactoryBean@5425}
securityManager = {DefaultWebSecurityManager@5428}
filters = {LinkedHashMap@5426}  size = 2
  "authc" -> {FormAuthenticationFilter@5436} "org.apache.shiro.web.filter.authc.FormAuthenticationFilter@3435b6d8"
  "sysUser" -> {SysUserFilter@5438} "com.github.cakin.shiro.chapter16.web.shiro.filter.SysUserFilter@13553828"

IV. protected FilterChainManager createFilterChainManager() after finishing the work

manager = {DefaultFilterChainManager@5681}
filterConfig = null
filters = {LinkedHashMap@5682}  size = 12
  "anon" -> {AnonymousFilter@5698} "anon"
  "authc" -> {FormAuthenticationFilter@5751} "authc"
  "authcBasic" -> {BasicHttpAuthenticationFilter@5702} "authcBasic"
  "logout" -> {LogoutFilter@5704} "logout"
  "noSessionCreation" -> {NoSessionCreationFilter@5706} "noSessionCreation"
  "perms" -> {PermissionsAuthorizationFilter@5708} "perms"
  "port" -> {PortFilter@5710} "port"
  "rest" -> {HttpMethodPermissionFilter@5712} "rest"
  "roles" -> {RolesAuthorizationFilter@5714} "roles"
  "ssl" -> {SslFilter@5716} "ssl"
  "user" -> {UserFilter@5718} "user"
  "sysUser" -> {SysUserFilter@5862} "sysUser"
filterChains = {LinkedHashMap@5683}  size = 4
  "/login" -> {SimpleNamedFilterList@6101}  size = 1
  "/logout" -> {SimpleNamedFilterList@6103}  size = 1
  "/authenticated" -> {SimpleNamedFilterList@6105}  size = 1
  "/**" -> {SimpleNamedFilterList@6107}  size = 2

filters: the default filter plus the filter registered in the configuration file.

filterChains: the configuration value in the configuration file.

V. AbstractShiroFilter analysis

1. If you add a breakpoint to the executeChain function, click http://localhost:8080/chapter16/ After the method runs, the variables are as follows:

chain = {ProxiedFilterChain@6960}
orig = {ServletHandler$CachedChain@6959} "spring"
filters = {SimpleNamedFilterList@6107}  size = 2
  0 = {UserFilter@5718} "user"
  1 = {SysUserFilter@5862} "sysUser"
index = 0

The call is as follows:

executeChain:449, AbstractShiroFilter (org.apache.shiro.web.servlet)
call:365, AbstractShiroFilter$1 (org.apache.shiro.web.servlet)
doCall:90, SubjectCallable (org.apache.shiro.subject.support)
call:83, SubjectCallable (org.apache.shiro.subject.support)
execute:383, DelegatingSubject (org.apache.shiro.subject.support)
doFilterInternal:362, AbstractShiroFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
invokeDelegate:344, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:261, DelegatingFilterProxy (org.springframework.web.filter)

2. From the above analysis, the user filter and SysUserFilter will be used

UserFilter will go to the following functions

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    if (isLoginRequest(request, response)) {
        return true;
    } else {
        Subject subject = getSubject(request, response);
        // If principal is not null, then the user is known and should be allowed access.
        return subject.getPrincipal() != null;
    }
}

When you go to isAccessAllowed, its call stack is as follows:

isAccessAllowed:50, UserFilter (org.apache.shiro.web.filter.authc)
onPreHandle:162, AccessControlFilter (org.apache.shiro.web.filter)
isFilterChainContinued:203, PathMatchingFilter (org.apache.shiro.web.filter)
preHandle:178, PathMatchingFilter (org.apache.shiro.web.filter)
doFilterInternal:131, AdviceFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
doFilter:66, ProxiedFilterChain (org.apache.shiro.web.servlet)
executeChain:449, AbstractShiroFilter (org.apache.shiro.web.servlet)
call:365, AbstractShiroFilter$1 (org.apache.shiro.web.servlet)
doCall:90, SubjectCallable (org.apache.shiro.subject.support)
call:83, SubjectCallable (org.apache.shiro.subject.support)
execute:383, DelegatingSubject (org.apache.shiro.subject.support)
doFilterInternal:362, AbstractShiroFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
invokeDelegate:344, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:261, DelegatingFilterProxy (org.springframework.web.filter)

SysUserFilter will go to the following functions

protected boolean onPreHandle( ServletRequest request, ServletResponse response, Object mappedValue ) {
    // Get user information
    String username = (String) SecurityUtils.getSubject().getPrincipal();
    // Set user information to the "user" attribute
    request.setAttribute(Constants.CURRENT_USER, userService.findByUsername(username));
    return true;
}

When you go to the function onPreHandle, its call stack is as follows:

onPreHandle:38, SysUserFilter (com.github.cakin.shiro.chapter16.web.shiro.filter)
isFilterChainContinued:203, PathMatchingFilter (org.apache.shiro.web.filter)
preHandle:178, PathMatchingFilter (org.apache.shiro.web.filter)
doFilterInternal:131, AdviceFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
doFilter:66, ProxiedFilterChain (org.apache.shiro.web.servlet)
executeChain:108, AdviceFilter (org.apache.shiro.web.servlet)
doFilterInternal:137, AdviceFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
doFilter:66, ProxiedFilterChain (org.apache.shiro.web.servlet)
executeChain:449, AbstractShiroFilter (org.apache.shiro.web.servlet)
call:365, AbstractShiroFilter$1 (org.apache.shiro.web.servlet)
doCall:90, SubjectCallable (org.apache.shiro.subject.support)
call:83, SubjectCallable (org.apache.shiro.subject.support)
execute:383, DelegatingSubject (org.apache.shiro.subject.support)
doFilterInternal:362, AbstractShiroFilter (org.apache.shiro.web.servlet)
doFilter:125, OncePerRequestFilter (org.apache.shiro.web.servlet)
invokeDelegate:344, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:261, DelegatingFilterProxy (org.springframework.web.filter)

We can analyze the runtime call from the call stack.

Topics: Shiro Apache Spring github