shiro安全控制目录
Shiro既然作为一个安全框架,必然有一个核心的Filter来过滤处理请求。若是在web.xml配置繁杂的shiroFilter过滤器链,不仅会依赖底层容器,而且不能利用Spring容器的特性。于是通过DelegatingFilterProxy类,使用Spring来管理Servlet Filter,并且还可以在其中定义一系列的Shiro Filter。
1. shiro Filter
拦截器名 | 拦截器类 | 说明 |
---|---|---|
认证 | ||
authc | FormAuthenticationFilter | 基于表单的拦截器,没有认证会跳到相应的登录页面登录; |
authcBasic | BasicHttpAuthenticationFilter | Basic HTTP身份验证拦截器,Realm进行认证; |
logout | LogoutFilter | 登出拦截器; |
user | UserFilter | 用户拦截器,用户认证后和RememberMe都可通过;shiro authc和user的区别 |
授权 | ||
roles | RolesAuthorizationFilter | 角色授权拦截器,验证用户是否拥有所有角色; |
perms | PermissionsAuthorizationFilter | 权限授权拦截器,验证用户是否拥有所有权限; |
port | PortFilter | 端口拦截器 |
rest | HttpMethodPermissionFilter | rest风格拦截器,自动根据请求方法构建权限字符串,示例“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll); |
ssl | authz.SslFilter | SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样; |
一般来说,用户的登录/注册请求不进行拦截,故使用anon拦截器。当调用Realm完成认证后,可以对url路径进行认证/授权过滤器配置(其作用可以替代方法上的角色/权限注解),例如对/**=authc配置,即对所有路径都会都会校验是否完成认证操作。
1.2 拦截器的书写风格
- 支持Ant风格模式;
- ?:匹配一个字符;
- *:匹配零个或多个字符串;
- **:匹配零个或多个路径;
1.3 拦截器匹配顺序
采用的是第一次匹配优先的方式。即使用第一个匹配的url拦截器链。
如:
-/bb/**=filter1
-/bb/aa=filter2
-/**=filter3
实际上请求的url是"\bb\aa",那么将使用filter1进行拦截。
1.4 过滤器的意义
1.4.1. 无参过滤器
- /admin/** = anon :无参,表示可匿名访问
- /admin/user/** = authc :无参,表示需要认证才能访问
- /admin/user/** = authcBasic :无参,表示需要httpBasic认证才能访问
- /admin/user/** = ssl :无参,表示需要安全的URL请求,协议为https
- /home = user :表示用户不一定需要通过认证,只要曾被 Shiro 记住过登录状态就可以正常发起 /home 请求。【即结束会话后,但是shiro记住了登录状态,依旧可以登录】
1.4.2. 有参过滤器
- /edit = authc,perms[admin:edit] :表示用户必需已通过认证,并拥有 admin:edit 权限才可以正常发起 /edit 请求。
- /admin = authc,roles[admin] :表示用户必需已通过认证,并拥有 admin 角色才可以正常发起 /admin 请求。
- /admin/user/** = port[8081] :当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString。
- /admin/user/** = rest[user] :根据请求方式来识别,相当于 /admins/user/**=perms[user:get]或perms[user:post] 等等。
- /admin** = roles["admin,guest"] :允许多个参数(逗号分隔),此时要全部通过才算通过,相当于hasAllRoles()。
/admin** = perms["user:add:,user:del:"]:允许多个参数(逗号分隔),此时要全部通过才算通过,相当于isPermitedAll()
1.4.3. 退出登录
- /logout = logout
1.5 拦截器的方法
1.5.1. AdviceFilter
AdviceFilter提供了AOP风格的支持,类似于SpringMVC中的Interceptor
boolean preHandle(ServletRequest request, ServletResponse response) throws Exception
void postHandle(ServletRequest request, ServletResponse response) throws Exception
void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception;
1.5.2. PathMatchingFilter
PathMatchingFilter提供了基于Ant风格的请求路径匹配功能以及拦截器参数解析的功能。
boolean pathsMatch(String path, ServletRequest request)
boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception
- pathsMatch:该方法用于path与请求路径进行匹配的方法;如果匹配返回true
- onPreHandle:在preHandle中,当pathsMatch匹配一个路径后,会调用onPreHandler方法并将路径绑定参数配置传给mappedValue;然后可以在这个方法中进行一些验证(如角色授权),如果验证失败可以返回false中断流程;默认返回true;也就是说子类可以只实现onPreHandle即可,无须实现preHandle。如果没有path与请求路径匹配,默认是通过的(即preHandle返回true)。
1.5.3 AccessControlFilter
AccessControlFilter提供了访问控制的基础功能;比如是否允许访问/当访问拒绝时如何处理等:
abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception; boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;
abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;
- isAccessAllowed:表示是否允许访问,mappedValue就是[urls]配置的拦截器参数部分,如果允许则返回true,否则返回false。若返回false,则会调用onAccessDenied方法。
- onAccessDenied:当拒绝访问时,请求是否处理;若返回true,则表示需要继续处理,若返回false,则证明该请求已被最终处理,直接返回给浏览器。
注:若是onAccessDenied [dɪˈnaɪd] 返回true,说明该请求通过该过滤器,会继续调用后面的过滤器链或者直接请求到对应资源。
另外AccessControlFilter还提供了如下方法处理如登录后/重定向到上一个请求:
//身份验证时使用,默认/login.jsp
void setLoginUrl(String loginUrl)
String getLoginUrl()
//获取Subject实例
Subject getSubject(ServletRequest request, ServletResponse response)
//当前请求是否是登录请求
boolean isLoginRequest(ServletRequest request, ServletResponse response)
//将当前请求保存起来并重定向到登录页面
void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException
//将请求保存起来,如登录成功后再重定向回该请求
void saveRequest(ServletRequest request)
若是我们想进行访问控制,可以继承AccessControlFilter;如果我们想添加一些通用数据,我们可以直接继承PathMatchingFilter。
2. shiro自定义过滤器项目实战
注:每一次请求,都会按照从上到下的配置调用shiro配置的过滤器链。
实战一:/admin** = roles[admin,guest]含义是用户拥有所有角色信息才能访问该URL地址,但是我们自定义一个过滤器,拥有某一角色,便可访问。
/login/init/** = anon
/admin** =rolesOr[admin,guest]
/** = user
可参考org.apache.shiro.web.filter.authz.RolesAuthorizationFilter实现自定义过滤器。
@Service("rolesOrFilter")
public class RolesOrFilter extends AuthorizationFilter {
/**
* 授权过滤器,是否允许登录
*
* @param request
* @param response
* @param mappedValue
* @return
* @throws Exception
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
//调用父类方法获取subject对象
Subject subject = getSubject(request, response);
//自定义拦截器会被配置为一个标签,我们会用标签控制访问路径
String[] roles = (String[]) mappedValue;
if (roles != null && roles.length != 0) {
for (String role : roles) {
if (subject.hasRole(role)) {
return true;
}
}
}
return false;
}
}
案例二当session过期后,用户使用ajax调用时,返回特定的状态码,而不是shiro默认的重定向到登录页面。
SystemUserFilter
继承org.apache.shiro.web.filter.authc.UserFilter
类,当一个请求url匹配该shiro Filter,便执行isAccessAllowed
方法(因为自定义类并未重写,故继承父类的方法),判断用户是否认证或设置RememberMe,若设置,则返回true放行。否则的话,执行onAccessDenied
方法,若onAccessDenied依旧返回false,则拒绝该次请求。
@Service("userFilter")
public class SystemUserFilter extends UserFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (isAjaxRequest(httpRequest)) {
httpResponse.setStatus(911); // ajax, session超时
} else {
saveRequestAndRedirectToLogin(request, response);
}
return false;
}
private boolean isAjaxRequest(HttpServletRequest httpRequest) {
String accept = httpRequest.getHeader("accept");
String xRequestedWith = httpRequest.getHeader("X-Requested-With");
// 如果是异步请求
return ((accept != null && accept.indexOf("application/json") != -1)
|| (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1));
}
}
文章参考
shiro中Filter过滤器管理