博客 / 詳情

返回

自定義過濾器配置 Shiro 認證失敗返回 json 數據

by emanjusaka from https://www.emanjusaka.com/2023/10/filter-shiro-authenticatio... 彼岸花開可奈何
本文歡迎分享與聚合,全文轉載請留下原文地址。

​Shiro​​​權限框架認證失敗默認是重定向頁面的,這對於前後端分離的項目及其不友好,可能會造成請求404的問題。現在我們自定義過濾器實現認證失敗返回json數據。

攔截器就是一道道的關卡,每一道關卡都有各自的職責。

實現思路

由於Shiro​​默認的過濾器認證失敗後是進行重定向操作的,所以我們考慮自定義過濾器重寫它的邏輯。

  1. 設置 Shiro​​ 的 ShiroFilterFactoryBean​​攔截請求進行認證並配置自定義的攔截器。
  2. 實現自定義的攔截器,重寫認證失敗後的邏輯。

實現過程

  1. 配置 Shiro​​ 的 ShiroFilterFactoryBean​​設置攔截請求進行認證

    @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.com/2023/10/filter-shiro-authenticatio...
微信公眾號:emanjusaka的編程棧
user avatar FatTiger4399 頭像 docker_app 頭像 fulng 頭像 chazhoudeqingchun 頭像 edagarli 頭像 huzilachadedanche 頭像
6 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.