Shiro過濾器源碼

過濾器

Shiro還提供了過濾器,可以配置我們的過濾規則,過濾規則對順序是有要求的,短路優先原則,也就是前面的適配成功之後,就不會再適配後面的規則了。

Shiro是一個強大易用的Java安全框架,提供了認證、授權、加密和會話管理等功能,直接查看DefaultFilter類。

路徑如下:org.apache.shiro.web.filter.mgt

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

    private final Class<? extends Filter> filterClass;

    private DefaultFilter(Class<? extends Filter> filterClass) {
        this.filterClass = filterClass;
    }

    public Filter newInstance() {
        return (Filter)ClassUtils.newInstance(this.filterClass);
    }

    public Class<? extends Filter> getFilterClass() {
        return this.filterClass;
    }

    public static Map<String, Filter> createInstanceMap(FilterConfig config) {
        Map<String, Filter> filters = new LinkedHashMap(values().length);
        DefaultFilter[] var2 = values();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            DefaultFilter defaultFilter = var2[var4];
            Filter filter = defaultFilter.newInstance();
            if (config != null) {
                try {
                    filter.init(config);
                } catch (ServletException var9) {
                    String msg = "Unable to correctly init default filter instance of type " + filter.getClass().getName();
                    throw new IllegalStateException(msg, var9);
                }
            }

            filters.put(defaultFilter.name(), filter);
        }

        return filters;
    }
}

AnonymousFilter 匿名攔截器,即不需要登錄即可訪問;一般用於靜態資源過濾;示例/static/**=anon

不需要繼承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 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:其主要行爲就是通知拒絕後的處理、登錄成功後的行爲,登錄失敗後的行爲。基於表單的攔截器;如/**=authc,如果沒有登錄會跳到相應的登錄頁面登錄

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 退出過濾器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;
}

PermissionsAuthorizationFilter 其默認是獲取pathConfig中字段並進行比對,一般來說我們需要自定義permissons集合。權限授權攔截器,驗證用戶是否擁有所有權限;屬性和roles一樣;示例/user/**=perms["user:create"]

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

PortFilter  端口攔截器,主要屬性port(80):可以通過的端口;示例/test= port[80],如果用戶訪問該頁面是非80,將自動將請求端口改爲80並重定向到該80端口,其他路徑/參數等都一樣

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

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章