Shiro DefaultFilter

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

AnonymousFilter

类似于没有这个Filter一样,前置通知默认通过,所以不需要继承AccessControlFilter,只继承PathMatchingFilter即可

@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {
    // Always return true since we allow access to anyone
    return true;
}

AuthenticationFilter

其主要行为就是认证的过滤,是否通过判断(isAccessAllowed)中进行认证判断

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    Subject subject = getSubject(request, response);
    return subject.isAuthenticated();
}

AuthenticatingFilter

其主要行为就是认证成功和认证失败时的处理

// 执行登录的公共方法抽取到了这里
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
    // 创建Token时获取用户名密码的行为抽象化
    AuthenticationToken token = createToken(request, response);
    if (token == null) {
        String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
                "must be created in order to execute a login attempt.";
        throw new IllegalStateException(msg);
    }
    try {
        Subject subject = getSubject(request, response);
        subject.login(token);
        // 登录成功后的行为
        return onLoginSuccess(token, subject, request, response);
    } catch (AuthenticationException e) {
        // 登录失败后的行为
        return onLoginFailure(token, e, request, response);
    }
}

protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception;

protected AuthenticationToken createToken(String username, String password,
                                              ServletRequest request, ServletResponse response) {
    boolean rememberMe = isRememberMe(request);
    String host = getHost(request);
    return createToken(username, password, rememberMe, host);
}

protected AuthenticationToken createToken(String username, String password,
                                          boolean rememberMe, String host) {
    return new UsernamePasswordToken(username, password, rememberMe, host);
}

FormAuthenticationFilter

其主要行为就是通知拒绝后的处理、登录成功后的行为,登录失败后的行为

BasicHttpAuthenticationFilter

基于HTTP methods的身份认证拦截器,配置形如:authcBasic[POST,PUT,DELETE,GET],其他的方法名称配置的均不进行过滤形如authcBasic[hello],形如authcBasic[permissive]默认不进行过滤

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    HttpServletRequest httpRequest = WebUtils.toHttp(request);
    String httpMethod = httpRequest.getMethod();

    Set<String> methods = httpMethodsFromOptions((String[])mappedValue);
    boolean authcRequired = methods.size() == 0;
    for (String m : methods) {
        if (httpMethod.toUpperCase(Locale.ENGLISH).equals(m)) { // list of methods is in upper case
            authcRequired = true;
            break;
        }
    }
    
    if (authcRequired) {
        return super.isAccessAllowed(request, response, mappedValue);
    }
    else {
        return true;
    }
}

private Set<String> httpMethodsFromOptions(String[] options) {
    Set<String> methods = new HashSet<String>();

    if (options != null) {
        for (String option : options) {
            if (!option.equalsIgnoreCase(PERMISSIVE)) {
                methods.add(option.toUpperCase(Locale.ENGLISH));
            }
        }
    }
    return methods;
}

LogoutFilter

protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

    Subject subject = getSubject(request, response);

    // Check if POST only logout is enabled
    if (isPostOnlyLogout()) {

        // check if the current request's method is a POST, if not redirect
        if (!WebUtils.toHttp(request).getMethod().toUpperCase(Locale.ENGLISH).equals("POST")) {
           return onLogoutRequestNotAPost(request, response);
        }
    }

    String redirectUrl = getRedirectUrl(request, response, subject);
    //try/catch added for SHIRO-298:
    try {
        subject.logout();
    } catch (SessionException ise) {
        log.debug("Encountered session exception during logout.  This can generally safely be ignored.", ise);
    }
    issueRedirect(request, response, redirectUrl);
    // 重定向后断开过滤链
    return false;
}

NoSessionCreationFilter

@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, Boolean.FALSE);
    return true;
}
1.If a Subject does not yet have a Session by the time this filter is called, 
this filter effectively disables all calls to subject.getSession() and subject.getSession(true). 
If either are called during the request, an exception will be thrown.

2.However, if the Subject already has an associated session before this filter is invoked, 
either because it was created in another part of the application, 
or a filter higher in the chain created one, this filter has no effect.

1。如果一个主题在这个过滤器被调用的时候还没有一个会话,
这个过滤器有效地禁用了所有对Subject.getsession()和Subject.getsession(true)的调用。
如果在请求期间调用任一种,则会抛出异常。

2。但是,如果在这个过滤器被调用之前,主题已经有了一个相关的会话,
要么是因为它是在应用程序的另一部分中创建的,
要么是在链中更高的过滤器创建了一个过滤器,这个过滤器没有效果。

PermissionsAuthorizationFilter

public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {

    Subject subject = getSubject(request, response);
    // 该过滤器只有mappedValue不为空的时候才会生效,形如perms[file:edit,file:delete]
    String[] perms = (String[]) mappedValue;

    boolean isPermitted = true;
    if (perms != null && perms.length > 0) {
        if (perms.length == 1) {
            if (!subject.isPermitted(perms[0])) {
                isPermitted = false;
            }
        } else {
            if (!subject.isPermittedAll(perms)) {
                isPermitted = false;
            }
        }
    }

    return isPermitted;
}

MappedValue的由来Demo

package com.wjz.demo;

import java.util.Arrays;

import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.util.StringUtils;

public class ShiroStringUtilsDemo {

    public static void main(String[] args) {
        String s = "foo, perms[ file : edit, file : delete ], roles[ admin, user, system ]";
        String[] ss = StringUtils.split(s, StringUtils.DEFAULT_DELIMITER_CHAR, '[', ']', true, true);
        for (String si : ss) {
            String[] sis = toNameConfigPair(si);
            System.out.println("Filter Name: " + sis[0]+" || "+"Config (MappedValue): "+sis[1]);
            if (sis[1] != null) {
                for (String siss : StringUtils.split(sis[1])) {
                    System.out.println("Path Config: "+ siss);
                }
            }
        }
        System.out.println("====================================");
        String[] vals = new String[] {"PUT", "DELETE"};
        System.out.println(Arrays.binarySearch(vals, "permissive") >= 0);
    }
    
    protected static String[] toNameConfigPair(String token) throws ConfigurationException {

        try {
            String[] pair = token.split("\[", 2);
            String name = StringUtils.clean(pair[0]);

            if (name == null) {
                throw new IllegalArgumentException("Filter name not found for filter chain definition token: " + token);
            }
            String config = null;

            if (pair.length == 2) {
                config = StringUtils.clean(pair[1]);
                //if there was an open bracket, it assumed there is a closing bracket, so strip it too:
                config = config.substring(0, config.length() - 1);
                config = StringUtils.clean(config);

                //backwards compatibility prior to implementing SHIRO-205:
                //prior to SHIRO-205 being implemented, it was common for end-users to quote the config inside brackets
                //if that config required commas.  We need to strip those quotes to get to the interior quoted definition
                //to ensure any existing quoted definitions still function for end users:
                if (config != null && config.startsWith(""") && config.endsWith(""")) {
                    String stripped = config.substring(1, config.length() - 1);
                    stripped = StringUtils.clean(stripped);

                    //if the stripped value does not have any internal quotes, we can assume that the entire config was
                    //quoted and we can use the stripped value.
                    if (stripped != null && stripped.indexOf('"') == -1) {
                        config = stripped;
                    }
                    //else:
                    //the remaining config does have internal quotes, so we need to assume that each comma delimited
                    //pair might be quoted, in which case we need the leading and trailing quotes that we stripped
                    //So we ignore the stripped value.
                }
            }
            
            return new String[]{name, config};

        } catch (Exception e) {
            String msg = "Unable to parse filter chain definition token: " + token;
            throw new ConfigurationException(msg, e);
        }
    }
}

PortFilter

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    int requiredPort = toPort(mappedValue);
    int requestPort = request.getServerPort();
    return requiredPort == requestPort;
}

/** 
* 默认过滤80端口的请求 * /some/path/** = port * 过滤8080端口的请求 * /another/path/** = port[8080] *
*/ protected int toPort(Object mappedValue) { String[] ports = (String[]) mappedValue; if (ports == null || ports.length == 0) { return getPort(); } if (ports.length > 1) { throw new ConfigurationException("PortFilter can only be configured with a single port. You have " + "configured " + ports.length + ": " + StringUtils.toString(ports)); } return Integer.parseInt(ports[0]); }
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {

    //just redirect to the specified port:
    int port = toPort(mappedValue);

    String scheme = getScheme(request.getScheme(), port);

    StringBuilder sb = new StringBuilder();
    sb.append(scheme).append("://");
    sb.append(request.getServerName());
    if (port != DEFAULT_HTTP_PORT && port != SslFilter.DEFAULT_HTTPS_PORT) {
        sb.append(":");
        sb.append(port);
    }
    if (request instanceof HttpServletRequest) {
        sb.append(WebUtils.toHttp(request).getRequestURI());
        String query = WebUtils.toHttp(request).getQueryString();
        if (query != null) {
            sb.append("?").append(query);
        }
    }

    WebUtils.issueRedirect(request, response, sb.toString());

    return false;
}

protected String getScheme(String requestScheme, int port) {
    if (port == DEFAULT_HTTP_PORT) {
        return HTTP_SCHEME;
    // 443端口 : https    
    } else if (port == SslFilter.DEFAULT_HTTPS_PORT) {
        return SslFilter.HTTPS_SCHEME;
    } else {
        return requestScheme;
    }
}
原文地址:https://www.cnblogs.com/BINGJJFLY/p/9372391.html