當前位置:首頁 > IT技術 > 數(shù)據(jù)庫 > 正文

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


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

下圖是shiro-web 提供的filter,每種filter都對應了不同的權限攔截規(guī)則,本文主要分析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的配置參數(shù)
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(請求)已經(jīng)執(zhí)行過一次doFilterInternal
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();

//判斷是否已經(jīng)執(zhí)行過doFilterInternal
if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());
//跳過當前filter的處理,直接走后續(xù)邏輯(說白了就是脫離shiro的處理)
filterChain.doFilter(request, response);
} else //noinspection deprecation
//判斷是否允許執(zhí)行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);

//執(zhí)行doFilterInternal邏輯,該方法由子類重寫來執(zhí)行具體的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;
}

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






AdviceFilter類源碼fe


//一個AOP的類,在執(zhí)行chain.doFilter(request, response); 添加了前置 后置 最終三個環(huán)繞方法.
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 {

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

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

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

} catch (Exception e) {
exception = e;
} finally {
//異常與否都在最后執(zhí)行
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方法出現(xiàn)異常則在這里拋出
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 {

//路徑匹配器
//匹配規(guī)則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)) {
//匹配到路徑執(zhí)行isFilterChainContinued
log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);
Object config = this.appliedPaths.get(path);
return isFilterChainContinued(request, response, path, config);
}
}

//如果沒有匹配允許執(zhí)行
return true;
}


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

//這里判斷是否允許shiro執(zhí)行 默認允許
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});
}
//則執(zhí)行onPreHandle,根據(jù)返回值來決定是否繼續(xù)允許執(zhí)行后續(xù)的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();
}

//子類根據(jù)業(yè)務規(guī)則覺得是否中斷請求
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

開通會員,享受整站包年服務立即開通 >