完善微信绑定功能

This commit is contained in:
wenc000 2020-03-03 22:18:10 +08:00
parent 17334c098b
commit 4d0b643f7b
6 changed files with 200 additions and 17 deletions

View File

@ -1,6 +1,8 @@
package com.cm.common.wechat.config; package com.cm.common.wechat.config;
import com.cm.common.wechat.config.pojo.WechatOfficialAccountProperties;
import com.cm.common.wechat.filter.WechatFilter; import com.cm.common.wechat.filter.WechatFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -18,13 +20,19 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class WechatFilterConfig { public class WechatFilterConfig {
@Autowired
private WechatOfficialAccountProperties wechatOfficialAccountProperties;
@Bean @Bean
public FilterRegistrationBean wechatFilterRegister() { public FilterRegistrationBean wechatFilterRegister() {
WechatFilter wechatFilter = new WechatFilter();
wechatFilter.setWechatOfficialAccountProperties(wechatOfficialAccountProperties);
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WechatFilter()); filterRegistrationBean.setFilter(wechatFilter);
filterRegistrationBean.addUrlPatterns("/wechat/**", "/wechatroute/**"); filterRegistrationBean.addUrlPatterns("/wechat/*", "/wechatroute/*");
filterRegistrationBean.setName("wechatFilter"); filterRegistrationBean.setName("wechatFilter");
filterRegistrationBean.setOrder(2); filterRegistrationBean.setOrder(1);
return filterRegistrationBean; return filterRegistrationBean;
} }

View File

@ -20,9 +20,11 @@ public class WechatOfficialAccountProperties {
private WechatOfficialAccountAuthorizeProperties authorize; private WechatOfficialAccountAuthorizeProperties authorize;
private String accessTokenUrl; private String accessTokenUrl;
private String bindUserUrl;
private String appId; private String appId;
private String appSecret; private String appSecret;
private String grantType; private String grantType;
private String configToken;
public WechatOfficialAccountAuthorizeProperties getAuthorize() { public WechatOfficialAccountAuthorizeProperties getAuthorize() {
return authorize; return authorize;
@ -40,6 +42,14 @@ public class WechatOfficialAccountProperties {
this.accessTokenUrl = accessTokenUrl; this.accessTokenUrl = accessTokenUrl;
} }
public String getBindUserUrl() {
return bindUserUrl == null ? "" : bindUserUrl.trim();
}
public void setBindUserUrl(String bindUserUrl) {
this.bindUserUrl = bindUserUrl;
}
public String getAppId() { public String getAppId() {
return appId == null ? "" : appId.trim(); return appId == null ? "" : appId.trim();
} }
@ -63,4 +73,33 @@ public class WechatOfficialAccountProperties {
public void setGrantType(String grantType) { public void setGrantType(String grantType) {
this.grantType = grantType; this.grantType = grantType;
} }
public String getConfigToken() {
return configToken == null ? "" : configToken.trim();
}
public void setConfigToken(String configToken) {
this.configToken = configToken;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"authorize\":")
.append(authorize);
sb.append(",\"accessTokenUrl\":")
.append("\"").append(accessTokenUrl).append("\"");
sb.append(",\"bindUserUrl\":")
.append("\"").append(bindUserUrl).append("\"");
sb.append(",\"appId\":")
.append("\"").append(appId).append("\"");
sb.append(",\"appSecret\":")
.append("\"").append(appSecret).append("\"");
sb.append(",\"grantType\":")
.append("\"").append(grantType).append("\"");
sb.append(",\"configToken\":")
.append("\"").append(configToken).append("\"");
sb.append('}');
return sb.toString();
}
} }

View File

@ -0,0 +1,44 @@
package com.cm.common.wechat.controller;
import com.cm.common.constants.ISystemConstant;
import io.swagger.annotations.Api;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
*
* @ClassName: WechatOfficialAccountTestController
* @Description: 微信公众号测试
* @Author: WangGeng
* @Date: 2020/3/3 12:10 下午
* @Version: 1.0
**/
@Api(tags = ISystemConstant.API_TAGS_WECHAT_PREFIX + "页面测试")
@Controller
@RequestMapping(ISystemConstant.WECHAT_ROUTE_PREFIX + "/officialaccount")
public class WechatOfficialAccountTestRouteController {
@GetMapping("index")
public void index(HttpServletResponse response) throws IOException {
Writer writer = response.getWriter();
writer.write("<!DOCTYPE html><html><body><h1>Hello World!!!</h1><br/><br/><a href=\"/usercenter/wechatroute/officialaccount/index2\">点击跳转</a></body></html>");
writer.flush();
writer.close();
}
@GetMapping("index2")
public void index2(HttpServletResponse response) throws IOException {
Writer writer = response.getWriter();
writer.write("<!DOCTYPE html><html><body><h1>Config Success</h1><br/><br/><a href=\"/usercenter/wechatroute/officialaccount/index\">返回</a></body></html>");
writer.flush();
writer.close();
}
}

View File

@ -1,15 +1,25 @@
package com.cm.common.wechat.filter; package com.cm.common.wechat.filter;
import com.cm.common.constants.ISystemConstant; import com.cm.common.constants.ISystemConstant;
import com.cm.common.wechat.config.pojo.WechatOfficialAccountProperties;
import com.cm.common.wechat.manager.officialaccount.WechatOfficialAccountManager; import com.cm.common.wechat.manager.officialaccount.WechatOfficialAccountManager;
import org.apache.shiro.crypto.hash.Sha1Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** /**
* When you feel like quitting. Think about why you started * When you feel like quitting. Think about why you started
@ -24,12 +34,48 @@ import java.io.IOException;
public class WechatFilter implements Filter { public class WechatFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(WechatFilter.class); private static final Logger LOG = LoggerFactory.getLogger(WechatFilter.class);
private WechatOfficialAccountProperties wechatOfficialAccountProperties;
private AntPathMatcher antPathMatcher;
private static final String WECHAT_LOGIN_URL = "/**/wechat/sign/login";
/**
* 微信放行接口
*/
private static final String WECHAT_RELEASE_URL = "/**/wechatrelease/**";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
antPathMatcher = new AntPathMatcher();
}
@Override @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse; HttpServletResponse response = (HttpServletResponse) servletResponse;
String requestUri = request.getRequestURI(); String requestUri = request.getRequestURI();
if (antPathMatcher.match(WECHAT_LOGIN_URL, requestUri) || antPathMatcher.match(WECHAT_RELEASE_URL, requestUri)) {
filterChain.doFilter(request, response);
return;
}
// 判断是不是微信配置校验
String signatureParameter = request.getParameter("signature");
String timestampParameter = request.getParameter("timestamp");
String nonceParameter = request.getParameter("nonce");
String echostrParameter = request.getParameter("echostr");
boolean isWechatConfig = !StringUtils.isEmpty(signatureParameter)
&& !StringUtils.isEmpty(timestampParameter)
&& !StringUtils.isEmpty(nonceParameter)
&& !StringUtils.isEmpty(echostrParameter);
if (isWechatConfig && isWechatOfficialAccountConfigRequest(signatureParameter, timestampParameter, nonceParameter, echostrParameter)) {
Writer writer = response.getWriter();
writer.write(echostrParameter);
writer.flush();
writer.close();
return;
}
// 绑定校验
// 如果参数都存在标识从服务器重定向回页面获取AccessToken后放行 // 如果参数都存在标识从服务器重定向回页面获取AccessToken后放行
String codeParameter = request.getParameter("code"); String codeParameter = request.getParameter("code");
String stateParameter = request.getParameter("state"); String stateParameter = request.getParameter("state");
@ -37,18 +83,49 @@ public class WechatFilter implements Filter {
try { try {
WechatOfficialAccountManager.getInstance().setUserAccessToken(codeParameter, stateParameter, request.getSession()); WechatOfficialAccountManager.getInstance().setUserAccessToken(codeParameter, stateParameter, request.getSession());
} catch (Exception e) { } catch (Exception e) {
LOG.error("设置微信公众号session异常"); LOG.error(e.getMessage(), e);
response.setStatus(404);
filterChain.doFilter(request, response);
return;
} }
return;
} }
// 判断session是否存在 // 判断session是否存在
Object accessToken = request.getSession().getAttribute(ISystemConstant.SESSION_WECHAT_ACCESS_TOKEN); Object accessToken = request.getSession().getAttribute(ISystemConstant.SESSION_WECHAT_ACCESS_TOKEN);
// session 不存在重定向登录 // session 不存在重定向登录
if (StringUtils.isEmpty(accessToken)) { if (StringUtils.isEmpty(accessToken)) {
response.sendRedirect(WechatOfficialAccountManager.getInstance().getAuthorizeUrl(requestUri)); response.sendRedirect(WechatOfficialAccountManager.getInstance().getAuthorizeUrl(request.getRequestURL().toString()));
return; return;
} }
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} }
/**
* 是来自微信的配置请求
*
* @param signature
* @param timestamp
* @param nonce
* @param echostr
* @return
*/
private boolean isWechatOfficialAccountConfigRequest(String signature, String timestamp, String nonce, String echostr) {
List<String> sortList = new ArrayList<>();
sortList.add(wechatOfficialAccountProperties.getConfigToken());
sortList.add(timestamp);
sortList.add(nonce);
Collections.sort(sortList);
StringBuilder newSignature = new StringBuilder();
sortList.forEach(str -> {
newSignature.append(str);
});
if (!StringUtils.pathEquals(signature, new SimpleHash("SHA-1", newSignature.toString()).toString())) {
return false;
}
return true;
}
public void setWechatOfficialAccountProperties(WechatOfficialAccountProperties wechatOfficialAccountProperties) {
this.wechatOfficialAccountProperties = wechatOfficialAccountProperties;
}
} }

View File

@ -6,7 +6,6 @@ import com.cm.common.exception.WechatAccessTokenException;
import com.cm.common.exception.WechatAccessTokenForUserException; import com.cm.common.exception.WechatAccessTokenForUserException;
import com.cm.common.exception.WechatUserInfoException; import com.cm.common.exception.WechatUserInfoException;
import com.cm.common.utils.AesUtil; import com.cm.common.utils.AesUtil;
import com.cm.common.utils.http.RestRequestUtil;
import com.cm.common.wechat.config.pojo.WechatOfficialAccountProperties; import com.cm.common.wechat.config.pojo.WechatOfficialAccountProperties;
import com.cm.common.wechat.pojo.WechatOfficialAccountAccessToken; import com.cm.common.wechat.pojo.WechatOfficialAccountAccessToken;
import com.cm.common.wechat.pojo.WechatOfficialAccountAccessTokenForUser; import com.cm.common.wechat.pojo.WechatOfficialAccountAccessTokenForUser;
@ -14,6 +13,7 @@ import com.cm.common.wechat.pojo.WechatOfficialAccountUser;
import com.cm.common.wechat.pojo.WechatOfficialAccountUserInfo; import com.cm.common.wechat.pojo.WechatOfficialAccountUserInfo;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
@ -23,6 +23,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -61,6 +62,7 @@ public class WechatOfficialAccountManager {
if ((currentTime - this.updateTime) >= 5400000) { if ((currentTime - this.updateTime) >= 5400000) {
LOG.debug("刷新公众号 AccessToken 开始"); LOG.debug("刷新公众号 AccessToken 开始");
this.accessToken = getNewAccessToken(); this.accessToken = getNewAccessToken();
LOG.debug("accessToken: {}", this.accessToken);
this.updateTime = System.currentTimeMillis(); this.updateTime = System.currentTimeMillis();
LOG.debug("刷新公众号 AccessToken 结束"); LOG.debug("刷新公众号 AccessToken 结束");
} }
@ -95,15 +97,15 @@ public class WechatOfficialAccountManager {
/** /**
* 重定向url * 重定向url
* *
* @param requestUri * @param redirectUrl
* @return * @return
*/ */
public String getAuthorizeUrl(String requestUri) { public String getAuthorizeUrl(String redirectUrl) {
StringBuilder authorizeUrl = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getAuthorizeUrl()); StringBuilder authorizeUrl = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getAuthorizeUrl());
authorizeUrl.append("?appid=").append(wechatOfficialAccountProperties.getAppId()); authorizeUrl.append("?appid=").append(wechatOfficialAccountProperties.getAppId());
authorizeUrl.append("&scope=").append(wechatOfficialAccountProperties.getAuthorize().getScope()); authorizeUrl.append("&scope=").append(wechatOfficialAccountProperties.getAuthorize().getScope());
authorizeUrl.append("&state=").append(wechatOfficialAccountProperties.getAuthorize().getState()); authorizeUrl.append("&state=").append(wechatOfficialAccountProperties.getAuthorize().getState());
authorizeUrl.append("&redirect_uri=").append(requestUri); authorizeUrl.append("&redirect_uri=").append(redirectUrl);
authorizeUrl.append("&response_type=code#wechat_redirect"); authorizeUrl.append("&response_type=code#wechat_redirect");
return authorizeUrl.toString(); return authorizeUrl.toString();
} }
@ -116,12 +118,12 @@ public class WechatOfficialAccountManager {
* @param session * @param session
*/ */
public void setUserAccessToken(String code, String state, HttpSession session) throws Exception { public void setUserAccessToken(String code, String state, HttpSession session) throws Exception {
if (!StringUtils.equals(state, wechatOfficialAccountProperties.getAuthorize().getState())) { if (StringUtils.equals(state, wechatOfficialAccountProperties.getAuthorize().getState())) {
WechatOfficialAccountAccessTokenForUser wechatOfficialAccountAccessTokenForUser = getUserAccessToken(code); WechatOfficialAccountAccessTokenForUser wechatOfficialAccountAccessTokenForUser = getUserAccessToken(code);
WechatOfficialAccountUserInfo wechatOfficialAccountUserInfo = getUserInfo(wechatOfficialAccountAccessTokenForUser.getAccess_token(), wechatOfficialAccountAccessTokenForUser.getOpenid()); WechatOfficialAccountUserInfo wechatOfficialAccountUserInfo = getUserInfo(wechatOfficialAccountAccessTokenForUser.getAccess_token(), wechatOfficialAccountAccessTokenForUser.getOpenid());
WechatOfficialAccountUser wechatOfficialAccountUser = new WechatOfficialAccountUser(); WechatOfficialAccountUser wechatOfficialAccountUser = new WechatOfficialAccountUser();
// 绑定用户 | 登录 // 绑定用户 | 登录
String wechatSignInfo = AesUtil.aesCommonEncoder("WECHAT_SIGN_INFO", Base64.encodeBase64String(new StringBuilder(wechatOfficialAccountAccessTokenForUser.getOpenid()).append("_WenG_").append(wechatOfficialAccountProperties.getAppId()).toString().getBytes("UTF-8"))); String wechatSignInfo = Base64.encodeBase64String(AesUtil.aesCommonEncoder("WECHAT_SIGN_INFO", new StringBuilder(wechatOfficialAccountAccessTokenForUser.getOpenid()).append("_WenG_").append(wechatOfficialAccountProperties.getAppId()).toString()).getBytes("UTF-8"));
String token = getAppToken(wechatSignInfo); String token = getAppToken(wechatSignInfo);
wechatOfficialAccountUser.setToken(token); wechatOfficialAccountUser.setToken(token);
wechatOfficialAccountUser.setWechatOfficialAccountAccessTokenForUser(wechatOfficialAccountAccessTokenForUser); wechatOfficialAccountUser.setWechatOfficialAccountAccessTokenForUser(wechatOfficialAccountAccessTokenForUser);
@ -142,7 +144,8 @@ public class WechatOfficialAccountManager {
Map<String, Object> params = new HashMap<>(1); Map<String, Object> params = new HashMap<>(1);
params.put("signInfo", wechatSignInfo); params.put("signInfo", wechatSignInfo);
HttpEntity<Map<String, Object>> requestBody = new HttpEntity<>(params); HttpEntity<Map<String, Object>> requestBody = new HttpEntity<>(params);
ResponseEntity<Map> result = restTemplate.postForEntity("", requestBody, Map.class); ResponseEntity<Map> result = restTemplate.postForEntity(wechatOfficialAccountProperties.getBindUserUrl(), requestBody, Map.class);
LOG.debug("状态码:{}", result.getStatusCodeValue());
if (HttpStatus.OK.value() != result.getStatusCodeValue()) { if (HttpStatus.OK.value() != result.getStatusCodeValue()) {
throw new SearchException("获取Token失败"); throw new SearchException("获取Token失败");
} }
@ -156,16 +159,18 @@ public class WechatOfficialAccountManager {
* @param code * @param code
* @return * @return
*/ */
public WechatOfficialAccountAccessTokenForUser getUserAccessToken(String code) { public WechatOfficialAccountAccessTokenForUser getUserAccessToken(String code) throws IOException {
Map<String, Object> params = new HashMap<>(4); Map<String, Object> params = new HashMap<>(4);
params.put("appid", wechatOfficialAccountProperties.getAppId()); params.put("appid", wechatOfficialAccountProperties.getAppId());
params.put("secret", wechatOfficialAccountProperties.getAppSecret()); params.put("secret", wechatOfficialAccountProperties.getAppSecret());
params.put("code", code); params.put("code", code);
params.put("grant_type", wechatOfficialAccountProperties.getAuthorize().getGrantType()); params.put("grant_type", wechatOfficialAccountProperties.getAuthorize().getGrantType());
StringBuilder url = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getAccessTokenUrl()); StringBuilder url = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getAccessTokenUrl());
url.append("?appid={appid}&secret={secret}&code={code}&grant_type={grant_type}"); url.append("?appid={appid}&secret={secret}&code={code}&grant_type={grant_type}");
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
WechatOfficialAccountAccessTokenForUser wechatOfficialAccountAccessTokenForUser = restTemplate.getForObject(url.toString(), WechatOfficialAccountAccessTokenForUser.class, params); String resultJson = restTemplate.getForObject(url.toString(), String.class, params);
WechatOfficialAccountAccessTokenForUser wechatOfficialAccountAccessTokenForUser = new ObjectMapper().readValue(resultJson, WechatOfficialAccountAccessTokenForUser.class);
if (wechatOfficialAccountAccessTokenForUser == null) { if (wechatOfficialAccountAccessTokenForUser == null) {
LOG.error("获取用户AccessToken异常"); LOG.error("获取用户AccessToken异常");
throw new WechatAccessTokenForUserException("获取用户AccessToken异常"); throw new WechatAccessTokenForUserException("获取用户AccessToken异常");
@ -183,14 +188,15 @@ public class WechatOfficialAccountManager {
* @param openid * @param openid
* @return * @return
*/ */
public WechatOfficialAccountUserInfo getUserInfo(String accessToken, String openid) { public WechatOfficialAccountUserInfo getUserInfo(String accessToken, String openid) throws IOException {
Map<String, Object> params = new HashMap<>(2); Map<String, Object> params = new HashMap<>(2);
params.put("access_token", accessToken); params.put("access_token", accessToken);
params.put("openid", openid); params.put("openid", openid);
StringBuilder url = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getUserinfoUrl()); StringBuilder url = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getUserinfoUrl());
url.append("?access_token={access_token}&openid={openid}&lang=zh_CN"); url.append("?access_token={access_token}&openid={openid}&lang=zh_CN");
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
WechatOfficialAccountUserInfo wechatOfficialAccountUserInfo = restTemplate.getForObject(url.toString(), WechatOfficialAccountUserInfo.class, params); String resultJson = restTemplate.getForObject(url.toString(), String.class, params);
WechatOfficialAccountUserInfo wechatOfficialAccountUserInfo = new ObjectMapper().readValue(resultJson, WechatOfficialAccountUserInfo.class);
if (wechatOfficialAccountUserInfo == null) { if (wechatOfficialAccountUserInfo == null) {
throw new WechatUserInfoException("获取微信用户信息失败"); throw new WechatUserInfoException("获取微信用户信息失败");
} }

View File

@ -23,6 +23,7 @@ public class WechatOfficialAccountUserInfo {
private String country; private String country;
private String headimgurl; private String headimgurl;
private String[] privilege; private String[] privilege;
private String language;
private String unionid; private String unionid;
private Integer errcode; private Integer errcode;
private String errmsg; private String errmsg;
@ -91,6 +92,14 @@ public class WechatOfficialAccountUserInfo {
this.privilege = privilege; this.privilege = privilege;
} }
public String getLanguage() {
return language == null ? "" : language.trim();
}
public void setLanguage(String language) {
this.language = language;
}
public String getUnionid() { public String getUnionid() {
return unionid == null ? "" : unionid.trim(); return unionid == null ? "" : unionid.trim();
} }