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

打印 上一主题 下一主题

主题 874|帖子 874|积分 2622

by emanjusaka from ​ https://www.emanjusaka.top/archives/11 彼岸花开可奈何
本文欢迎分享与聚合,全文转载请留下原文地址。
Shiro权限框架认证失败默认是重定向页面的,这对于前后端分离的项目及其不友好,可能会造成请求404的问题。现在我们自定义过滤器实现认证失败返回json数据。
拦截器就是一道道的关卡,每一道关卡都有各自的职责。

实现思路

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

  • 设置 Shiro 的 ShiroFilterFactoryBean拦截请求进行认证并配置自定义的拦截器。
  • 实现自定义的拦截器,重写认证失败后的逻辑。
实现过程


  • 配置 Shiro 的 ShiroFilterFactoryBean设置拦截请求进行认证
    1. @Bean
    2.     public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    3.         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    4.         shiroFilterFactoryBean.setSecurityManager(securityManager);
    5.         Map<String, String> map = new LinkedHashMap<>();
    6.         //登出
    7.         map.put("/logout", "logout");
    8.         // 登录
    9.         map.put("/login","anon");
    10.         //对所有用户认证
    11.         map.put("/**", "authc");
    12.         Map<String, Filter> filterMap = new HashMap<>();
    13.         // 自定义的拦截器
    14.         filterMap.put("authc",new ShiroLoginFilter());
    15.         shiroFilterFactoryBean.setFilters(filterMap);
    16.         shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
    17.         return shiroFilterFactoryBean;
    18.     }
    复制代码
上面配置对登录接口进行了放行,对其他接口都要进行认证,这个可以根据自己的实际需要去配置。同时还要配置我们的自定义拦截器,拦截器Map 的key要和配置的认证authc一致,否则会不生效。

  • 实现自定义的拦截器,重写认证失败后的逻辑。
    1. package com.icms.shiro.filter;
    2. import com.alibaba.fastjson.JSON;
    3. import com.icms.enu.ExceptionCodeEnum;
    4. import com.icms.exception.CustomException;
    5. import com.icms.page.Result;
    6. import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    7. import org.camunda.bpm.model.bpmn.impl.instance.From;
    8. import javax.servlet.ServletRequest;
    9. import javax.servlet.ServletResponse;
    10. import javax.servlet.http.HttpServletRequest;
    11. import javax.servlet.http.HttpServletResponse;
    12. import java.io.PrintWriter;
    13. /**
    14. * @Author emanjusaka
    15. * @Date 2023/10/25 14:42
    16. * @Version 1.0
    17. */
    18. public class ShiroLoginFilter extends FormAuthenticationFilter {
    19.     @Override
    20.     protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    21.         HttpServletResponse httpResponse = (HttpServletResponse) response;
    22.         HttpServletRequest httpRequest = (HttpServletRequest) request;
    23.         httpResponse.setStatus(200);
    24.         httpResponse.setContentType("application/json;charset=utf-8");
    25.         //解决跨域问题
    26.         if ("OPTIONS".equals(httpRequest.getMethod())){
    27.             httpResponse.setStatus(HttpServletResponse.SC_NO_CONTENT);;
    28.             return true;
    29.         }
    30.         httpResponse.getWriter().print(JSON.toJSONString(new Result(ExceptionCodeEnum.STATUS_CODE_NO_LOGIN)));
    31.         httpResponse.getWriter().flush();
    32.         httpResponse.getWriter().close();
    33.         return false;
    34.     }
    35. }
    复制代码
    这里自定义拦截器继承FormAuthenticationFilter 重写了 onAccessDenied 方法。在onAccessDenied 方法中我们可以返回我们需要的 json 数据,也可以记录日志执行我们自己的方法。这里返回的数据是我自定义的Result 类,里面包含了错误码和错误信息。
    认证失败是我们的业务逻辑的错误而不是网络请求的错误,所以我们把HTTP的状态码设置成了 200 ,通过我们自己定义的Result来返回实际的错误信息。
    注意:Shiro本身是没有解决跨域问题的,我们要自己实现解决Shiro的跨域问题。
    例如/user/getUserInfo接口,没有配置过滤,就会被拦截,这个时候无论是在Controller上还是在接口实现上配置@CrossOrigin,都不会生效。这个时候需要做如下配置:
    1. //解决跨域问题
    2.         if ("OPTIONS".equals(httpRequest.getMethod())){
    3.             httpResponse.setStatus(HttpServletResponse.SC_NO_CONTENT);
    4.             return true;
    5.         }
    复制代码
    自己实现拦截器设置允许跨域也是可以的,这里使用的上述的方法。
扩展知识

Shiro中的拦截器


  • authc拦截器:主要用于实现基于表单的身份验证,它会拦截用户登录表单提交的路径,并在拦截器工厂中配置该路径。此外,它负责创建登录认证所需的Token令牌,并触发登录认证流程。如果用户已经登录,那么将直接进入要访问的路径;如果用户未登录,则访问会被拒绝,并自动跳转到登录页面。
  • authcBasic拦截器:主要用于实现基于HTTP基本认证的身份验证。
  • logout拦截器:主要用于处理用户的注销请求。
  • user拦截器:充当了整个安全管理器的入口,主要负责拦截需要安全控制的请求并进行处理。
  • anon拦截器:这种拦截器允许不需要登录就能访问的资源,通常用于静态资源或者移动端接口。
  • roles拦截器:主要负责用户的角色校验。
  • perms拦截器和roles拦截器:这两个拦截器主要与授权相关,用于处理用户角色和权限相关的请求。
  • port拦截器:它主要拦截网络请求,验证用户是否具有访问特定端口的权限。
  • rest拦截器:用于在Web应用程序中对HTTP请求的请求方法(HTTP method)进行权限过滤和控制。它的作用是限制用户对某些HTTP请求方法的访问权限,例如GET、POST、PUT、DELETE等。通过该过滤器,您可以根据需要来控制某些请求方法的访问权限,并且可以根据不同的请求方法,对不同的用户或用户组进行特定的授权设置。
  • ssl拦截器:主要用于处理SSL协议相关的请求。
  • noSessionCreation拦截器:用于处理无状态会话的过滤器。
  1. public enum DefaultFilter{
  2.   anno(AnonymousFilter.class),
  3.   authc(FormAuthenticationFilter.class),
  4.   authcBasic(BasicHttpAuthenticationFilter.class),
  5.   logout(LogoutFilter.class),
  6.   noSessionCreation(NoSessionCreationFilter.class),
  7.   perms(PermissionsAuthorizationFilter.class),
  8.   port(PortFilter.class),
  9.   rest(HttpMethodPermissionFilter.class),
  10.   roles(RolesAuthorizationFilter.class),
  11.   ssl(SslFilter.class),
  12.   user(UserFilter.class);
  13. }
复制代码
与身份验证相关的拦截器


  • 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的编程栈

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

忿忿的泥巴坨

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表