SpringBoot+Shiro放行OPTIONS請求,解決跨域問題
阿新 • • 發佈:2021-11-24
問題:
整合shiro之後發現配置放行的介面可以正常訪問,而需要登入驗證的介面會報錯
其中OPTIONS型別的介面會報302
導致後續的post請求報錯提示跨域問題
Shiro登入流程
首先Shiro是根據請求中cookie攜帶的JSESSIONID判斷是否登入的
當呼叫登入介面登入成功時,後端的響應頭會新增一個set-cookie的引數
JSESSIONID代表當前登入的使用者,前端只要在請求中攜帶這個引數Shiro就可以識別出使用者並放行。
但是post請求會先發送一個OPTIONS型別的探測請求,由於這個請求沒有攜帶JSESSIONID,Shiro就會判斷為未登入,進行攔截。
所以解決的思路就是放行OPTIONS型別的請求。
解決
- 建立ShiroUserFilter
package com.school.service.config; import com.school.service.entity.Result; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.web.filter.authc.UserFilter; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.RequestMethod; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @Slf4j public class ShiroUserFilter extends UserFilter { /** * 在訪問過來的時候檢測是否為OPTIONS請求,如果是就直接返回true */ @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletRequest httpRequest = (HttpServletRequest) request; if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { log.info("OPTIONS放行"); setHeader(httpRequest,httpResponse); return true; } return super.preHandle(request,response); } /** * 該方法會在驗證失敗後呼叫,這裡由於是前後端分離,後臺不控制頁面跳轉 * 因此重寫改成傳輸JSON資料 */ @Override protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException { saveRequest(request); setHeader((HttpServletRequest) request,(HttpServletResponse) response); PrintWriter out = response.getWriter(); //自己控制返回的json資料 out.println(new Result("500",null,"Shiro驗證失敗")); out.flush(); out.close(); } /** * 為response設定header,實現跨域 */ private void setHeader(HttpServletRequest request, HttpServletResponse response){ //跨域的header設定 response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Methods", request.getMethod()); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers")); //防止亂碼,適用於傳輸JSON資料 response.setHeader("Content-Type","application/json;charset=UTF-8"); response.setStatus(HttpStatus.OK.value()); } }
- 在Shiro配置類的shiroFilter中新增進去
package com.school.service.config; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * Shiro配置類 */ @Configuration public class ShiroConfig { @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/school/goToLogin");//設定登入頁面 shiroFilterFactoryBean.setUnauthorizedUrl("/school/goToLogin");//許可權不足跳轉頁面,這個在Default過濾器中設定無效,具體看 https://blog.csdn.net/bicheng4769/article/details/86680955 //新增自定義Filter,放行OPTIONS請求-----------------------------------------這裡✨✨✨✨✨✨✨✨ Map<String, Filter> filters = shiroFilterFactoryBean.getFilters(); filters.put("authc", new ShiroUserFilter()); shiroFilterFactoryBean.setFilters(filters); //----------------------------------------------------------------------------------------------✨ Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問--> filterChainDefinitionMap.put("/service/school/**", "anon"); filterChainDefinitionMap.put("/swagger-ui.html", "anon"); filterChainDefinitionMap.put("/swagger-resources", "anon"); filterChainDefinitionMap.put("/v2/api-docs", "anon"); filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon"); filterChainDefinitionMap.put("/configuration/security", "anon"); filterChainDefinitionMap.put("/configuration/ui", "anon"); filterChainDefinitionMap.put("/service/article/**", "authc"); filterChainDefinitionMap.put("/service/chat/**", "authc"); filterChainDefinitionMap.put("/service/diary/**", "authc"); filterChainDefinitionMap.put("/service/file/**", "authc"); filterChainDefinitionMap.put("/service/problem/**", "authc"); filterChainDefinitionMap.put("/service/team-article/**", "authc"); filterChainDefinitionMap.put("/service/team/**", "authc"); filterChainDefinitionMap.put("/service/user/**", "authc"); filterChainDefinitionMap.put("/service/user-friend/**", "authc"); filterChainDefinitionMap.put("/service/user-info/**", "authc"); filterChainDefinitionMap.put("/service/widget/**", "authc"); //主要這行程式碼必須放在所有許可權設定的最後,不然會導致所有 url 都被攔截 剩餘的都需要認證 filterChainDefinitionMap.put("/**", "anon"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(myRealm()); return defaultWebSecurityManager; } @Bean public MyRealm myRealm (){ MyRealm myRealm = new MyRealm(); return myRealm; } }