过滤器
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;
}
}