前言
shiro在进行身份认证时,如果失败了,默认会跳转到Web工程根目录下的"/login.jsp"页面,如果在配置类中配置了这句话:
shiroFilterFactoryBean.setLoginUrl("/myLogin");
认证失败后会跳转到setLoginUrl这个方法指定的路径中。
这里说的跳转,其实就是重定向
但是这样非常不灵活,如果是前后端分离的话,可能就会报错404了。
现在呢,我们想要在身份认证失败后不跳转页面,而是返回JSON数据,让前端自由的控制跳转到什么页面。
步骤
- 重写FormAuthenticationFilter 过滤器,然后重写它的onAccessDenied 方法。
- 注册我们刚刚重写的这个过滤器.
1、重写FormAuthenticationFilter
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setStatus(200);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
JSONObject json = new JSONObject();
json.put("state","403");
json.put("msg","登录已失效,请重新登录!");
out.println(json);
out.flush();
out.close();
return false;
}
}
- 首先使用httpServletResponse 设置http的状态码与内容格式。
- 接着创建一个打印流。
- 实例化一个JSON对象,用来装数据
- 封装数据
- 输出
这里提一下为什么设置了两个状态值,一个是http的状态值,一个是json对象里面的一个状态值。
原因很简单,http的状态值只用来表示这次http请求的状态,与业务逻辑无关,这也是我为什么在json对象里面还要单独写一个状态值,将其分开。
现在很多开发者将其混淆、滥用,这是很不规范的。举个例子,比如用户密码错误这个场景,有的人直接把http状态码写成500,500表示的意思是Internal Server Error(请求未完成。服务器遇到不可预知的情况。换句话说就是服务器内部错误),可是用户密码错误时服务器内部错误吗?显然不是! 所以要分开使用。
2、注册自定义过滤器
由于这篇文章讲的是Spring boot+Shiro,所以这里就只展示Spring boot在配置类中的写法,Spring的xml配置文件写法就不写了,但是思想是一致的。
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
// 创建 ShiroFilterFactoryBean
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
/* ************* 注册自定义授权过滤器 开始******************/
// 1、创建过滤器Map,用来装自定义过滤器
LinkedHashMap<String, Filter> map = new LinkedHashMap<>();
// 2、将自定义过滤器放入map中,如果实现了自定义授权过滤器,那就必须在这里注册,否则Shiro不会使用自定义的授权过滤器
map.put("authc", new MyFormAuthenticationFilter());
// 3、将过滤器Ma绑定到shiroFilterFactoryBean上
shiroFilterFactoryBean.setFilters(map);
/* ************* 注册自定义授权过滤器 结束******************/
//此处省略其他配置
......
return shiroFilterFactoryBean;
}
特别注意:这里map的key值不是随便写的,是要按照Shiro内置的FilterChain的Filter Name来写。
Shiro 内置的 FilterChain
Filter Name | Class |
---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
port | org.apache.shiro.web.filter.authz.PortFilter |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
ssl | org.apache.shiro.web.filter.authz.SslFilter |
user | org.apache.shiro.web.filter.authc.UserFilter |
这里我继承了org.apache.shiro.web.filter.authc.FormAuthenticationFilter,那就得写 “authc”。
如果要重写权限验证器,就重写 PermissionsAuthorizationFilter 在注册时的key写:perms
如果要重写角色验证器,就重写 RolesAuthorizationFilter 在注册时的key写:roles
技 术 无 他, 唯 有 熟 尔。
知 其 然, 也 知 其 所 以 然。
踏 实 一 些, 不 要 着 急, 你 想 要 的 岁 月 都 会 给 你。