- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
最近在写 OAuth2 对接的代码,由于授权服务器(竹云BambooCloud IAM)部署在甲方内网,所以想着自己 Mock 一下授权方的返回体,验证一下我的代码。我这才踩到了坑……
选择的 Mock 框架是 国产开源的 Moco(https://github.com/dreamhead/moco),先下载moco-runner-1.3.0-standalone.jar
再根据 Moco的官方文档(https://github.com/dreamhead/moco/blob/master/moco-doc/apis.md)和竹云对接文档配置了以下的mock配置:
BambooCloud-IAM-OAuth2-Moco.json
[
{
"description": "授权回调接口",
"request": {
"uri": "/idp/oauth2/authorize",
"method": "get",
"queries": {
"client_id": "client-id-test",
"redirect_uri": "http://localhost:8188/api/oauth2/callback",
"response_type": "code"
}
},
"redirectTo" : "http://localhost:8188/api/oauth2/callback?code=123456"
},
{
"description": "获取token接口",
"request": {
"uri": "/idp/oauth2/getToken",
"method": "post",
"headers": {
"content-type": "application/x-www-form-urlencoded"
},
"forms": {
"client_id" : "client-id-test",
"client_secret" : "client-secret-test",
"grant_type" : "authorization_code",
"code" : "123456"
}
},
"response": {
"json": {
"access_token" : "123456789"
}
}
},
{
"description": "获取用户信息接口",
"request": {
"uri": "/idp/oauth2/getUserInfo",
"method": "get",
"queries": {
"client_id": "client-id-test",
"access_token": "123456789"
}
},
"response": {
"json": {
"spRoleList":["zhangsan"],
"uid":"20190904124905344-F4BE-C2C9EFF24",
"sorgId":null,
"displayName":"张三",
"loginName":"zhangsan",
"secAccValid":1,
"givenName":"729026",
"pinyinShortName":null,
"spNameList":["portalID","tyxtest","data","certificate","sysCertification","customer"],
"employeeNumber":null
}
}
}
]
启动 Moco
java -Dfile.encoding=UTF-8 -jar moco-runner-1.3.0-standalone.jar http -p 12306 -c BambooCloud-IAM-OAuth2-Moco.json
-Dfile.encoding=UTF-8
- 指定 Moco 启动服务的字符集,作者默认在程序里写了 GBK,不指定返回中文可能乱码然后配置 SpringBoot 服务配置文件,这里就是给大家看看,不一定具备通用性:
# OAuth2配置 #
################################################
#是否开启oauth2单点登录,true/false,默认不启用。
oauth2.sso.enabled=true
#授权方颁发的clientId
oauth2.sso.clientId=client-id-test
#授权方颁发的clientSecret
oauth2.sso.clientSecret=client-secret-test
#登录地址,授权方提供,示例:https://{host}:{port}/idp/oauth2/authorize
oauth2.sso.authUrl=http://localhost:12306/idp/oauth2/authorize
#获取token地址,授权方提供,示例:https://{host}:{port}/idp/oauth2/getToken
oauth2.sso.tokenUrl=http://localhost:12306/idp/oauth2/getToken
#获取用户信息地址,授权方提供,示例:https://{host}:{port}/idp/oauth2/getUserInfo
oauth2.sso.userInfoUrl=http://localhost:12306/idp/oauth2/getUserInfo
#授权回调地址,由【Nginx代理当前服务的地址 或 当前服务地址+"/api/oauth2/callback"】 组成,示例:https://{host}:{port}/api/oauth2/callback
oauth2.sso.redirectUri=http://localhost:8188/api/oauth2/callback
启动项目(8188端口,有两个api地址: /api/oauth2/sso
与 /api/oauth2/callback
),先通过程序 http://localhost:8188/api/oauth2/sso
第一、二个请求会重定向请求到 Moco 服务上的 授权回调接口
并带上一些参数
第三个请求由 Moco 服务的 授权回调接口
回调项目的回调地址(授权码回调,项目会先调用 Moco 的 获取token接口
),然后因为全局错误拦截返回了左侧的 JSON,提示 "400 Bad Request: [no body]"
我还以为是 Moco 服务的问题,就用 Postman 调了下 获取token接口
,结果是通的!!
然后再看下 Moco 的控制台输出:
经过找不同,发现项目调用接口时请求头 Content-Type
加了 ;chartset=UTF-8
!!!
由于 Moco 配置文件里指定请求头必须一致,才能正确返回,否则就是 400
状态码,而且没有返回体。
先看看出问题的代码部分:
怀疑 MediaType.APPLICATION_FORM_URLENCODED
值有UTF-8字符集,点进去
看注释时写的是不带字符集的,debug 看下 setContentType
后的 headers
内容
继续debug
看来还没改变,继续debug,进入 RestTemplate
的 exchange()
方法
resolveUrl()
这个方法我看了下,没涉及改请求头,问题应出在 doExecute()
方法中,断点打在 request
对象后,看看 request
对象中的 headers
,发现是空的,而下边有一个方法是 requestCallback.doWithRequest(request);
看起来是对 request
对象进行了处理,直接跟进去
RequestCallback
有两个最终的实现,Xhr
对应 XMLHttpRequest
,这里发的是HTTP请求,所以是 HttpEntityRequestCallback
的实现
在怀疑的位置打两个断点,跟进循环看看,首先进了下边的判断,messageConverter
对象是 AllEncompassingFormHttpMessageConverter
的实例,而且在write()
方法执行前,headers
仍是正常的
查看 write()
方法实现,经典 3 选 1,由于我们的 Content-Type
是 application/x-www-form-urlencoded
,理应进入 FormHttpMessageConverter
实现中
writeForm()
执行前 ContentType
仍未改变,进入 writeForm()
方法看看
终于,在 org.springframework.http.converter.FormHttpMessageConverter.getFormContentType(MediaType)
的注释和代码中发现了问题。
对于表单内容类型的请求的 ContentType
设置分三种情况:
ContentType
就默认添加 application/x-www-form-urlencoded;charset=UTF-8
ContentType
但没字符集,也会自动加字符集ContentType
存在且有字符集设置,则直接返回跳出 getFormContentType()
,可以看到 contenType
对象已经有字符集了。
联想到作者信誓旦旦地注释 // should never occur
这就很说明问题了 🤪
RestTemplate
自动使用 org.springframework.http.converter.FormHttpMessageConverter
添加字符集可能是出于对请求乱码的担忧。而我们这里的 Moco 配置添加了请求头判断,值全转成大写或小宝一定要一致,所以改一下 Moco 问题接口配置的content-type的值,加上字符集验证效果。
有1说1 ,保存完后,看下 Moco 控制台它已经自动刷新配置文件了,Moco 真好用。
重新调用接口验证通过。
当我们浏览网页时,我们想对网页内容如文章、评论中的观点持赞同或反对意见时,可以通过点击网页中的“顶”和“踩”来进行投票。而整个交互过程,开发者可以通过ajax异步来实现,从而提高用户体验
我以为我了解了python中列表切片的基础知识,但是在切片上使用负步骤时收到了意外错误,如下所示: >>> a = list(range(10)) >>> a [0, 1, 2, 3, 4, 5, 6
本文实例为大家分享了基于PHP+jQuery+MySql实现红蓝(顶踩)投票代码,供大家参考,具体内容如下 数据库操作: ?
我是一名优秀的程序员,十分优秀!