完善微信绑定功能

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;
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;
}

View File

@ -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();
}
}

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;
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;
}
}
// 判断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<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.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<String, Object> params = new HashMap<>(1);
params.put("signInfo", wechatSignInfo);
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()) {
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<String, Object> 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<String, Object> 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("获取微信用户信息失败");
}

View File

@ -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();
}