文档章节

神奇的403

Mr_Qi
 Mr_Qi
发布于 06/13 13:08
字数 1319
阅读 543
收藏 4
点赞 1
评论 0

背景

应曾老师要求 来写一篇神奇的403……

分享会结束后 某曾老师高兴地说道:“试用出bug了……测试【^^!】说你修改了profile“

先来看一下现象!

请求返回403~!

问题

/kzf6/maintain/getNoFinishMaintainList.do?idCar=4060591933968503770&maintainPkId= 403

 

首先一个403过来 开发反馈在测试环境上没有问题 但是在试用环境GG!

随后运维反馈nginx没有做过变更。

那么问题出现在哪呢?

分析

首先确认并非nginx问题 去tomcat查看对应access日志

[root@iZuf65br1kzrdlcne4bxpzZ logs]# grep 403 localhost_access_log.2018-05-09.txt |wc -l
1067

似乎全部集中在发布之后 看来还是新的代码导致!

查看该请求

/**
 * 未完工工单了列表
 *
 * @param  maintainSo 车辆主键
 * @return 未完成工单的列表
 * @throws Exception
 */
@RequestMapping(value = "/getNoFinishMaintainList", method = {RequestMethod.POST, RequestMethod.GET})
@RequiresPermissions(Permission.MAINTAIN_CAR_MAINTAIN_VIEW_LIST)
@ResponseBody
public List<TsMaintainVO> getNoFinishMaintainList(MaintainSo maintainSo) throws Exception {
}

这个请求同时支持GET和POST 似乎看起来也没有问题!

那问题出在哪呢???

首先看到大部分出问题的都是POST请求

由于前几次关于POST请求的跨域问题考虑是否跨域导致出现了一些问题!

尝试了一下url

$.post("https://yunxiu-trial.f6car.com/kzf6/maintain/getNoFinishMaintais")

依然返回了403 这个比较奇怪了!按照道理这个请求应该返回404 不应该返回403啊 那么可以断定请求没有打到Servlet!

因此猜测这个应该是到了某个filter出现了问题~ 调查后发现小伙伴加了tomcat的corsFilter

<filter>
    <filter-name>CorsFilter</filter-name>
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
    <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>*</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.methods</param-name>
        <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
    </init-param>
    <init-param>
        <param-name>cors.support.credentials</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

把这段话注释掉了 果然一切正常!

解读

@Override
public void doFilter(final ServletRequest servletRequest,
        final ServletResponse servletResponse, final FilterChain filterChain)
        throws IOException, ServletException {
    if (!(servletRequest instanceof HttpServletRequest) ||
            !(servletResponse instanceof HttpServletResponse)) {
        throw new ServletException(sm.getString("corsFilter.onlyHttp"));
    }
 
    // Safe to downcast at this point.
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
 
    // Determines the CORS request type.
    CorsFilter.CORSRequestType requestType = checkRequestType(request);
 
    // Adds CORS specific attributes to request.
    if (decorateRequest) {
        CorsFilter.decorateCORSProperties(request, requestType);
    }
    switch (requestType) {
    case SIMPLE:
        // Handles a Simple CORS request.
        this.handleSimpleCORS(request, response, filterChain);
        break;
    case ACTUAL:
        // Handles an Actual CORS request.
        this.handleSimpleCORS(request, response, filterChain);
        break;
    case PRE_FLIGHT:
        // Handles a Pre-flight CORS request.
        this.handlePreflightCORS(request, response, filterChain);
        break;
    case NOT_CORS:
        // Handles a Normal request that is not a cross-origin request.
        this.handleNonCORS(request, response, filterChain);
        break;
    default:
        // Handles a CORS request that violates specification.
        this.handleInvalidCORS(request, response, filterChain);
        break;
    }
}

我们由于得到了403 着重看一下什么情况返回403

/**
 * Handles a CORS request that violates specification.
 *
 * @param request
 *            The {@link HttpServletRequest} object.
 * @param response
 *            The {@link HttpServletResponse} object.
 * @param filterChain
 *            The {@link FilterChain} object.
 */
private void handleInvalidCORS(final HttpServletRequest request,
        final HttpServletResponse response, final FilterChain filterChain) {
    String origin = request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);
    String method = request.getMethod();
    String accessControlRequestHeaders = request.getHeader(
            REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
 
    response.setContentType("text/plain");
    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
    response.resetBuffer();
 
    if (log.isDebugEnabled()) {
        // Debug so no need for i18n
        StringBuilder message =
                new StringBuilder("Invalid CORS request; Origin=");
        message.append(origin);
        message.append(";Method=");
        message.append(method);
        if (accessControlRequestHeaders != null) {
            message.append(";Access-Control-Request-Headers=");
            message.append(accessControlRequestHeaders);
        }
        log.debug(message.toString());
    }
}

果然如此 在cors不合法的场景下直接返回了403 SC_FORBIDDEN

interesting~为何全部走到了InvalidCORS呢?

很明显关键在于该方法

/**
 * Determines the request type.
 *
 * @param request
 */
protected CORSRequestType checkRequestType(final HttpServletRequest request) {
    CORSRequestType requestType = CORSRequestType.INVALID_CORS;
    if (request == null) {
        throw new IllegalArgumentException(
                sm.getString("corsFilter.nullRequest"));
    }
    String originHeader = request.getHeader(REQUEST_HEADER_ORIGIN);
    // Section 6.1.1 and Section 6.2.1
    if (originHeader != null) {
        if (originHeader.isEmpty()) {
            requestType = CORSRequestType.INVALID_CORS;
        } else if (!isValidOrigin(originHeader)) {
            requestType = CORSRequestType.INVALID_CORS;
        } else if (isLocalOrigin(request, originHeader)) {
            return CORSRequestType.NOT_CORS;
        } else {
            String method = request.getMethod();
            if (method != null) {
                if ("OPTIONS".equals(method)) {
                    String accessControlRequestMethodHeader =
                            request.getHeader(
                                    REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
                    if (accessControlRequestMethodHeader != null &&
                            !accessControlRequestMethodHeader.isEmpty()) {
                        requestType = CORSRequestType.PRE_FLIGHT;
                    } else if (accessControlRequestMethodHeader != null &&
                            accessControlRequestMethodHeader.isEmpty()) {
                        requestType = CORSRequestType.INVALID_CORS;
                    } else {
                        requestType = CORSRequestType.ACTUAL;
                    }
                } else if ("GET".equals(method) || "HEAD".equals(method)) {
                    requestType = CORSRequestType.SIMPLE;
                } else if ("POST".equals(method)) {
                    String mediaType = getMediaType(request.getContentType());
                    if (mediaType != null) {
                        if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES
                                .contains(mediaType)) {
                            requestType = CORSRequestType.SIMPLE;
                        } else {
                            requestType = CORSRequestType.ACTUAL;
                        }
                    }
                } else {
                    requestType = CORSRequestType.ACTUAL;
                }
            }
        }
    } else {
        requestType = CORSRequestType.NOT_CORS;
    }
 
    return requestType;
}

一开始就赋值为CORSRequestType requestType = CORSRequestType.INVALID_CORS; 因此很容易得出结论 当未重新赋值的时候cors就是失败的~

所以可以从这段代码中得到答案

String mediaType = getMediaType(request.getContentType());
                   if (mediaType != null) {
                       if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES
                               .contains(mediaType)) {
                           requestType = CORSRequestType.SIMPLE;
                       } else {
                           requestType = CORSRequestType.ACTUAL;
                       }
                   }

当mediaType为空时【前提是POST】此时结果就是CORS失败导致403 而mediaType是从ContentType中获取的 因此ContentType必须不能为空才可以。 否则post请求过来就是cors失败返回403~

至于为啥POST请求某些场景没有携带ContentType  从前端分析吧

 

彩蛋

你以为结束了么!事实上在测试环境和试用环境两个不同的结果也是一个问题!为何在测试环境一切OK了 上了生产该问题集中爆发了呢?

事实上测试环境也有问题【只不过在某种场景下测试环境没有问题】

private boolean isLocalOrigin(HttpServletRequest request, String origin) {
 
    // Build scheme://host:port from request
    StringBuilder target = new StringBuilder();
    String scheme = request.getScheme();
    if (scheme == null) {
        return false;
    } else {
        scheme = scheme.toLowerCase(Locale.ENGLISH);
    }
    target.append(scheme);
    target.append("://");
 
    String host = request.getServerName();
    if (host == null) {
        return false;
    }
    target.append(host);
 
    int port = request.getServerPort();
    if ("http".equals(scheme) && port != 80 ||
            "https".equals(scheme) && port != 443) {
        target.append(':');
        target.append(port);
    }
 
    return origin.equalsIgnoreCase(target.toString());
}

我们从这段代码中可以看到 tomcat的CorsFilter通过该请求来判断是否是LocalOrigin

在测试环境下由于没有nginx 因此直接通过http请求访问 此时获取到的scheme为http 因此实质上异常的post请求【即没有携带ContentType】也能得到未跨域的结果 因此正常返回

但是对于试用环境 由于 https 也一直强调的getSchema仍然会返回http【为什么?自己想!!】那么port返回啥? 参考 https使用nginx设置port

因此此时很明显一个http 而另一个是https 明显匹配不上【此时后端认为需要跨域了……】

结果很明了~

 

© 著作权归作者所有

共有 人打赏支持
Mr_Qi
粉丝 252
博文 298
码字总数 312931
作品 0
南京
程序员
ruby gem 0.9.4的问题

在配置gem的过程中,有极小机率会遇到gem安装后失效的问题,比方说会403,如下所示: C:Usersgeraldlau>gem update --system Updating RubyGems... ERROR: While executing gem ... (Gem::R...

神勇小白鼠 ⋅ 2011/03/08 ⋅ 0

IIS错误代码大全

400 无法解析此请求。 401.1 未经授权:访问由于凭据无效被拒绝。 401.2 未经授权: 访问由于服务器配置倾向使用替代身份验证方法而被拒绝。 401.3 未经授权:访问由于 ACL 对所请求资源的设置...

刘赤龙 ⋅ 2010/06/08 ⋅ 0

HTTP错误列表

HTTP 400 - 请求无效 HTTP 401.1 - 未授权:登录失败 HTTP 401.2 - 未授权:服务器配置问题导致登录失败 HTTP 401.3 - ACL 禁止访问资源 HTTP 401.4 - 未授权:授权被筛选器拒绝 HTTP 401.5...

crazyinsomnia ⋅ 2010/02/02 ⋅ 0

Status Code详解

1xx:信息响应类,表示接收到请求并且继续处理 2xx:处理成功响应类,表示动作被成功接收、理解和接受 3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理 4xx:客户端错误,客户请求包...

pricker ⋅ 2015/08/17 ⋅ 0

网络 报 40? 对应的解释

今天在ubuntu上面下载android4.0.3的源码(allwinner-A10),NND 不停的给我提示401,403 用户名我可是填写了啊!郁闷中! 400 无法解析此请求。 401.1 未经授权:访问由于凭据无效被拒绝。 40...

雨焰 ⋅ 2012/10/30 ⋅ 0

HTTP STATE CODE

------------------------------------------------------------------------------ 1xx:指示信息--表示请求已接收,继续处理 2xx:成功--表示请求已被成功接收 3xx:重定向--要完成请求必须...

origin ⋅ 2014/06/10 ⋅ 0

HTTP 错误代码表

代码 指示 2xx 成功 200 正常;请求已完成。 201 正常;紧接 POST 命令。 202 正常;已接受用于处理,但处理尚未完成。 203 正常;部分信息 — 返回的信息只是一部分。 204 正常;无响应 — ...

SeanCai ⋅ 2011/02/23 ⋅ 0

HTTP常见错误代码列表汇总及解决方案

HTTP常见错误代码列表汇总及解决方案 常见的HTTP错误可以分为以下四大类。每一大类又细分为很多类小错误。分别是: 1、401类错误 最常见的出错提示:401 UNAUTHORIZED 这表示你必须有一个正确...

长平狐 ⋅ 2012/10/08 ⋅ 0

服务器错误数字(代码)对照表

400 错误请求 — 请求中有语法问题,或不能满足请求。 404 找不到 — 服务器找不到给定的资源;文件不存在 500 内部错误 — 因为意外情况,服务器不能完成请求 或者出问题了 2xx 成功 200 正...

陈映亮 ⋅ 2016/07/21 ⋅ 0

常见的网页报错

400 无法解析此请求。 401.1 未经授权:访问由于凭据无效被拒绝。 401.2 未经授权: 访问由于服务器配置倾向使用替代身份验证方法而被拒绝。 401.3 未经授权:访问由于 ACL 对所请求资源的设置...

21种犹豫 ⋅ 2014/12/02 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Linux中的端口大全

1 被LANA定义的端口 端口 名称 描述 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat 用于列举连接了的端口的系统状态 13 d...

寰宇01 ⋅ 17分钟前 ⋅ 0

Confluence 6 如何备份存储文件和页面信息

备份的 ZIP 文件包含有 entities.xml,这个 XML 文件包含有 Confluence 的所有页面内容和存储附件的目录。 备份 Zip 文件结构 页面的附件是存储在附件存储目录中的,通过页面和附件 ID 进行识...

honeymose ⋅ 19分钟前 ⋅ 0

【每天一个JQuery特效】根据状态确定是否滑入或滑出被选元素

主要效果: 本文主要采用slideToggle()方法实现以一行代码同时实现以展开或收缩的方式显示或隐藏被选元素。 主要代码如下: <!DOCTYPE html><html><head><meta charset="UTF-8">...

Rhymo-Wu ⋅ 23分钟前 ⋅ 0

度量.net framework 迁移到.net core的工作量

把现有的.net framework程序迁移到.net core上,是一个非常复杂的工作,特别是一些API在两个平台上还不能同时支持。两个类库的差异性,通过人工很难识别全。好在微软的工程师们考虑到了我们顾...

李朝强 ⋅ 28分钟前 ⋅ 0

请不要在“微服务”的狂热中迷失自我!

微服务在过去几年一直是一个非常热门的话题(附录1)。何为“微服务的疯狂”,举个例子: 众所周知,Netflix在DevOps上的表现非常棒。Netfix可以做微服务。因此:如果我做微服务,我也将非常...

harries ⋅ 30分钟前 ⋅ 0

oAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享

背景 6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。 重要变化: 基于Spring Boot 2.0.X 不兼容 Spring Boot 1.5.X 期间踩过几个坑,分享出来给大伙,主要是关于...

冷冷gg ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 理发师小姐姐的魔法

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @冰冰棒- :分享田馥甄的单曲《My Love》 《My Love》- 田馥甄 手机党少年们想听歌,请使劲儿戳(这里) @Li-Wang :哎,头发又长了。。。又要...

小小编辑 ⋅ 今天 ⋅ 8

Kafka1.0.X_消费者API详解2

偏移量由消费者管理 kafka Consumer Api还提供了自己存储offset的功能,将offset和data做到原子性,可以让消费具有Exactly Once 的语义,比kafka默认的At-least Once更强大 消费者从指定分区...

特拉仔 ⋅ 今天 ⋅ 0

NEO智能合约之发布和升级(二)

接NEO智能合约之发布和升级(一),我们接下来说说智能合约的升级功能。 一 准备工作 合约的升级需要在合约内预先设置好升级接口,以方便在升级时调用。接下来我们对NEO智能合约之发布和升级...

红烧飞鱼 ⋅ 今天 ⋅ 0

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部