`Shiro`許可權框架認證失敗預設是重定向頁面的,這對於前後端分離的項目及其不友好,可能會造成請求404的問題。現在我們自定義過濾器實現認證失敗返回json數據。 ...
by emanjusaka from https://www.emanjusaka.top/archives/11 彼岸花開可奈何
本文歡迎分享與聚合,全文轉載請留下原文地址。
Shiro
許可權框架認證失敗預設是重定向頁面的,這對於前後端分離的項目及其不友好,可能會造成請求404的問題。現在我們自定義過濾器實現認證失敗返回json數據。
攔截器就是一道道的關卡,每一道關卡都有各自的職責。
實現思路
由於Shiro
預設的過濾器認證失敗後是進行重定向操作的,所以我們考慮自定義過濾器重寫它的邏輯。
-
設置
Shiro
的ShiroFilterFactoryBean
攔截請求進行認證並配置自定義的攔截器。 -
實現自定義的攔截器,重寫認證失敗後的邏輯。
實現過程
-
配置
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
一致,否則會不生效。
-
實現自定義的攔截器,重寫認證失敗後的邏輯。
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中的攔截器
-
authc攔截器:主要用於實現基於表單的身份驗證,它會攔截用戶登錄表單提交的路徑,併在攔截器工廠中配置該路徑。此外,它負責創建登錄認證所需的Token令牌,並觸發登錄認證流程。如果用戶已經登錄,那麼將直接進入要訪問的路徑;如果用戶未登錄,則訪問會被拒絕,並自動跳轉到登錄頁面。
-
authcBasic攔截器:主要用於實現基於HTTP基本認證的身份驗證。
-
logout攔截器:主要用於處理用戶的註銷請求。
-
user攔截器:充當了整個安全管理器的入口,主要負責攔截需要安全控制的請求併進行處理。
-
anon攔截器:這種攔截器允許不需要登錄就能訪問的資源,通常用於靜態資源或者移動端介面。
-
roles攔截器:主要負責用戶的角色校驗。
-
perms攔截器和roles攔截器:這兩個攔截器主要與授權相關,用於處理用戶角色和許可權相關的請求。
-
port攔截器:它主要攔截網路請求,驗證用戶是否具有訪問特定埠的許可權。
-
rest攔截器:用於在Web應用程式中對HTTP請求的請求方法(HTTP method)進行許可權過濾和控制。它的作用是限制用戶對某些HTTP請求方法的訪問許可權,例如GET、POST、PUT、DELETE等。通過該過濾器,您可以根據需要來控制某些請求方法的訪問許可權,並且可以根據不同的請求方法,對不同的用戶或用戶組進行特定的授權設置。
-
ssl攔截器:主要用於處理SSL協議相關的請求。
-
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的編程棧