1. 程式人生 > >ssm+vue 使用shiro後 post請求報錯 Request header field Content-Type is not allowed by Access-Control-Allow-H

ssm+vue 使用shiro後 post請求報錯 Request header field Content-Type is not allowed by Access-Control-Allow-H

ssm+vue 使用shiro後 post請求報錯 Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

出現的問題

當我傳送post請求時,控制檯就報錯這個,很明顯這個是跨域的問題。但是問題是我的跨域肯定是沒有問題的,已經使用了這麼多天,使用的都是跨域啊。而且使用get沒問題,登入(post方式)的時候也沒問題。我用fiddler觀察了介面,發現這個介面其實返回了json資訊 “登入超時,請重新登入”。這更讓我百思不得其解,我一度懷疑我的cors跨域是不是真的有問題,還是其他什麼莫名的bug。

經過長時間的排查和查詢資料後,我發現了問題的所在。

由於我這個專案比較特殊,是前後端分離並且跨域的,所以需要注意的點比較多,突然我找到一篇部落格,和我的專案很像,仔細看了下,似乎和我的問題一樣,是這位老哥的部落格https://blog.csdn.net/madonghyu/article/details/80027387

他在部落格裡說:發現當ajax請求為複雜請求時,cookie無法被攜帶傳輸到伺服器的,導致一直無法訪問。 

我檢查了一下我的請求,用fiddler發現請求時果然沒有cookie

原因分析

其實,在post跨域請求時,一個請求會發送2次,第一次是驗證請求(options)這個請求時通知後臺,我要傳送跨域請求了,第二次才是真實的操作請求。

由於第一次的options是不帶cookie的,所以shiro的身份驗證是不通過的,所以才會返回json“登入超時”,第一次驗證請求未通過,自然第二次請求報錯。只是報錯請求頭啥的,我沒看懂。

解決問題

既然是第一次的options沒通過導致的,那我就判斷是否是options請求,如果是就直接讓它通過。如果不是就走正常的程式

判斷是否是options請求的關鍵程式碼是

        HttpServletResponse httpResponse = (HttpServletResponse) response;

        HttpServletRequest httpRequest = (HttpServletRequest) request;

        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {

            setHeader(httpRequest,httpResponse);

            return true;

        }

    

在這篇部落格裡,他是重寫shiro的UserFilter,實現通過OPTIONS請求,不是必須重寫這個方法,其他方法也行,在xml檔案裡配置就行了。

我是在重寫AuthorizationFilter裡使用的,程式碼如下

package com.wolwo.shiro;



import com.fasterxml.jackson.databind.ObjectMapper;

import com.wolwo.base.util.JsonResult;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.StringUtils;

import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import org.apache.shiro.web.util.WebUtils;

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;

import java.util.Set;



public class MyAuthorizationFilter extends AuthorizationFilter {



    @Override

    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {



        /**

         * 在訪問過來的時候檢測是否為OPTIONS請求,如果是就直接返回true

         */

        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;

        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;

        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {

            setHeader(httpRequest,httpResponse);

            return true;

        }

        //獲得當前物件

        Subject subject = getSubject(servletRequest, servletResponse);

        //判斷是否已登入

        if (null != subject.getPrincipals()) {

            return true;

        }

        //可在此處根據session中存放的使用者許可權,比對路徑,如果擁有該許可權則放行

        return false;

    }



    @Override

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)

            throws IOException {



        saveRequest(request);

        setHeader((HttpServletRequest) request,(HttpServletResponse) response);

        PrintWriter out = response.getWriter();



        //物件轉json,傳給前臺接收

        ObjectMapper mapper = new ObjectMapper();

        JsonResult jsonResult = new JsonResult(JsonResult.NOTLOGIN, "登入超時,請重新登入");

        String result = mapper.writeValueAsString(jsonResult);

        out.println(result);

        out.flush();

        out.close();

        return false;

    }



    /**

     * 為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());

    }

}

這樣請求就ok了