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