写点什么

自定义过滤器配置 Shiro 认证失败返回 json 数据

作者:emanjusaka
  • 2023-10-27
    湖南
  • 本文字数:3913 字

    阅读完需:约 13 分钟

自定义过滤器配置 Shiro 认证失败返回 json 数据

by emanjusaka from ​ https://www.emanjusaka.top/archives/11 彼岸花开可奈何

本文欢迎分享与聚合,全文转载请留下原文地址。


Shiro权限框架认证失败默认是重定向页面的,这对于前后端分离的项目及其不友好,可能会造成请求 404 的问题。现在我们自定义过滤器实现认证失败返回 json 数据。


拦截器就是一道道的关卡,每一道关卡都有各自的职责。


实现思路

由于Shiro默认的过滤器认证失败后是进行重定向操作的,所以我们考虑自定义过滤器重写它的逻辑。


  1. 设置 ShiroShiroFilterFactoryBean拦截请求进行认证并配置自定义的拦截器。

  2. 实现自定义的拦截器,重写认证失败后的逻辑。

实现过程

  1. 配置 ShiroShiroFilterFactoryBean设置拦截请求进行认证


    @Bean        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();            shiroFilterFactoryBean.setSecurityManager(securityManager);            Map<String, String> map = new LinkedHashMap<>();            //登出            map.put("/logout", "logout");            // 登录            map.put("/login","anon");            //对所有用户认证            map.put("/**", "authc");            Map<String, Filter> filterMap = new HashMap<>();            // 自定义的拦截器            filterMap.put("authc",new ShiroLoginFilter());            shiroFilterFactoryBean.setFilters(filterMap);            shiroFilterFactoryBean.setFilterChainDefinitionMap(map);            return shiroFilterFactoryBean;        }
复制代码


上面配置对登录接口进行了放行,对其他接口都要进行认证,这个可以根据自己的实际需要去配置。同时还要配置我们的自定义拦截器,拦截器 Map 的 key 要和配置的认证authc一致,否则会不生效。


  1. 实现自定义的拦截器,重写认证失败后的逻辑。


    package com.icms.shiro.filter;
import com.alibaba.fastjson.JSON; import com.icms.enu.ExceptionCodeEnum; import com.icms.exception.CustomException; import com.icms.page.Result; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import org.camunda.bpm.model.bpmn.impl.instance.From;
import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter;
/** * @Author emanjusaka * @Date 2023/10/25 14:42 * @Version 1.0 */ public class ShiroLoginFilter extends FormAuthenticationFilter { @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletRequest httpRequest = (HttpServletRequest) request; httpResponse.setStatus(200); httpResponse.setContentType("application/json;charset=utf-8"); //解决跨域问题 if ("OPTIONS".equals(httpRequest.getMethod())){ httpResponse.setStatus(HttpServletResponse.SC_NO_CONTENT);; return true; } httpResponse.getWriter().print(JSON.toJSONString(new Result(ExceptionCodeEnum.STATUS_CODE_NO_LOGIN))); httpResponse.getWriter().flush(); httpResponse.getWriter().close(); return false; } }
复制代码


这里自定义拦截器继承`FormAuthenticationFilter` 重写了 `onAccessDenied` 方法。在`onAccessDenied` 方法中我们可以返回我们需要的 json 数据,也可以记录日志执行我们自己的方法。这里返回的数据是我自定义的`Result` 类,里面包含了错误码和错误信息。
认证失败是我们的业务逻辑的错误而不是网络请求的错误,所以我们把HTTP的状态码设置成了 `200` ,通过我们自己定义的`Result`来返回实际的错误信息。
**注意**:Shiro本身是没有解决跨域问题的,我们要自己实现解决`Shiro`的跨域问题。
例如`/user/getUserInfo`接口,没有配置过滤,就会被拦截,这个时候无论是在Controller上还是在接口实现上配置`@CrossOrigin`,都不会生效。这个时候需要做如下配置:
复制代码


    //解决跨域问题            if ("OPTIONS".equals(httpRequest.getMethod())){                httpResponse.setStatus(HttpServletResponse.SC_NO_CONTENT);                return true;            }
复制代码


自己实现拦截器设置允许跨域也是可以的,这里使用的上述的方法。
复制代码

扩展知识

Shiro 中的拦截器

  1. authc 拦截器:主要用于实现基于表单的身份验证,它会拦截用户登录表单提交的路径,并在拦截器工厂中配置该路径。此外,它负责创建登录认证所需的 Token 令牌,并触发登录认证流程。如果用户已经登录,那么将直接进入要访问的路径;如果用户未登录,则访问会被拒绝,并自动跳转到登录页面。

  2. authcBasic 拦截器:主要用于实现基于 HTTP 基本认证的身份验证。

  3. logout 拦截器:主要用于处理用户的注销请求。

  4. user 拦截器:充当了整个安全管理器的入口,主要负责拦截需要安全控制的请求并进行处理。

  5. anon 拦截器:这种拦截器允许不需要登录就能访问的资源,通常用于静态资源或者移动端接口。

  6. roles 拦截器:主要负责用户的角色校验。

  7. perms 拦截器和 roles 拦截器:这两个拦截器主要与授权相关,用于处理用户角色和权限相关的请求。

  8. port 拦截器:它主要拦截网络请求,验证用户是否具有访问特定端口的权限。

  9. rest 拦截器:用于在 Web 应用程序中对 HTTP 请求的请求方法(HTTP method)进行权限过滤和控制。它的作用是限制用户对某些 HTTP 请求方法的访问权限,例如 GET、POST、PUT、DELETE 等。通过该过滤器,您可以根据需要来控制某些请求方法的访问权限,并且可以根据不同的请求方法,对不同的用户或用户组进行特定的授权设置。

  10. ssl 拦截器:主要用于处理 SSL 协议相关的请求。

  11. noSessionCreation 拦截器:用于处理无状态会话的过滤器。


public enum DefaultFilter{  anno(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);}
复制代码

与身份验证相关的拦截器

  • authc(FormAuthenticationFilter)

  • 基于表单的拦截器;如“/**=authc”,如果没有登录会跳转到相应的登录页面登录。主要属性:usernameParam:表单提交的用户名参数名(username)。passwordParam:表单提交的密码参数名(password)。rememberMeParam:表单提交的记住我参数名(rememberMe)。loginUrl:登录页面地址(/login.jsp)。successUrl:登录成功后的默认重定向地址。failureKeyAttribute:登录失败后错误信息存储 Key(shiroLoginFailure)。

  • authcBasic(BasicHttpAuthenticationFilter)

  • Basic HTTP 身份验证拦截器,主要属性:applicationName:弹出登录框显示的信息(application)。

  • logout(LogoutFilter)

  • 退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/)。示例:“/logout=logout”

  • user(UserFilter)

  • 用户拦截器,用户已经身份验证/记住我登录的都可。示例:“/**=user”

  • anon(AnnonymousFilter)

  • 匿名拦截器,即不需要登录即可访问,一般用于静态资源过滤或者需要在登录之前进行的请求。示例:“/static/**=anon”

与授权相关的拦截器

  • roles(RolesAuthorizationFilter)

  • 角色授权拦截器,验证用户是否拥有所有角色。主要属性:loginUrl:登录页面地址(/login.jsp)。unauthorizedUrl:未授权后重定向的地址。示例:“/admin/**=roles[admin]”

  • perms(PermissionsAuthorizationFilter)

  • 权限授权拦截器,验证用户是否拥有所有权限,属性和 roles 一样。示例:“/user/**=perms[“user:create”]”

  • port(PortFilter)

  • 端口拦截器,主要属性:port(80):可以通过的端口。示例:“/test=port[80]”,如果用户访问该页面是非 80 端口,将自动将端口改为 80 并重定向到该 80 端口,其他路径/参数等都一样。

  • rest(HttpMethodPermissionFilter)

  • rest 风格拦截器,自动根据请求方法构建权限字符串(GET=read,POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read,MKCOL=create)构建权限字符串。示例:“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll)。

  • ssl(SslFilter)

  • SSL 拦截器,只有请求协议是 https 才能通过,否则自动跳转到 https 端口(443),其他和 port 拦截器一样。

其他拦截器

  • noSessionCreation(NoSessionCreationFilter)

  • 不创建会话拦截器,调用 subject.getSession(false)不会有问题,但是如果 subject.getSession(true)将抛出异常。


本文原创,才疏学浅,如有纰漏,欢迎指正。如果本文对您有所帮助,欢迎点赞,并期待您的反馈交流,共同成长。

原文地址: https://www.emanjusaka.top/archives/11

微信公众号:emanjusaka 的编程栈

用户头像

emanjusaka

关注

还未添加个人签名 2022-04-29 加入

分享技术,共同成长。一个编程菜鸟的升级打怪之路~

评论

发布
暂无评论
自定义过滤器配置 Shiro 认证失败返回 json 数据_Java_emanjusaka_InfoQ写作社区