国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

工作踩坑系列——https訪問遇到“已阻止載入混合活動(dòng)內(nèi)容”

Shisui / 3416人閱讀

摘要:?jiǎn)栴}復(fù)現(xiàn)經(jīng)過一段時(shí)間的調(diào)研工作,終于將公司的環(huán)境改造成支持訪問模式,信心滿滿的打開公司測(cè)試環(huán)境主頁,。當(dāng)一個(gè)網(wǎng)頁出現(xiàn)這種情況時(shí),它被稱為混合內(nèi)容頁面。請(qǐng)求,默認(rèn)行為。

前言

最近在主導(dǎo)公司網(wǎng)站進(jìn)行全站Https改造工作,本文記錄在改造過程中遇到的一個(gè)由于后端302跳轉(zhuǎn)導(dǎo)致前端瀏覽器阻止訪問的問題,感覺這樣的問題有一定通用性,所以編輯成文,希望能給遇到類似問題的人們有所幫助。

問題復(fù)現(xiàn)

經(jīng)過一段時(shí)間的調(diào)研工作,終于將公司的環(huán)境改造成支持https訪問模式,信心滿滿的打開公司測(cè)試環(huán)境主頁,https://test.xxx.com。一切正常,就在我以為改造工作就要完成的時(shí)候,問題就出現(xiàn)了。

進(jìn)入主頁正常,輸入用戶名和密碼登錄,頁面就不動(dòng)了。調(diào)出Firefox的控制臺(tái)查看,發(fā)現(xiàn)這么一行報(bào)錯(cuò)。


(圖一)

打開網(wǎng)絡(luò)面板查看得到如下內(nèi)容

(圖二)

前端發(fā)起了一個(gè)https的Ajax請(qǐng)求,后端返回狀態(tài)碼為302,location為http://開頭網(wǎng)址,這樣就造成了混合訪問。本應(yīng)該有Ajax自動(dòng)處理的302跳轉(zhuǎn)就這樣被瀏覽器禁止了。

問題分析 1. 什么是混合內(nèi)容

當(dāng)用戶訪問使用HTTPS的頁面時(shí),他們與web服務(wù)器之間的連接是使用SSL加密的,從而保護(hù)連接不受嗅探器和中間人攻擊。

如果HTTPS頁面包括由普通明文HTTP連接加密的內(nèi)容,那么連接只是被部分加密:非加密的內(nèi)容可以被嗅探者入侵,并且可以被中間人攻擊者修改,因此連接不再受到保護(hù)。當(dāng)一個(gè)網(wǎng)頁出現(xiàn)這種情況時(shí),它被稱為混合內(nèi)容頁面。

詳情可見https://developer.mozilla.org...

2. 為什么經(jīng)過后端跳轉(zhuǎn)后Location由https變?yōu)榱薶ttp。

我們后端采用Java開發(fā),部署與Tomcat,對(duì)于Servlet來說一般采用HttpServletResponse.sendRedirect(String url) 方法實(shí)現(xiàn)頁面跳轉(zhuǎn)(302跳轉(zhuǎn))。那么問題是不是出在這個(gè)方法呢?答案是否定的。
sendRedirect(String url)方法中url參數(shù)可以傳入絕對(duì)地址和相對(duì)地址。我們使用的時(shí)候一般傳入相對(duì)地址,這樣由方法內(nèi)部自動(dòng)轉(zhuǎn)換為絕對(duì)地址也就是返回給瀏覽器中Location參數(shù)中的地址,sendRedirect()方法內(nèi)部會(huì)根據(jù)當(dāng)前訪問的scheme來決定拼接后絕對(duì)地址的scheme,也就是說如果訪問地址是https開頭那么跳轉(zhuǎn)鏈接的絕對(duì)地址也會(huì)是https的,http同理。在本次實(shí)例中我們傳入的就是相對(duì)地址,跳轉(zhuǎn)鏈接的絕對(duì)路徑地址開頭是由請(qǐng)求地址決定的,也就是后端程序收到的HttpServletRequest請(qǐng)求協(xié)議一定是http開頭的。

我們看到(圖二)中地址請(qǐng)求地址是由https開頭的,為什么到了后端程序后就成為了http請(qǐng)求呢?我們接著往下說。

(圖三)

為了方便說明我畫了一張https配置的架構(gòu)圖,我們使用Nginx作為反向代理服務(wù)器,上游服務(wù)器使用Tomcat,我們?cè)贜ginx層進(jìn)行Https配置,由Nginx負(fù)責(zé)處理Https請(qǐng)求。但是Nginx自身處理方式規(guī)定向上游服務(wù)器發(fā)送請(qǐng)求的時(shí)候是以http的方式請(qǐng)求的。這也就說明了為什么我們后端代碼收到的請(qǐng)求是http協(xié)議,真想終于大白了。

解決方法

問題終于明了了,接下來就是解決的時(shí)候。

1.解決方案1.0

既然經(jīng)過Nginx代理后Tomcat服務(wù)器運(yùn)行的代碼都變成了http請(qǐng)求,然后sendRedirect方法傳入相對(duì)地址就會(huì)隨著請(qǐng)求地址也變成http。那么我們不再使用相對(duì)地址而使用絕對(duì)地址。這樣跳轉(zhuǎn)地址就全部由我們做主,想跳轉(zhuǎn)到哪里就跳轉(zhuǎn)的哪里,媽媽再也不用擔(dān)心我們跳轉(zhuǎn)了。

先期改造:

    /**
     * 重新實(shí)現(xiàn)sendRedirect。
     * @param request
     * @param response
     * @param url
     * @throws IOException
     */
    public static void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException{
        if(url.startsWith("http://")||url.startsWith("https://")){
            //絕對(duì)路徑,直接跳轉(zhuǎn)。
            response.sendRedirect(url);
            return;
        }
        // 收集請(qǐng)求信息,為拼接絕對(duì)地址做準(zhǔn)備。
        String serverName = request.getServerName();
        int port = request.getServerPort();
        String contextPath = request.getContextPath();
        String servletPath = request.getServletPath();
        String queryString = request.getQueryString();

        // 拼接絕對(duì)地址
        StringBuilder absoluteUrl = new StringBuilder();
        // 強(qiáng)制使用https
        absoluteUrl.append("https").append("://").append(serverName);
        //80和443位http和https默認(rèn)接口,無需拼接。
        if (port != 80 && port != 443) {
            absoluteUrl.append(":").append(port);
        }
        if (contextPath != null) {
            absoluteUrl.append(contextPath);
        }
        if (servletPath != null) {
            absoluteUrl.append(servletPath);
        }
        // 將相對(duì)地址加入。
        absoluteUrl.append(url);
        if (queryString != null) {
            absoluteUrl.append(queryString);
        }
        // 跳轉(zhuǎn)到絕對(duì)地址。
        response.sendRedirect(absoluteUrl.toString());
    }

我們自己了一個(gè)sendRedirect()方法,但是還有一點(diǎn)小小的瑕疵,我們將所有相對(duì)地址都轉(zhuǎn)化成http開頭的絕對(duì)地址,對(duì)于那些我們即支持https由支持http的網(wǎng)站來說,這樣就不適合了,所以我們需要和前端請(qǐng)求做一個(gè)預(yù)定,讓前端再發(fā)類似于Ajax訪問的時(shí)候,自定義一個(gè)request的header,告訴我們是https訪問還是http訪問,我們?cè)诤蠖舜a中判斷這個(gè)自定義header,決定代碼行為。

/**
     * 重新實(shí)現(xiàn)sendRedirect。
     * @param request
     * @param response
     * @param url
     * @throws IOException
     */
    public static void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException{
        
        
        if(url.startsWith("http://")||url.startsWith("https://")){
            //絕對(duì)路徑,直接跳轉(zhuǎn)。
            response.sendRedirect(url);
            return;
        }
        
        //假設(shè)前端請(qǐng)求頭為http_https_scheme,可以傳入的值有http或https,不傳默認(rèn)為https。
        if(("http").equals(request.getHeader("http_https_scheme"))){
            //http請(qǐng)求,默認(rèn)行為。
            response.sendRedirect(url);
            return;
        }
        
        // 收集請(qǐng)求信息,為拼接絕對(duì)地址做準(zhǔn)備。
        String serverName = request.getServerName();
        int port = request.getServerPort();
        String contextPath = request.getContextPath();
        String servletPath = request.getServletPath();
        String queryString = request.getQueryString();

        // 拼接絕對(duì)地址
        StringBuilder absoluteUrl = new StringBuilder();
        // 強(qiáng)制使用https
        absoluteUrl.append("https").append("://").append(serverName);
        //80和443位http和https默認(rèn)接口,無需拼接。
        if (port != 80 && port != 443) {
            absoluteUrl.append(":").append(port);
        }
        if (contextPath != null) {
            absoluteUrl.append(contextPath);
        }
        if (servletPath != null) {
            absoluteUrl.append(servletPath);
        }
        // 將相對(duì)地址加入。
        absoluteUrl.append(url);
        if (queryString != null) {
            absoluteUrl.append(queryString);
        }
        // 跳轉(zhuǎn)到絕對(duì)地址。
        response.sendRedirect(absoluteUrl.toString());
    }

以上為改造之后的代碼,增加了請(qǐng)求頭判斷邏輯。這樣我們的方法就支持http和https混合模式了。


更進(jìn)一步:
讓我們對(duì)上面的代碼更進(jìn)一步,其實(shí)我們就是對(duì)sendRedirect的邏輯重新編排,只不過我們使用的靜態(tài)方法的模式,可不可以直接重寫response中的sendRedirect()方法?

/**
 * 重寫sendRedirect方法。
 *
 */
public class HttpsServletResponseWrapper extends HttpServletResponseWrapper {
    private final HttpServletRequest request;

    public HttpsServletResponseWrapper(HttpServletRequest request,HttpServletResponse response) {
        super(response);
        this.request=request;
    }

    @Override
    public void sendRedirect(String location) throws IOException {
        if(location.startsWith("http://")||location.startsWith("https://")){
            //絕對(duì)路徑,直接跳轉(zhuǎn)。
            super.sendRedirect(location);
            return;
        }
        
        //假設(shè)前端請(qǐng)求頭為http_https_scheme,可以傳入的值有http或https,不傳默認(rèn)為https。
        if(("http").equals(request.getHeader("http_https_scheme"))){
            //http請(qǐng)求,默認(rèn)行為。
            super.sendRedirect(location);
            return;
        }
        
        // 收集請(qǐng)求信息,為拼接絕對(duì)地址做準(zhǔn)備。
        String serverName = request.getServerName();
        int port = request.getServerPort();
        String contextPath = request.getContextPath();
        String servletPath = request.getServletPath();
        String queryString = request.getQueryString();

        // 拼接絕對(duì)地址
        StringBuilder absoluteUrl = new StringBuilder();
        // 強(qiáng)制使用https
        absoluteUrl.append("https").append("://").append(serverName);
        //80和443位http和https默認(rèn)接口,無需拼接。
        if (port != 80 && port != 443) {
            absoluteUrl.append(":").append(port);
        }
        if (contextPath != null) {
            absoluteUrl.append(contextPath);
        }
        if (servletPath != null) {
            absoluteUrl.append(servletPath);
        }
        // 將相對(duì)地址加入。
        absoluteUrl.append(location);
        if (queryString != null) {
            absoluteUrl.append(queryString);
        }
        // 跳轉(zhuǎn)到絕對(duì)地址。
        super.sendRedirect(absoluteUrl.toString());
    }
}

具體邏輯一樣,我們只是繼承了HttpServletResponseWrapper 這個(gè)包裝類,在這里使用了一個(gè)觀察者模式重新編寫了sendRedirect()方法邏輯。
我們可以這樣使用我們自定義等HttpsServletResponseWrapper

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String location="/login";
        new HttpsServletResponseWrapper(request, response).sendRedirect(location);

    }

再進(jìn)一步:

既然我們有了新的HttpServletResponseWrapper ,我們?cè)谛枰牡胤绞謩?dòng)包裝HttpServletResponse 就顯得有點(diǎn)多余了。我們可以利用servletfilter機(jī)制來自動(dòng)包裝。

public class HttpsServletResponseWrapperFilter implements Filter{

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

            chain.doFilter(request, new HttpsServletResponseWrapper((HttpServletRequest)request, (HttpServletResponse)response));

    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
        
    }

}

在web.xml中設(shè)置filter映射,可以直接使用HttpServletResponse 對(duì)象,無需包裝,因?yàn)樵谡?qǐng)求經(jīng)過HttpsServletResponseWrapperFilter 的時(shí)候response已經(jīng)被包裝為HttpsServletResponseWrapper

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String location="/login";
        response.sendRedirect(location);

    }

至此,我們已經(jīng)代碼邏輯無縫的嵌入到我們的后端代碼中,看上去更優(yōu)雅了。

2.解決方案2.0

在1.0版本中我們的關(guān)注點(diǎn)都是Nginx上游服務(wù)中運(yùn)行的后端代碼,我們通過對(duì)代碼的改造達(dá)到我們的目的。現(xiàn)在我們轉(zhuǎn)換一下思路,將關(guān)注點(diǎn)放在Nginx上,既然是Nginx代理之后,我們的scheme丟失,那么Nginx有沒有給我們提供一種機(jī)制保留代理之后的scheme呢,答案是肯定的。

location / {
    proxy_set_header X-Forwarded-Proto $scheme;
}

一行簡(jiǎn)單的配置,就解決了我們的問題,Nginx在代理的時(shí)候保留了scheme,這樣我們?cè)谔D(zhuǎn)的時(shí)候可以直接使用HttpServletResponse.sendRedirect()方法。

小結(jié)

通過解決方案1.0的修改代碼方式和2.0的修改配置方式,我們都解決了問題。在日常開發(fā)中解決問題的方式很多,只要你了解產(chǎn)生問題的原理,在產(chǎn)生問題的任意環(huán)節(jié)都可以尋求解決方案。這篇工作記錄就寫到這里,當(dāng)然這個(gè)問題還有其他的解決方式,如果你有其他的解決方案可以留言告訴我。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/40028.html

相關(guān)文章

  • 踩坑】近來在Firefox上遇到的一些坑

    摘要:最近在幫人解決下的一些兼容問題。驗(yàn)證不通過的話,輸入框會(huì)加上紅色的邊框。然后妹紙?jiān)诿枋鲋姓f的是瀏覽器中,修改密碼頁面,輸入框中不輸入任何字符,輸入框顏色也是紅的我還以為又是哪里的寫得不對(duì)呢。最后發(fā)現(xiàn),輸入框好像都帶了個(gè)屬性。 因?yàn)楣ぷ饕荒甓嘁詠恚龅墓ぷ骰径际呛蛍ebkit系列打交道。 先是做m站,后來做了兩個(gè)app內(nèi)嵌的hybrid項(xiàng)目,從來只考慮webkit前綴和相關(guān)的偽類。 最...

    JaysonWang 評(píng)論0 收藏0
  • 踩坑】近來在Firefox上遇到的一些坑

    摘要:最近在幫人解決下的一些兼容問題。驗(yàn)證不通過的話,輸入框會(huì)加上紅色的邊框。然后妹紙?jiān)诿枋鲋姓f的是瀏覽器中,修改密碼頁面,輸入框中不輸入任何字符,輸入框顏色也是紅的我還以為又是哪里的寫得不對(duì)呢。最后發(fā)現(xiàn),輸入框好像都帶了個(gè)屬性。 因?yàn)楣ぷ饕荒甓嘁詠恚龅墓ぷ骰径际呛蛍ebkit系列打交道。 先是做m站,后來做了兩個(gè)app內(nèi)嵌的hybrid項(xiàng)目,從來只考慮webkit前綴和相關(guān)的偽類。 最...

    AJie 評(píng)論0 收藏0
  • Weex系列(7) ——踩坑填坑的總總

    摘要:的方法在安卓底下會(huì)崩潰,結(jié)果竟然是要在里面設(shè)置正確的,因?yàn)槲沂怯霉倬W(wǎng)的腳手架搭起來的項(xiàng)目,不知道大家會(huì)不會(huì)遇到,改一下就可以解決問題了。 目錄 Weex系列(序) —— 總要知道原生的一點(diǎn)東東(iOS) Weex系列(序) —— 總要知道原生的一點(diǎn)東東(Android) Weex系列(1) —— Hello World項(xiàng)目 Weex系列(2) —— 頁面跳轉(zhuǎn)和通信 Weex系列(3)...

    toddmark 評(píng)論0 收藏0
  • Weex系列(7) ——踩坑填坑的總總

    摘要:的方法在安卓底下會(huì)崩潰,結(jié)果竟然是要在里面設(shè)置正確的,因?yàn)槲沂怯霉倬W(wǎng)的腳手架搭起來的項(xiàng)目,不知道大家會(huì)不會(huì)遇到,改一下就可以解決問題了。 目錄 Weex系列(序) —— 總要知道原生的一點(diǎn)東東(iOS) Weex系列(序) —— 總要知道原生的一點(diǎn)東東(Android) Weex系列(1) —— Hello World項(xiàng)目 Weex系列(2) —— 頁面跳轉(zhuǎn)和通信 Weex系列(3)...

    phpmatt 評(píng)論0 收藏0
  • nextjs踩坑

    摘要:踩坑幾乎一整年沒咋寫文章,主要是懶,加上工作也挺忙。遇到一些坑,也有一些收獲這里記錄一下。個(gè)人習(xí)慣使用啟動(dòng)服務(wù)。總結(jié)說了上面那么多,其實(shí)官方文檔里都有相關(guān)例子,就當(dāng)我的個(gè)人踩坑記錄吧。 Next.js踩坑 幾乎一整年沒咋寫文章,主要是懶,加上工作也挺忙。但是想趁著年底發(fā)一篇,希望明年更勤奮一點(diǎn)。其實(shí)不是沒東西寫,就是想深入一個(gè)東西還是很困難的,要查各種資料,最終還是懶就是了。 next...

    JayChen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<