• 當前位置:首頁 > IT技術 > 數據庫 > 正文

    Shiro系列教程 AccessControlFilter源碼分析
    2022-02-14 10:37:31


    AccessControlFilter是shiro-web模塊當中比較重要的類,所有的攔截器都繼承此類,分析此類源碼對應使用其它的filter有很大的幫助。

    下圖是shiro-web 提供的filter,每種filter都對應了不同的權限攔截規則,本文主要分析AccessControlFilter。

    Shiro系列教程 AccessControlFilter源碼分析_sed


    AccessControlFilter的繼承關系

    Shiro系列教程 AccessControlFilter源碼分析_sed_02


    ServletContextSupport 源碼比較簡單不做分析。


    AbstractFilter源碼分析


    public abstract class AbstractFilter extends ServletContextSupport implements Filter {

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


    protected FilterConfig filterConfig;


    public FilterConfig getFilterConfig() {
    return filterConfig;
    }


    public void setFilterConfig(FilterConfig filterConfig) {
    this.filterConfig = filterConfig;
    setServletContext(filterConfig.getServletContext());
    }

    //獲取filter的配置參數
    protected String getInitParam(String paramName) {
    FilterConfig config = getFilterConfig();
    if (config != null) {
    return StringUtils.clean(config.getInitParameter(paramName));
    }
    return null;
    }

    //filter初始化方法
    public final void init(FilterConfig filterConfig) throws ServletException {
    //設置配置信息對象FilterConfig
    setFilterConfig(filterConfig);
    try {
    //初始化完畢后回調
    onFilterConfigSet();
    } catch (Exception e) {
    if (e instanceof ServletException) {
    throw (ServletException) e;
    } else {
    if (log.isErrorEnabled()) {
    log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
    }
    throw new ServletException(e);
    }
    }
    }


    protected void onFilterConfigSet() throws Exception {
    }

    }

    NameableFilter源碼分析





    public abstract class NameableFilter extends AbstractFilter implements Nameable {


    //給filter加了名字
    private String name;

    //獲取名字
    protected String getName() {
    //如果為空則返回web.xml filter-name的值
    if (this.name == null) {
    FilterConfig config = getFilterConfig();
    if (config != null) {
    this.name = config.getFilterName();
    }
    }

    return this.name;
    }

    public void setName(String name) {
    this.name = name;
    }

    protected StringBuilder toStringBuilder() {
    String name = getName();
    if (name == null) {
    return super.toStringBuilder();
    } else {
    StringBuilder sb = new StringBuilder();
    sb.append(name);
    return sb;
    }
    }

    }






    OncePerRequestFilter類源碼分析


    public abstract class OncePerRequestFilter extends NameableFilter {

    //后綴
    public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

    //是允許此filter處理
    private boolean enabled = true; //most filters wish to execute when configured, so default to true

    public boolean isEnabled() {
    return enabled;
    }

    public void setEnabled(boolean enabled) {
    this.enabled = enabled;
    }


    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {
    //獲得一個key用來標識當前request(請求)已經執行過一次doFilterInternal
    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();

    //判斷是否已經執行過doFilterInternal
    if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
    log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());
    //跳過當前filter的處理,直接走后續邏輯(說白了就是脫離shiro的處理)
    filterChain.doFilter(request, response);
    } else //noinspection deprecation
    //判斷是否允許執行doFilterInternal 默認是允許的,不走下面的邏輯
    if (/* added in 1.2: */ !isEnabled(request, response) ||
    /* retain backwards compatibility: */ shouldNotFilter(request) ) {
    log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.",
    getName());
    filterChain.doFilter(request, response);
    } else {
    // Do invoke this filter...

    log.trace("Filter '{}' not yet executed. Executing now.", getName());
    //設置標識
    request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

    //執行doFilterInternal邏輯,該方法由子類重寫來執行具體的shiro邏輯
    try {
    doFilterInternal(request, response, filterChain);
    } finally {
    // Once the request has finished, we're done and we don't
    // need to mark as 'already filtered' any more.
    request.removeAttribute(alreadyFilteredAttributeName);
    }
    }
    }


    @SuppressWarnings({"UnusedParameters"})
    protected boolean isEnabled(ServletRequest request, ServletResponse response) throws ServletException, IOException {
    return isEnabled();
    }

    protected String getAlreadyFilteredAttributeName() {
    String name = getName();
    if (name == null) {
    name = getClass().getName();
    }
    //名稱加上后綴
    return name + ALREADY_FILTERED_SUFFIX;
    }


    @Deprecated
    @SuppressWarnings({"UnusedDeclaration"})
    protected boolean shouldNotFilter(ServletRequest request) throws ServletException {
    return false;
    }

    //由子類重寫,這個方法最多被執行一次
    protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
    throws ServletException, IOException;
    }






    AdviceFilter類源碼fe


    //一個AOP的類,在執行chain.doFilter(request, response); 添加了前置 后置 最終三個環繞方法.
    public abstract class AdviceFilter extends OncePerRequestFilter {


    //AOP方法
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
    return true;
    }


    //AOP方法
    @SuppressWarnings({"UnusedDeclaration"})
    protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
    }


    AOP方法
    @SuppressWarnings({"UnusedDeclaration"})
    public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
    }


    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {
    chain.doFilter(request, response);
    }

    public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
    throws ServletException, IOException {

    Exception exception = null;

    try {

    //執行前置AOP方法 根據返回值continueChain覺得是否繼續執行chain.doFilter(request, response);
    boolean continueChain = preHandle(request, response);
    if (log.isTraceEnabled()) {
    log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]");
    }

    //如果preHandle返回true則執行
    if (continueChain) {
    executeChain(request, response, chain);
    }

    //如果不出異常則執行postHandle
    postHandle(request, response);
    if (log.isTraceEnabled()) {
    log.trace("Successfully invoked postHandle method");
    }

    } catch (Exception e) {
    exception = e;
    } finally {
    //異常與否都在最后執行
    cleanup(request, response, exception);
    }
    }

    protected void cleanup(ServletRequest request, ServletResponse response, Exception existing)
    throws ServletException, IOException {
    Exception exception = existing;
    try {
    //AOP方法
    afterCompletion(request, response, exception);
    if (log.isTraceEnabled()) {
    log.trace("Successfully invoked afterCompletion method.");
    }
    } catch (Exception e) {
    if (exception == null) {
    exception = e;
    } else {
    log.debug("afterCompletion implementation threw an exception. This will be ignored to " +
    "allow the original source exception to be propagated.", e);
    }
    }
    //如果executeChain方法出現異常則在這里拋出
    if (exception != null) {
    if (exception instanceof ServletException) {
    throw (ServletException) exception;
    } else if (exception instanceof IOException) {
    throw (IOException) exception;
    } else {
    if (log.isDebugEnabled()) {
    String msg = "Filter execution resulted in an unexpected Exception " +
    "(not IOException or ServletException as the Filter API recommends). " +
    "Wrapping in ServletException and propagating.";
    log.debug(msg);
    }
    throw new ServletException(exception);
    }
    }
    }
    }


    PathMatchingFilter類源碼分析



    public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor {

    //路徑匹配器
    //匹配規則http://blog.csdn.net/nimasike/article/details/70739982
    protected PatternMatcher pathMatcher = new AntPathMatcher();

    //這里存的內容是 例如:
    // /login.jsp [anon]
    // /index.jsp [bar, baz]
    protected Map<String, Object> appliedPaths = new LinkedHashMap<String, Object>();

    //假設你的配置是 /user/** = user, roles[admin, foo]
    //如果這個類是roles path為/user/** config為admin, foo
    //如果這個類是user path為/user/** config為null
    public Filter processPathConfig(String path, String config) {
    String[] values = null;
    if (config != null) {
    values = split(config);
    }

    this.appliedPaths.put(path, values);
    return this;
    }


    //獲得請求路徑
    //假設請求http://localhost/index.jsp?id=18
    //則返回值為/index.jsp
    protected String getPathWithinApplication(ServletRequest request) {
    return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
    }

    //請求路徑與path匹配
    protected boolean pathsMatch(String path, ServletRequest request) {
    String requestURI = getPathWithinApplication(request);
    log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, requestURI);
    return pathsMatch(path, requestURI);
    }


    protected boolean pathsMatch(String pattern, String path) {
    return pathMatcher.matches(pattern, path);
    }


    //這個方法返回false則請求會被中斷
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

    if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
    if (log.isTraceEnabled()) {
    log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");
    }
    return true;
    }

    //首先進行路徑匹配
    for (String path : this.appliedPaths.keySet()) {
    // If the path does match, then pass on to the subclass implementation for specific checks
    //(first match 'wins'):
    if (pathsMatch(path, request)) {
    //匹配到路徑執行isFilterChainContinued
    log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);
    Object config = this.appliedPaths.get(path);
    return isFilterChainContinued(request, response, path, config);
    }
    }

    //如果沒有匹配允許執行
    return true;
    }


    @SuppressWarnings({"JavaDoc"})
    private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
    String path, Object pathConfig) throws Exception {

    //這里判斷是否允許shiro執行 默認允許
    if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
    if (log.isTraceEnabled()) {
    log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}]. " +
    "Delegating to subclass implementation for 'onPreHandle' check.",
    new Object[]{getName(), path, pathConfig});
    }
    //則執行onPreHandle,根據返回值來決定是否繼續允許執行后續的filter
    //所有shiro-fiter都會重寫此方法,如果返回false 則請求會被中斷
    return onPreHandle(request, response, pathConfig);
    }

    if (log.isTraceEnabled()) {
    log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}]. " +
    "The next element in the FilterChain will be called immediately.",
    new Object[]{getName(), path, pathConfig});
    }

    return true;
    }


    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    return true;
    }


    @SuppressWarnings({"UnusedParameters"})
    protected boolean isEnabled(ServletRequest request, ServletResponse response, String path, Object mappedValue)
    throws Exception {
    return isEnabled(request, response);
    }
    }



    AccessControlFilter類源碼

    public abstract class AccessControlFilter extends PathMatchingFilter {

    //默認登錄頁
    public static final String DEFAULT_LOGIN_URL = "/login.jsp";

    public static final String GET_METHOD = "GET";

    public static final String POST_METHOD = "POST";

    private String loginUrl = DEFAULT_LOGIN_URL;

    public String getLoginUrl() {
    return loginUrl;
    }

    public void setLoginUrl(String loginUrl) {
    this.loginUrl = loginUrl;
    }

    protected Subject getSubject(ServletRequest request, ServletResponse response) {
    return SecurityUtils.getSubject();
    }

    //子類根據業務規則覺得是否中斷請求
    protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;

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

    protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;

    //這里調用的isAccessAllowed
    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
    }

    protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
    return pathsMatch(getLoginUrl(), request);
    }

    //保存請求路徑調轉到登錄頁面
    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    saveRequest(request);
    redirectToLogin(request, response);
    }

    //保存請求路徑
    protected void saveRequest(ServletRequest request) {
    WebUtils.saveRequest(request);
    }

    //跳轉到登錄頁
    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    String loginUrl = getLoginUrl();
    WebUtils.issueRedirect(request, response, loginUrl);
    }

    }



    技術交流群:212320390






    本文摘自 :https://blog.51cto.com/u

    開通會員,享受整站包年服務
    国产呦精品一区二区三区网站|久久www免费人咸|精品无码人妻一区二区|久99久热只有精品国产15|中文字幕亚洲无线码