公司增加了个微信扫码登录,费劲千辛万苦终于把应用申请下来了,但遇到了一个头疼的事情:微信授权回调域只能写一个,且不支持通配。这下可好了,总不能把每个需要微信登录的二级域名都申请一个应用吧?而且一个开放平台账号最多申请十个。
这种方法网上一大堆,大体思想是,微信授权回调域写一个域名,然后将一个html页面放到这个域名下,微信扫码登录时填写的redirect_url就是到这个html,然后在这个html的js代码中,根据某一个参数去判断当前路由应该重定向到哪个地址。
假如我们有多个地址需要做微信扫码登录,比如我们的研发环境,测试环境,生产环境都需要做微信扫码登陆,但又不想给研发测试环境做外网的端口映射,那么可以使用这种方式。
首先在开放平台的授权回调域中写一个公共授权域名,比如oauth.loooswc.cn
然后将get-wechat-code.html
页面放到该域名下,比如oauth.loooswc.cn/oauth/wechat/get-wechat-code.html
最后在调用微信获取code
请求时,redirect_url
就写oauth.loooswc.cn/oauth/wechat/get-wechat-code.html?env={env}&request=/oauth/wechat/login_callback.do
,通过js代码根据参数env去判断应该重定向到哪个地址,比如env=dev时,则重定向到http://localhost:4200/oauth/wechat/login_callback.do
。
但是这种办法有种缺点,就是会将研发测试环境的地址暴漏出去,而且这个地址对生产环境是没有任何意义的,虽然别人拿到这个内网地址也访问不通,但总归是不好的,所以我们可以使用下面这种方法,通过后台请求重定向。
附带get-wechat-code.html
代码
<html lang="en">
<head>
<meta charset="UTF-8">
<title>微信登陆title>
head>
<body>
<script>
function getUrlParams(key) {
var args = {};
var pairs = location.search.substring(1).split('&');
for (var i = 0; i < pairs.length; i++) {
var pos = pairs[i].indexOf('=');
if (pos === -1) {
continue;
}
args[pairs[i].substring(0, pos)] = decodeURIComponent(pairs[i].substring(pos + 1));
}
return args[key];
}
function getRealRedirectUrl(env){
var realRedirectUrl = 'https://admin.loooswc.cn/';
if('dev' == env){
realRedirectUrl = 'http://localhost:8080/';
}else if('test' == env){
realRedirectUrl = 'http://192.168.1.10:30080/';
}else if('pre-prod' == env){
realRedirectUrl = 'http://192.168.1.20:30080/';
}
return realRedirectUrl;
}
function appendParams(params, url) {
var baseWithSearch = url.split('#')[0];
var hash = url.split('#')[1];
for (var i = 0; i < params.length; i++) {
if (params[i].value !== undefined) {
var newParam = params[i].key + "=" + params[i].value;
if (baseWithSearch.indexOf('?') > 0) {
var oldParamReg = new RegExp(params[i].key + '=[-\\w]{0,40}', 'g');
if (oldParamReg.test(baseWithSearch)) {
baseWithSearch = baseWithSearch.replace(oldParamReg, newParam);
} else {
baseWithSearch += "&" + newParam;
}
} else {
baseWithSearch += "?" + newParam;
}
}
}
if (hash) {
url = baseWithSearch + '#' + hash;
} else {
url = baseWithSearch;
}
return url;
}
var code = getUrlParams('code');
var appId = getUrlParams('appid');
var scope = getUrlParams('scope') || 'snsapi_base';
var state = getUrlParams('state');
var env = getUrlParams('env');
var request = getUrlParams('request');
var redirectUrl;
if (!code) {
redirectUrl = appendParams([{
key: 'appid',
value: appId
}, {
key: 'redirect_uri',
value: encodeURIComponent(location.href)
}, {
key: 'response_type',
value: 'code'
}, {
key: 'scope',
value: scope
}, {
key: 'state',
value: state
}], 'https://open.weixin.qq.com/connect/oauth2/authorize#wechat_redirect');
} else {
redirectUrl = appendParams([{key: 'code', value: code},{
key: 'state',
value: state
}], getRealRedirectUrl(env) + request);
}
location.href = redirectUrl;
// window.close();
script>
body>
html>
大体流程是,在某一个部署了后台服务的代码中,增加一个Controller
接口,该接口通过参数中的env和request,将请求重定向到{env}+{request}接口中,同时附带上自定义和微信回调携带的code和state参数,从而达到转发请求的作用。
在后台程序中,增加一个Controller
接口
@Controller
@RequestMapping("/oauth/wechat")
public class OauthWechatController {
private static final Logger log = LoggerFactory.getLogger(OauthWechatController.class);
@Autowired
private OauthWechatConfiguration oauthWechatConfiguration;
@RequestMapping(value= "/redirect" , method = RequestMethod.GET)
@ResponseBody
public void redirect(HttpServletRequest request, HttpServletResponse response) throws Exception {
String requestUrl = request.getParameter("request");
String code = request.getParameter("code");
String state = request.getParameter("state");
String env = request.getParameter("env");
String platform = request.getParameter("platform");
log.info("wechat授权跳转参数,env:"+env+",requestUrl:"+requestUrl+",code:"+code+",state:"+state);
String redirectUrl = oauthWechatConfiguration.getRedirectProdUrl();
if("dev".equals(env)){
redirectUrl = oauthWechatConfiguration.getRedirectDevUrl();
}else if("test".equals(env)){
redirectUrl = oauthWechatConfiguration.getRedirectTestUrl();
}else if("pre-prod".equals(env)){
redirectUrl = oauthWechatConfiguration.getRedirectPreProdUrl();
}
String url = redirectUrl+requestUrl+"?platform="+platform+"&code="+code+"&state="+state;
log.info("wechat授权跳转地址为:"+ url);
response.sendRedirect(url);
}
@Configuration
@ConfigurationProperties(prefix = "oauth.wechat")
public class OauthWechatConfiguration {
private String redirectDevUrl;
private String redirectTestUrl;
private String redirectPreProdUrl;
private String redirectProdUrl;
private String appId;
private String appSecret;
private String env;
//setter and getter...
}
application.yml
oauth:
wechat:
redirectDevUrl: http://localhost:4201
redirectTestUrl: http://192.168.1.10:30080
redirectPreProdUrl: http://192.168.1.20:30080
redirectProdUrl: https://admin.loooswc.cn
appId: xxxxxxxxxx
appSecret: xxxxxxxxxx
env: dev # dev研发 test测试 pre-prod准生产 prod生产
微信授权回调域填写admin.loooswc.cn
调用请求时redirect_url
填写https://admin.loooswc.cn/oauth/wechat/redirect.do?env=dev&request=/oauth/wechat/login.do
,当然是通过urlEncode
处理过的。
这样就可以实现一个授权回调域对多个域名有效的效果了