在使用shiro时,一般对未认证或未授权的请求统一过滤并做出响应,大体有以下配置:
<!-- 未认证时返回的页面(被authc user logout 等认证拦截器拦截后)访问的url -->
<property name="loginUrl" value="/user/unAuthenticated.do"/>
<!-- 未授权时返回的页面(被roles 等授权拦截器拦截后)访问的url -->
<property name="unauthorizedUrl" value="/user/unAuthorized.do"/>
或者在java原始配置中:
shiroFilterFactoryBean.setLoginUrl("/user/unAuthenticated.do");
shiroFilterFactoryBean.setUnauthorizedUrl("/user/unAuthorized.do");
但默认情况下,拦截器拦截后,是重定向到相应的url。这就造成一个问题,在前后端分离的项目中,前端无法对为认证或未授权的页面做出处理。而理想状态下是应该shiro对未认证或未授权的url拦截后,返回json,前端通过json再做出相应的提示或跳转页面。这就需要后端使用shiro时要 自 定 义 过 滤 器 \color{red}{自定义过滤器} 自定义过滤器
默认情况下,几个常用的拦截器总结如下:
有关认证的拦截器:
拦截器名 | 默认拦截器类 | 说明 |
---|---|---|
authc | FormAuthenticationFilter | 需要认证才可以访问 主要属性: loginUrl:登录的url,默认login.jsp,如果被这个过滤器拦截后,会重定向这个url successUrl:登录成功后重定向的url |
user | UserFilter | 需要认证或记住我才可以访问 |
logout | LogoutFilter | 退出url 主要属性: redirectUrl:退出后重定向的url,默认"/“ 如设置”/logout=logout"后,当访问/logout会被拦截,执行退出,再重定向到redirectUrl |
anon | AnonymousFilter | 不拦截,总会放行 |
有关授权的拦截器:
拦截器名 | 默认拦截器类 | 说明 |
---|---|---|
roles | RolesAuthorizationFilter | 验证当前用户是否拥有所有角色 主要属性: unauthorizedUrl:未授权重定向的url loginUrl:登录页面的url 如配置"/admin/**=roles[user,admin]"则访问/admin/时,如果该用户没有同事拥有user和admin两种角色,则重定向到unauthorizedUrl |
perms | PermissionsAuthorizationFilter | 验证当前用户是否拥有所有权限,主要属性和roles一致 |
换句话说,默认情况下,当shiro发生拦截行为时,会调用相应的拦截器重定向到相应的url。因为,如果希望它不发生重定向,而是直接返回json的话,需要我们自定义拦截器并使用自定义的拦截器。
以下对两个常用的拦截做示例(authc和roles)
在此之前,关注这几个拦截类的共同父类AccessControlFilter(eclipse中使用ctrl+T可以查看继承树),其中的onPreHandle方法:
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}
过滤的原则有两个决定,isAccessAllowed即判断请求的url是否合法,onAccessDenied即对不合法的请求的拦截后执行的逻辑。我们想要更改的是令shiro拦截后返回json,所以仅需改onAccessDenied即可。
对未认证页面的过滤
从上面表格中可以看出,authc使用的是FormAuthenticationFilter拦截器,查看onAccessDenied方法的源码如下(eclipse使用快捷键ctrl+shift+H可以搜索类):
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// 先判断访问的是否是登录页面,如果是则放行
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request