springBoot跨域解決

首先配置允許跨域

@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .maxAge(3600);
    }
}

因項目需對用戶校驗,因此配置了過濾器,
然後出現問題:

  **1.過濾器攔截後,頁面提示沒有跨域
  2.在過濾器配置跨域後,返回responsebody是空的**

代碼如下:


import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
@WebFilter(filterName = "sessionFilter", urlPatterns = {"/*"})
public class SessionFilter implements Filter {

    @Value("${manage.session.timeout}")
    private long timeout;

    private final ObjectMapper mapper = new ObjectMapper();

    //不需要登錄就可以訪問的路徑(比如:註冊登錄等)
    String[] includeUrls = new String[]{
            "/web-manage/su/submitLogin", "/su/submitLogin"};

    /**
     * 登錄校驗
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        log.info("Headers:{}", response.getHeader("Access-Control-Request-Headers"));
        //String origin = request.getHeader("Origin");
        //if (origin == null) {
        //    origin = request.getHeader("Referer");
        //}
         //response.setHeader("Access-Control-Allow-Origin", origin);
        //使用通配符responseBody是空的!放開上段代碼後項目正常
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");
        response.setHeader("Access-Control-Expose-Headers", "*");
        String uri = request.getRequestURI();
        String jsessionId = "";

        log.info("uri :{}, request.getContentType(): {}", uri, request.getContentType());
        //是否需要過濾
        boolean needFilter = isNeedFilter(uri, includeUrls);
        if (!needFilter) {
            //不需要過濾直接傳給下一個過濾器
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            Cookie[] cookies = request.getCookies();
            if (cookies == null || cookies.length == 0) {
                cookieNoJessionid(response);
            } else {
                for (Cookie cookie : cookies) {
                    if (CommonConstant.JSESSIONID.equals(cookie.getName())) {
                        jsessionId = cookie.getValue();
                        break;
                    }
                }

                log.info("ContentType:" + request.getContentType());


                if (request.getMethod().equals("OPTIONS")) {
                    response.setStatus(HttpStatus.OK.value());
                    return;
                }

                RequestWrapper requestWrapper = null;
                if (request instanceof HttpServletRequest) {
                    if ("POST".equals(request.getMethod().toUpperCase())
                            && request.getContentType() != null
                            && request.getContentType().contains("application/json")) {
                        requestWrapper = new RequestWrapper(request);
                    }
                }

                judgeLogin(jsessionId, response, request, requestWrapper, filterChain);
            }
        }
    }


    /**
     * 判斷是否已登錄
     *
     * @param jsessionId
     * @param response
     * @param request
     * @param requestWrapper
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    private void judgeLogin(String jsessionId, HttpServletResponse response, HttpServletRequest request,
                            RequestWrapper requestWrapper, FilterChain filterChain) throws IOException, ServletException {
        // 判斷 jsessionId 是否爲空
        if (StringUtils.isBlank(jsessionId)) {
            cookieNoJessionid(response);
        } else {
            // 獲取ValueOperations bean
            ServletContext context = request.getServletContext();
            ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
            ValueOperations valueOperations = (ValueOperations) ctx.getBean("valueOperations");

            // 獲取用戶信息
            String userBoStr = (String) valueOperations.get(RedisConstant.REDIS_LOGIN + jsessionId);
            SysUserBo sysUserBo = mapper.readValue(userBoStr, SysUserBo.class);
            if (sysUserBo != null) {
                filterChain.doFilter(request == null ? requestWrapper : request, response);
            } else {
                // 返回登錄超時提醒
                response.setContentType("application/json");
                response.setCharacterEncoding("UTF-8");
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.getWriter().write(this.mapper.writeValueAsString(ResultUtil.errorResult(
                        ExceptionEnum.ERROR_PARAMETERS.getCode(), "jsessionid 登錄超時")));
            }
        }
    }

    /**
     * cookie 無jsessionid
     * @param response
     * @throws IOException
     */
    private void cookieNoJessionid(HttpServletResponse response) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.getWriter().write(this.mapper.writeValueAsString(ResultUtil.errorResult(
                ExceptionEnum.ERROR_PARAMETERS.getCode(),
                "jsessionid 爲空!")));
    }

    /**
     * @param uri
     * @Description: 是否需要過濾
     */
    public boolean isNeedFilter(String uri, String[] includeUrls) {
        for (String includeUrl : includeUrls) {
            if (includeUrl.equals(uri)) {
                return false;
            }
        }

        return true;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }
}

百度一波,給出的解釋是:
出自:https://www.cnblogs.com/nopnog/articles/9133115.html
Access-Control-Allow-Origin 的正確玩法
  前端發起的跨域請求需要適當地響應 Access-Control-Allow-Origin 頭。但是這個頭目前並不支持部分通配符,無法匹配某個域名下的子域。直接使用星號又可能帶來各種問題。更坑爹的是它還不支持設置多個值。那麼如果一個後端程序用於多個域名的訪問該怎麼辦呢?
  很多時候一套後端的程序都對應了多個調用方,它們使用的的域名不同,scheme 可能也不同。所以 Access-Control-Allow-Origin 的值沒法寫死一個域名。如果直接暴力地將其設置爲「」,可能導致一些未被授權的域名也能請求到資源。而且當 Access-Control-Allow-Credentials 的值爲 true 時會導致Access-Control-Allow-Origin 無法被設置爲「」。
  正確的玩法應該是在後端程序中獲取從 HTTP 請求頭傳過來的 Origin 字段,然後在程序中驗證它的值是否合法,並且做出適當的響應。也就是說,可以不依賴前端的跨域限制,後端如果認爲一個請求的 Origin 來自不正確的地方就直接毫不留情地 403 掉。其它情況如果沒有 Origin 或者 Origin 正確則將 Origin 的值原原本本地放入 Access-Control-Allow-Origin 中響應回去。
  其實雖然這個問題可以完全通過後端解決,但我還是很費解規範中無法使用局部通配符,和無法配置多個值的設定。也許這麼設計的目的就是希望後端直接 403,而不是讓瀏覽器來攔截吧(反正讓瀏覽器來攔截還需要額外的傳輸成本,有點浪費)

發佈了38 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章