diff --git a/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/WechatFilterConfig.java b/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/WechatFilterConfig.java index 8bb9ed7..ca3654a 100644 --- a/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/WechatFilterConfig.java +++ b/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/WechatFilterConfig.java @@ -1,6 +1,8 @@ package com.cm.common.wechat.config; +import com.cm.common.wechat.config.pojo.WechatOfficialAccountProperties; import com.cm.common.wechat.filter.WechatFilter; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -18,13 +20,19 @@ import org.springframework.context.annotation.Configuration; @Configuration public class WechatFilterConfig { + @Autowired + private WechatOfficialAccountProperties wechatOfficialAccountProperties; + @Bean public FilterRegistrationBean wechatFilterRegister() { + WechatFilter wechatFilter = new WechatFilter(); + wechatFilter.setWechatOfficialAccountProperties(wechatOfficialAccountProperties); + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); - filterRegistrationBean.setFilter(new WechatFilter()); - filterRegistrationBean.addUrlPatterns("/wechat/**", "/wechatroute/**"); + filterRegistrationBean.setFilter(wechatFilter); + filterRegistrationBean.addUrlPatterns("/wechat/*", "/wechatroute/*"); filterRegistrationBean.setName("wechatFilter"); - filterRegistrationBean.setOrder(2); + filterRegistrationBean.setOrder(1); return filterRegistrationBean; } diff --git a/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/pojo/WechatOfficialAccountProperties.java b/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/pojo/WechatOfficialAccountProperties.java index d5a56a3..012ba42 100644 --- a/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/pojo/WechatOfficialAccountProperties.java +++ b/cloud-common-wechat/src/main/java/com/cm/common/wechat/config/pojo/WechatOfficialAccountProperties.java @@ -20,9 +20,11 @@ public class WechatOfficialAccountProperties { private WechatOfficialAccountAuthorizeProperties authorize; private String accessTokenUrl; + private String bindUserUrl; private String appId; private String appSecret; private String grantType; + private String configToken; public WechatOfficialAccountAuthorizeProperties getAuthorize() { return authorize; @@ -40,6 +42,14 @@ public class WechatOfficialAccountProperties { this.accessTokenUrl = accessTokenUrl; } + public String getBindUserUrl() { + return bindUserUrl == null ? "" : bindUserUrl.trim(); + } + + public void setBindUserUrl(String bindUserUrl) { + this.bindUserUrl = bindUserUrl; + } + public String getAppId() { return appId == null ? "" : appId.trim(); } @@ -63,4 +73,33 @@ public class WechatOfficialAccountProperties { public void setGrantType(String 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(); + } } diff --git a/cloud-common-wechat/src/main/java/com/cm/common/wechat/controller/WechatOfficialAccountTestRouteController.java b/cloud-common-wechat/src/main/java/com/cm/common/wechat/controller/WechatOfficialAccountTestRouteController.java new file mode 100644 index 0000000..d58e647 --- /dev/null +++ b/cloud-common-wechat/src/main/java/com/cm/common/wechat/controller/WechatOfficialAccountTestRouteController.java @@ -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("

Hello World!!!



点击跳转"); + writer.flush(); + writer.close(); + } + + @GetMapping("index2") + public void index2(HttpServletResponse response) throws IOException { + Writer writer = response.getWriter(); + writer.write("

Config Success



返回"); + writer.flush(); + writer.close(); + } + +} diff --git a/cloud-common-wechat/src/main/java/com/cm/common/wechat/filter/WechatFilter.java b/cloud-common-wechat/src/main/java/com/cm/common/wechat/filter/WechatFilter.java index 7d03010..dff413e 100644 --- a/cloud-common-wechat/src/main/java/com/cm/common/wechat/filter/WechatFilter.java +++ b/cloud-common-wechat/src/main/java/com/cm/common/wechat/filter/WechatFilter.java @@ -1,15 +1,25 @@ package com.cm.common.wechat.filter; import com.cm.common.constants.ISystemConstant; +import com.cm.common.wechat.config.pojo.WechatOfficialAccountProperties; 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.LoggerFactory; +import org.springframework.security.crypto.password.MessageDigestPasswordEncoder; +import org.springframework.util.AntPathMatcher; import org.springframework.util.StringUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 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 @@ -24,12 +34,48 @@ import java.io.IOException; public class WechatFilter implements Filter { 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 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; 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后放行 String codeParameter = request.getParameter("code"); String stateParameter = request.getParameter("state"); @@ -37,18 +83,49 @@ public class WechatFilter implements Filter { try { WechatOfficialAccountManager.getInstance().setUserAccessToken(codeParameter, stateParameter, request.getSession()); } catch (Exception e) { - LOG.error("设置微信公众号session异常"); + LOG.error(e.getMessage(), e); + response.setStatus(404); + filterChain.doFilter(request, response); + return; } - return; } // 判断session是否存在 Object accessToken = request.getSession().getAttribute(ISystemConstant.SESSION_WECHAT_ACCESS_TOKEN); // session 不存在重定向登录 if (StringUtils.isEmpty(accessToken)) { - response.sendRedirect(WechatOfficialAccountManager.getInstance().getAuthorizeUrl(requestUri)); + response.sendRedirect(WechatOfficialAccountManager.getInstance().getAuthorizeUrl(request.getRequestURL().toString())); return; } 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 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; + } + } diff --git a/cloud-common-wechat/src/main/java/com/cm/common/wechat/manager/officialaccount/WechatOfficialAccountManager.java b/cloud-common-wechat/src/main/java/com/cm/common/wechat/manager/officialaccount/WechatOfficialAccountManager.java index d4ebb14..fa48871 100644 --- a/cloud-common-wechat/src/main/java/com/cm/common/wechat/manager/officialaccount/WechatOfficialAccountManager.java +++ b/cloud-common-wechat/src/main/java/com/cm/common/wechat/manager/officialaccount/WechatOfficialAccountManager.java @@ -6,7 +6,6 @@ import com.cm.common.exception.WechatAccessTokenException; import com.cm.common.exception.WechatAccessTokenForUserException; import com.cm.common.exception.WechatUserInfoException; 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.pojo.WechatOfficialAccountAccessToken; 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 org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; +import org.codehaus.jackson.map.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.client.RestTemplateBuilder; @@ -23,6 +23,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpSession; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -61,6 +62,7 @@ public class WechatOfficialAccountManager { if ((currentTime - this.updateTime) >= 5400000) { LOG.debug("刷新公众号 AccessToken 开始"); this.accessToken = getNewAccessToken(); + LOG.debug("accessToken: {}", this.accessToken); this.updateTime = System.currentTimeMillis(); LOG.debug("刷新公众号 AccessToken 结束"); } @@ -95,15 +97,15 @@ public class WechatOfficialAccountManager { /** * 重定向url * - * @param requestUri + * @param redirectUrl * @return */ - public String getAuthorizeUrl(String requestUri) { + public String getAuthorizeUrl(String redirectUrl) { StringBuilder authorizeUrl = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getAuthorizeUrl()); authorizeUrl.append("?appid=").append(wechatOfficialAccountProperties.getAppId()); authorizeUrl.append("&scope=").append(wechatOfficialAccountProperties.getAuthorize().getScope()); 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"); return authorizeUrl.toString(); } @@ -116,12 +118,12 @@ public class WechatOfficialAccountManager { * @param session */ 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); WechatOfficialAccountUserInfo wechatOfficialAccountUserInfo = getUserInfo(wechatOfficialAccountAccessTokenForUser.getAccess_token(), wechatOfficialAccountAccessTokenForUser.getOpenid()); 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); wechatOfficialAccountUser.setToken(token); wechatOfficialAccountUser.setWechatOfficialAccountAccessTokenForUser(wechatOfficialAccountAccessTokenForUser); @@ -142,7 +144,8 @@ public class WechatOfficialAccountManager { Map params = new HashMap<>(1); params.put("signInfo", wechatSignInfo); HttpEntity> requestBody = new HttpEntity<>(params); - ResponseEntity result = restTemplate.postForEntity("", requestBody, Map.class); + ResponseEntity result = restTemplate.postForEntity(wechatOfficialAccountProperties.getBindUserUrl(), requestBody, Map.class); + LOG.debug("状态码:{}", result.getStatusCodeValue()); if (HttpStatus.OK.value() != result.getStatusCodeValue()) { throw new SearchException("获取Token失败"); } @@ -156,16 +159,18 @@ public class WechatOfficialAccountManager { * @param code * @return */ - public WechatOfficialAccountAccessTokenForUser getUserAccessToken(String code) { + public WechatOfficialAccountAccessTokenForUser getUserAccessToken(String code) throws IOException { Map params = new HashMap<>(4); params.put("appid", wechatOfficialAccountProperties.getAppId()); params.put("secret", wechatOfficialAccountProperties.getAppSecret()); params.put("code", code); params.put("grant_type", wechatOfficialAccountProperties.getAuthorize().getGrantType()); + StringBuilder url = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getAccessTokenUrl()); url.append("?appid={appid}&secret={secret}&code={code}&grant_type={grant_type}"); 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) { LOG.error("获取用户AccessToken异常"); throw new WechatAccessTokenForUserException("获取用户AccessToken异常"); @@ -183,14 +188,15 @@ public class WechatOfficialAccountManager { * @param openid * @return */ - public WechatOfficialAccountUserInfo getUserInfo(String accessToken, String openid) { + public WechatOfficialAccountUserInfo getUserInfo(String accessToken, String openid) throws IOException { Map params = new HashMap<>(2); params.put("access_token", accessToken); params.put("openid", openid); StringBuilder url = new StringBuilder(wechatOfficialAccountProperties.getAuthorize().getUserinfoUrl()); url.append("?access_token={access_token}&openid={openid}&lang=zh_CN"); 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) { throw new WechatUserInfoException("获取微信用户信息失败"); } diff --git a/cloud-common-wechat/src/main/java/com/cm/common/wechat/pojo/WechatOfficialAccountUserInfo.java b/cloud-common-wechat/src/main/java/com/cm/common/wechat/pojo/WechatOfficialAccountUserInfo.java index ff6cb41..d8e518c 100644 --- a/cloud-common-wechat/src/main/java/com/cm/common/wechat/pojo/WechatOfficialAccountUserInfo.java +++ b/cloud-common-wechat/src/main/java/com/cm/common/wechat/pojo/WechatOfficialAccountUserInfo.java @@ -23,6 +23,7 @@ public class WechatOfficialAccountUserInfo { private String country; private String headimgurl; private String[] privilege; + private String language; private String unionid; private Integer errcode; private String errmsg; @@ -91,6 +92,14 @@ public class WechatOfficialAccountUserInfo { this.privilege = privilege; } + public String getLanguage() { + return language == null ? "" : language.trim(); + } + + public void setLanguage(String language) { + this.language = language; + } + public String getUnionid() { return unionid == null ? "" : unionid.trim(); }