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

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

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

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

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

实现思路

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

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

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

实现过程

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

    java 复制代码
    @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. 实现自定义的拦截器,重写认证失败后的逻辑。

    java 复制代码
    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,都不会生效。这个时候需要做如下配置:

    java 复制代码
    //解决跨域问题
            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拦截器:用于处理无状态会话的过滤器。

java 复制代码
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的编程栈

相关推荐
xyliiiiiL13 分钟前
一文总结常见项目排查
java·服务器·数据库
shaoing15 分钟前
MySQL 错误 报错:Table ‘performance_schema.session_variables’ Doesn’t Exist
java·开发语言·数据库
腥臭腐朽的日子熠熠生辉1 小时前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯2 小时前
算法日常记录
java·算法·leetcode
27669582922 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息2 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen2 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算