Commit 5c402aa7 authored by nick zheng's avatar nick zheng

feat(software-copyright): 企业微信扫码/授权登录

parent f4fb890f
......@@ -28,6 +28,8 @@ public class LoginChannelBuilder implements ApplicationContextAware {
return applicationContext.getBean(EmailLoginChannel.class);
}else if (UserLoginConstants.LoginType.MEMBER_PLATFOMR_GOOGLE.equals(loginChannel)){
return applicationContext.getBean(GoogleLoginChannel.class);
} else if (UserLoginConstants.LoginType.MEMBER_PLATFOMR_WECOM.equals(loginChannel)) {
return applicationContext.getBean(WecomLoginChannel.class);
}
throw new I18nMessageException("exception/third.party.authorization.channel.abnormal");
}
......
package cn.com.poc.user.builder;
import cn.com.poc.common.utils.Assert;
import cn.com.poc.common.utils.StringUtils;
import cn.com.poc.equity.aggregate.MemberEquityService;
import cn.com.poc.user.dto.MemberLoginRequestDto;
import cn.com.poc.user.entity.MemberInfoEntity;
import cn.com.poc.user.query.CheckMemberInfoQueryCondition;
import cn.com.poc.user.query.CheckMemberInfoQueryItem;
import cn.com.poc.user.service.BizMemberInfoService;
import cn.com.poc.user.wecom.constant.WecomConstant;
import cn.com.poc.user.wecom.domain.WecomUserProfile;
import cn.com.poc.user.wecom.service.WecomLoginService;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import cn.com.yict.framemax.frame.service.FmxParamConfigService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class WecomLoginChannel implements LoginChannelService {
@Resource
private WecomLoginService wecomLoginService;
@Resource
private BizMemberInfoService bizMemberInfoService;
@Resource
private MemberEquityService memberEquityService;
@Resource
private FmxParamConfigService fmxParamConfigService;
@Override
public MemberInfoEntity doLogin(MemberLoginRequestDto memberLoginRequest) throws Exception {
Assert.notBlank(memberLoginRequest.getAuthCode());
Assert.notBlank(memberLoginRequest.getLoginType(), "loginType");
WecomUserProfile profile = wecomLoginService.getUserProfile(memberLoginRequest.getAuthCode(), memberLoginRequest.getLoginType());
if (profile == null) {
throw new I18nMessageException("exception/system.error");
}
MemberInfoEntity member = findExistingMember(profile);
if (member != null) {
return member;
}
return registerByWecom(profile);
}
private MemberInfoEntity findExistingMember(WecomUserProfile profile) throws Exception {
if (StringUtils.isNotBlank(profile.getMobile())) {
MemberInfoEntity member = findByMobile(profile.getMobile());
if (member != null) {
return member;
}
}
if (StringUtils.isNotBlank(profile.getEmail())) {
MemberInfoEntity member = findByEmail(profile.getEmail());
if (member != null) {
return member;
}
}
String account = buildWecomAccount(profile);
if (StringUtils.isBlank(account)) {
return null;
}
return bizMemberInfoService.getMemberEntityByAccount(account);
}
private MemberInfoEntity registerByWecom(WecomUserProfile profile) {
String account = resolveAccount(profile);
if (StringUtils.isBlank(account)) {
throw new I18nMessageException("exception/system.error");
}
MemberInfoEntity memberInfoEntity = new MemberInfoEntity();
memberInfoEntity.setAccount(account);
memberInfoEntity.setNickName(StringUtils.defaultIfBlank(profile.getName(), account));
memberInfoEntity.setAvatarUrl(resolveAvatar(profile.getAvatarUrl()));
memberInfoEntity.setMobilePhone(profile.getMobile());
memberInfoEntity.setEmail(profile.getEmail());
MemberInfoEntity result = bizMemberInfoService.createMemberInfo(memberInfoEntity);
try {
memberEquityService.initMemberEquity(result.getMemberId().longValue());
} catch (Exception e) {
throw new I18nMessageException("exception/system.error");
}
return result;
}
private MemberInfoEntity findByMobile(String mobile) throws Exception {
CheckMemberInfoQueryCondition condition = new CheckMemberInfoQueryCondition();
condition.setMobilePhone(mobile);
List<CheckMemberInfoQueryItem> items = bizMemberInfoService.checkMemberInfoIsExist(condition);
if (CollectionUtils.isEmpty(items)) {
return null;
}
return bizMemberInfoService.getById(items.get(0).getMemberId());
}
private MemberInfoEntity findByEmail(String email) throws Exception {
CheckMemberInfoQueryCondition condition = new CheckMemberInfoQueryCondition();
condition.setEmail(email);
List<CheckMemberInfoQueryItem> items = bizMemberInfoService.checkMemberInfoIsExist(condition);
if (CollectionUtils.isEmpty(items)) {
return null;
}
return bizMemberInfoService.getById(items.get(0).getMemberId());
}
private String resolveAccount(WecomUserProfile profile) {
if (StringUtils.isNotBlank(profile.getMobile())) {
return profile.getMobile();
}
if (StringUtils.isNotBlank(profile.getEmail())) {
return profile.getEmail();
}
return buildWecomAccount(profile);
}
private String resolveAvatar(String avatarUrl) {
if (StringUtils.isNotBlank(avatarUrl)) {
return avatarUrl;
}
return fmxParamConfigService.getParam("member.default.avatar");
}
private String buildWecomAccount(WecomUserProfile profile) {
String userId = StringUtils.defaultIfBlank(profile.getUserId(), profile.getOpenUserId());
if (StringUtils.isBlank(userId)) {
return null;
}
String corpId = profile.getCorpId();
if (StringUtils.isBlank(corpId)) {
return WecomConstant.AccountPrefix.WECOM + userId;
}
return WecomConstant.AccountPrefix.WECOM + corpId + ":" + userId;
}
}
......@@ -12,6 +12,7 @@ public class UserLoginConstants {
public final static String MEMBER_PLATFOMR_PW="MEMBER_PLATFOMR_PW";
public final static String MEMBER_PLATFOMR_EMAIL="MEMBER_PLATFOMR_EMAIL";
public final static String MEMBER_PLATFOMR_GOOGLE="MEMBER_PLATFOMR_GOOGLE";
public final static String MEMBER_PLATFOMR_WECOM="MEMBER_PLATFOMR_WECOM";
}
......
......@@ -23,6 +23,11 @@ public class MemberLoginRequestDto {
*/
private String authCode;
/**
* 第三方登录类型
*/
private String loginType;
/**
* 密码(md5)
*/
......@@ -52,6 +57,14 @@ public class MemberLoginRequestDto {
this.authCode = authCode;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
public String getPassword() {
return password;
}
......
package cn.com.poc.user.wecom.api;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHeader;
public interface WecomBaseApi {
String QY_BASE_URI = "https://qyapi.weixin.qq.com";
String OPEN_URI = "https://open.weixin.qq.com";
String QY_OPEN_URI = "https://open.work.weixin.qq.com";
Header JSON_HEADER = new BasicHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
}
package cn.com.poc.user.wecom.api;
import cn.com.poc.common.utils.http.LocalHttpClient;
import cn.com.poc.user.wecom.domain.WecomLoginInfoResponse;
import cn.com.poc.user.wecom.domain.WecomUserInfo3rd;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.StringEntity;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
@Service
public class WecomServiceApi implements WecomBaseApi {
public WecomUserInfo3rd getUserInfo3rd(String suiteAccessToken, String code) {
HttpUriRequest httpUriRequest = RequestBuilder.get()
.setUri(QY_BASE_URI + "/cgi-bin/service/getuserinfo3rd")
.addParameter("suite_access_token", suiteAccessToken)
.addParameter("code", code)
.build();
return LocalHttpClient.executeJsonResult(httpUriRequest, WecomUserInfo3rd.class);
}
public WecomUserInfo3rd getUserDetail3rd(String suiteAccessToken, String userTicket) {
String jsonData = String.format("{\"user_ticket\":\"%1$s\"}", userTicket);
HttpUriRequest httpUriRequest = RequestBuilder.post()
.setHeader(JSON_HEADER)
.addParameter("suite_access_token", suiteAccessToken)
.setUri(QY_BASE_URI + "/cgi-bin/service/getuserdetail3rd")
.setEntity(new StringEntity(jsonData, StandardCharsets.UTF_8))
.build();
return LocalHttpClient.executeJsonResult(httpUriRequest, WecomUserInfo3rd.class);
}
public WecomLoginInfoResponse getLoginInfo(String providerAccessToken, String authCode) {
String jsonData = String.format("{\"auth_code\":\"%1$s\"}", authCode);
HttpUriRequest httpUriRequest = RequestBuilder.post()
.setHeader(JSON_HEADER)
.addParameter("access_token", providerAccessToken)
.setUri(QY_BASE_URI + "/cgi-bin/service/get_login_info")
.setEntity(new StringEntity(jsonData, StandardCharsets.UTF_8))
.build();
return LocalHttpClient.executeJsonResult(httpUriRequest, WecomLoginInfoResponse.class);
}
}
package cn.com.poc.user.wecom.api;
import cn.com.poc.common.utils.http.LocalHttpClient;
import cn.com.poc.user.wecom.domain.WecomProviderAccessToken;
import cn.com.poc.user.wecom.domain.WecomSuiteAccessToken;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.StringEntity;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
@Service
public class WecomTokenApi implements WecomBaseApi {
public WecomProviderAccessToken getProviderToken(String corpId, String providerSecret) {
String jsonData = String.format("{\"corpid\":\"%1$s\",\"provider_secret\":\"%2$s\"}", corpId, providerSecret);
HttpUriRequest httpUriRequest = RequestBuilder.post()
.setHeader(JSON_HEADER)
.setUri(QY_BASE_URI + "/cgi-bin/service/get_provider_token")
.setEntity(new StringEntity(jsonData, StandardCharsets.UTF_8))
.build();
return LocalHttpClient.executeJsonResult(httpUriRequest, WecomProviderAccessToken.class);
}
public WecomSuiteAccessToken getSuiteToken(String suiteId, String suiteSecret, String suiteTicket) {
String jsonData = String.format("{\"suite_id\":\"%1$s\",\"suite_secret\":\"%2$s\",\"suite_ticket\":\"%3$s\"}",
suiteId, suiteSecret, suiteTicket);
HttpUriRequest httpUriRequest = RequestBuilder.post()
.setHeader(JSON_HEADER)
.setUri(QY_BASE_URI + "/cgi-bin/service/get_suite_token")
.setEntity(new StringEntity(jsonData, StandardCharsets.UTF_8))
.build();
return LocalHttpClient.executeJsonResult(httpUriRequest, WecomSuiteAccessToken.class);
}
}
package cn.com.poc.user.wecom.constant;
public final class WecomConstant {
private WecomConstant() {
}
public interface LoginType {
String QR_CODE_LOGIN = "qr_code_login";
String WEB_PAGE_LOGIN = "web_page_login";
}
public interface RedisKey {
String SUITE_TICKET = "wecom:suite_ticket";
String SUITE_ACCESS_TOKEN = "wecom:suite_access_token";
String PROVIDER_ACCESS_TOKEN = "wecom:provider_access_token";
}
public interface AccountPrefix {
String WECOM = "wecom:";
}
}
package cn.com.poc.user.wecom.domain;
import java.io.Serializable;
public class WecomBaseResult implements Serializable {
private static final String SUCCESS_CODE = "0";
private String errcode;
private String errmsg;
public String getErrcode() {
return errcode;
}
public void setErrcode(String errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
public boolean isSuccess() {
return errcode == null || "".equals(errcode) || SUCCESS_CODE.equals(errcode);
}
}
package cn.com.poc.user.wecom.domain;
import java.io.Serializable;
public class WecomCorpInfo implements Serializable {
private String corp_name;
private String corpid;
private String corp_type;
private String corp_square_logo_url;
public String getCorp_name() {
return corp_name;
}
public void setCorp_name(String corp_name) {
this.corp_name = corp_name;
}
public String getCorpid() {
return corpid;
}
public void setCorpid(String corpid) {
this.corpid = corpid;
}
public String getCorp_type() {
return corp_type;
}
public void setCorp_type(String corp_type) {
this.corp_type = corp_type;
}
public String getCorp_square_logo_url() {
return corp_square_logo_url;
}
public void setCorp_square_logo_url(String corp_square_logo_url) {
this.corp_square_logo_url = corp_square_logo_url;
}
}
package cn.com.poc.user.wecom.domain;
public class WecomLoginInfoResponse extends WecomBaseResult {
private Integer usertype;
private WecomUserInfo user_info;
private WecomCorpInfo corp_info;
private String agent;
private String auth_info;
public Integer getUsertype() {
return usertype;
}
public void setUsertype(Integer usertype) {
this.usertype = usertype;
}
public WecomUserInfo getUser_info() {
return user_info;
}
public void setUser_info(WecomUserInfo user_info) {
this.user_info = user_info;
}
public WecomCorpInfo getCorp_info() {
return corp_info;
}
public void setCorp_info(WecomCorpInfo corp_info) {
this.corp_info = corp_info;
}
public String getAgent() {
return agent;
}
public void setAgent(String agent) {
this.agent = agent;
}
public String getAuth_info() {
return auth_info;
}
public void setAuth_info(String auth_info) {
this.auth_info = auth_info;
}
}
package cn.com.poc.user.wecom.domain;
public class WecomLoginUrlDto {
private String url;
private String loginType;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
}
package cn.com.poc.user.wecom.domain;
public class WecomProviderAccessToken extends WecomBaseResult {
private String provider_access_token;
private int expires_in;
public String getProvider_access_token() {
return provider_access_token;
}
public void setProvider_access_token(String provider_access_token) {
this.provider_access_token = provider_access_token;
}
public int getExpires_in() {
return expires_in;
}
public void setExpires_in(int expires_in) {
this.expires_in = expires_in;
}
}
package cn.com.poc.user.wecom.domain;
public class WecomSuiteAccessToken extends WecomBaseResult {
private String suite_access_token;
private int expires_in;
public String getSuite_access_token() {
return suite_access_token;
}
public void setSuite_access_token(String suite_access_token) {
this.suite_access_token = suite_access_token;
}
public int getExpires_in() {
return expires_in;
}
public void setExpires_in(int expires_in) {
this.expires_in = expires_in;
}
}
package cn.com.poc.user.wecom.domain;
public class WecomUserInfo extends WecomBaseResult {
private String userid;
private String name;
private String mobile;
private String email;
private String avatar;
private String open_userid;
private String qr_code;
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getOpen_userid() {
return open_userid;
}
public void setOpen_userid(String open_userid) {
this.open_userid = open_userid;
}
public String getQr_code() {
return qr_code;
}
public void setQr_code(String qr_code) {
this.qr_code = qr_code;
}
}
package cn.com.poc.user.wecom.domain;
public class WecomUserInfo3rd extends WecomBaseResult {
private String CorpId;
private String UserId;
private String DeviceId;
private String user_ticket;
private Integer expires_in;
private String open_userid;
private String external_userid;
private String OpenId;
private String corpid;
private String userid;
private String name;
private Integer gender;
private String avatar;
private String qr_code;
private String mobile;
private String email;
public String getCorpId() {
return CorpId;
}
public void setCorpId(String corpId) {
CorpId = corpId;
}
public String getUserId() {
return UserId;
}
public void setUserId(String userId) {
UserId = userId;
}
public String getDeviceId() {
return DeviceId;
}
public void setDeviceId(String deviceId) {
DeviceId = deviceId;
}
public String getUser_ticket() {
return user_ticket;
}
public void setUser_ticket(String user_ticket) {
this.user_ticket = user_ticket;
}
public Integer getExpires_in() {
return expires_in;
}
public void setExpires_in(Integer expires_in) {
this.expires_in = expires_in;
}
public String getOpen_userid() {
return open_userid;
}
public void setOpen_userid(String open_userid) {
this.open_userid = open_userid;
}
public String getExternal_userid() {
return external_userid;
}
public void setExternal_userid(String external_userid) {
this.external_userid = external_userid;
}
public String getOpenId() {
return OpenId;
}
public void setOpenId(String openId) {
OpenId = openId;
}
public String getCorpid() {
return corpid;
}
public void setCorpid(String corpid) {
this.corpid = corpid;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getQr_code() {
return qr_code;
}
public void setQr_code(String qr_code) {
this.qr_code = qr_code;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
package cn.com.poc.user.wecom.domain;
public class WecomUserProfile {
private String corpId;
private String userId;
private String openUserId;
private String name;
private String avatarUrl;
private String mobile;
private String email;
public String getCorpId() {
return corpId;
}
public void setCorpId(String corpId) {
this.corpId = corpId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getOpenUserId() {
return openUserId;
}
public void setOpenUserId(String openUserId) {
this.openUserId = openUserId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
package cn.com.poc.user.wecom.domain.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class WecomProperties {
@Value("${wecom.corpid:}")
private String corpId;
@Value("${wecom.redirect_url:}")
private String redirectUrl;
@Value("${wecom.provider_secret:}")
private String providerSecret;
@Value("${wecom.suite_id:}")
private String suiteId;
@Value("${wecom.suite_secret:}")
private String suiteSecret;
@Value("${wecom.token:}")
private String token;
@Value("${wecom.encoding_aes_key:}")
private String encodingAesKey;
public String getCorpId() {
return corpId;
}
public String getRedirectUrl() {
return redirectUrl;
}
public String getProviderSecret() {
return providerSecret;
}
public String getSuiteId() {
return suiteId;
}
public String getSuiteSecret() {
return suiteSecret;
}
public String getToken() {
return token;
}
public String getEncodingAesKey() {
return encodingAesKey;
}
}
package cn.com.poc.user.wecom.rest;
import cn.com.yict.framemax.core.rest.BaseRest;
import cn.com.yict.framemax.web.permission.Access;
import cn.com.yict.framemax.web.permission.Permission;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Permission(Access.Anonymous)
public interface WecomCallbackRest extends BaseRest {
void commandEvent(HttpServletRequest request, HttpServletResponse response,
@RequestParam(value = "msg_signature") String msgSignature,
@RequestParam(value = "timestamp") String timestamp,
@RequestParam(value = "nonce") String nonce,
@RequestParam(value = "echostr", required = false) String data) throws Exception;
}
package cn.com.poc.user.wecom.rest;
import cn.com.poc.user.dto.MemberLoginResponseDto;
import cn.com.poc.user.wecom.domain.WecomLoginUrlDto;
import cn.com.yict.framemax.core.rest.BaseRest;
import cn.com.yict.framemax.web.permission.Access;
import cn.com.yict.framemax.web.permission.Permission;
import org.springframework.web.bind.annotation.RequestParam;
@Permission(Access.Anonymous)
public interface WecomLoginRest extends BaseRest {
WecomLoginUrlDto getOauthUrl(@RequestParam(required = false, name = "requesturl") String requestUrl,
@RequestParam(required = false, name = "scope") String scope,
@RequestParam(required = false, name = "state") String state) throws Exception;
WecomLoginUrlDto getQrConnectUrl(@RequestParam(required = false, name = "requesturl") String requestUrl,
@RequestParam(required = false, name = "userType") String userType,
@RequestParam(required = false, name = "state") String state) throws Exception;
MemberLoginResponseDto toLogin(@RequestParam String code, @RequestParam String loginType) throws Exception;
}
package cn.com.poc.user.wecom.rest.impl;
import cn.com.poc.common.service.RedisService;
import cn.com.poc.common.utils.StringUtils;
import cn.com.poc.user.wecom.constant.WecomConstant;
import cn.com.poc.user.wecom.domain.properties.WecomProperties;
import cn.com.poc.user.wecom.rest.WecomCallbackRest;
import cn.com.poc.user.wecom.util.WXBizMsgCrypt;
import org.json.JSONObject;
import org.json.XML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.PrintWriter;
@Component
public class WecomCallbackRestImpl implements WecomCallbackRest {
private static final Logger logger = LoggerFactory.getLogger(WecomCallbackRestImpl.class);
@Resource
private WecomProperties wecomProperties;
@Resource
private RedisService redisService;
@Override
public void commandEvent(HttpServletRequest request, HttpServletResponse response, String msgSignature, String timestamp, String nonce, String data) throws Exception {
String method = request.getMethod();
if ("GET".equalsIgnoreCase(method)) {
handleVerify(response, msgSignature, timestamp, nonce, data);
return;
}
if ("POST".equalsIgnoreCase(method)) {
handleSuiteTicket(request, response, msgSignature, timestamp, nonce);
}
}
private void handleVerify(HttpServletResponse response, String msgSignature, String timestamp, String nonce, String data) throws Exception {
PrintWriter writer = response.getWriter();
WXBizMsgCrypt wxcpt = buildSuiteCrypt();
String echoStr = wxcpt.VerifyURL(msgSignature, timestamp, nonce, data);
writer.print(echoStr);
writer.flush();
}
private void handleSuiteTicket(HttpServletRequest request, HttpServletResponse response, String msgSignature, String timestamp, String nonce) throws Exception {
StringBuilder body = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
body.append(line);
}
WXBizMsgCrypt wxcpt = buildSuiteCrypt();
String decryptedXml = wxcpt.DecryptMsg(msgSignature, timestamp, nonce, body.toString());
logger.info("wecom suite callback payload: {}", decryptedXml);
JSONObject jsonObject = XML.toJSONObject(decryptedXml).getJSONObject("xml");
String infoType = jsonObject.optString("InfoType");
if ("suite_ticket".equalsIgnoreCase(infoType)) {
String suiteId = jsonObject.optString("SuiteId");
String suiteTicket = jsonObject.optString("SuiteTicket");
if (StringUtils.isNotBlank(suiteId) && StringUtils.isNotBlank(suiteTicket)) {
String redisKey = WecomConstant.RedisKey.SUITE_TICKET + ":" + suiteId;
redisService.set(redisKey, suiteTicket);
logger.info("stored suite_ticket for suiteId {}", suiteId);
}
}
PrintWriter writer = response.getWriter();
writer.print("success");
writer.flush();
}
private WXBizMsgCrypt buildSuiteCrypt() throws Exception {
return new WXBizMsgCrypt(wecomProperties.getToken(), wecomProperties.getEncodingAesKey(), wecomProperties.getSuiteId());
}
}
package cn.com.poc.user.wecom.rest.impl;
import cn.com.poc.common.utils.Assert;
import cn.com.poc.common.utils.StringUtils;
import cn.com.poc.support.security.oauth.constants.OauthConstants;
import cn.com.poc.user.builder.LoginChannelBuilder;
import cn.com.poc.user.constants.UserLoginConstants;
import cn.com.poc.user.dto.MemberLoginRequestDto;
import cn.com.poc.user.dto.MemberLoginResponseDto;
import cn.com.poc.user.entity.MemberInfoEntity;
import cn.com.poc.user.wecom.constant.WecomConstant;
import cn.com.poc.user.wecom.domain.WecomLoginUrlDto;
import cn.com.poc.user.wecom.domain.properties.WecomProperties;
import cn.com.poc.user.wecom.rest.WecomLoginRest;
import cn.com.poc.user.wecom.service.WecomComponentService;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import cn.com.yict.framemax.security.oauth.OauthAccesstokenManager;
import cn.com.yict.framemax.security.oauth.entity.OauthResultEntity;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class WecomLoginRestImpl implements WecomLoginRest {
@Resource
private WecomProperties wecomProperties;
@Resource
private WecomComponentService wecomComponentService;
@Resource
private LoginChannelBuilder loginChannelBuilder;
@Resource
private OauthAccesstokenManager tokenManager;
@Override
public WecomLoginUrlDto getOauthUrl(String requestUrl, String scope, String state) throws Exception {
String redirectUrl = StringUtils.isBlank(requestUrl) ? wecomProperties.getRedirectUrl() : requestUrl;
String suiteId = wecomProperties.getSuiteId();
Assert.notBlank(redirectUrl, "redirectUrl");
Assert.notBlank(suiteId, "suiteId");
String scopeValue = StringUtils.defaultIfBlank(scope, "snsapi_privateinfo");
String stateValue = StringUtils.defaultIfBlank(state, WecomConstant.LoginType.WEB_PAGE_LOGIN);
String oauthUrl = wecomComponentService.oauth2Authorize(suiteId, redirectUrl, "code", scopeValue, stateValue);
WecomLoginUrlDto result = new WecomLoginUrlDto();
result.setLoginType(WecomConstant.LoginType.WEB_PAGE_LOGIN);
result.setUrl(oauthUrl);
return result;
}
@Override
public WecomLoginUrlDto getQrConnectUrl(String requestUrl, String userType, String state) throws Exception {
String redirectUrl = StringUtils.isBlank(requestUrl) ? wecomProperties.getRedirectUrl() : requestUrl;
String corpId = wecomProperties.getCorpId();
Assert.notBlank(redirectUrl, "redirectUrl");
Assert.notBlank(corpId, "corpId");
String stateValue = StringUtils.defaultIfBlank(state, WecomConstant.LoginType.QR_CODE_LOGIN);
String qrUrl = wecomComponentService.connectQrconnect(corpId, redirectUrl, userType, stateValue);
WecomLoginUrlDto result = new WecomLoginUrlDto();
result.setLoginType(WecomConstant.LoginType.QR_CODE_LOGIN);
result.setUrl(qrUrl);
return result;
}
@Override
public MemberLoginResponseDto toLogin(String code, String loginType) throws Exception {
Assert.notBlank(code, "code");
Assert.notBlank(loginType, "loginType");
MemberLoginRequestDto request = new MemberLoginRequestDto();
request.setLoginChannel(UserLoginConstants.LoginType.MEMBER_PLATFOMR_WECOM);
request.setAuthCode(code);
request.setLoginType(loginType);
MemberInfoEntity memberInfoEntity = (MemberInfoEntity) loginChannelBuilder
.getService(request.getLoginChannel())
.doLogin(request);
if (memberInfoEntity == null) {
throw new I18nMessageException("exception/system.error");
}
OauthResultEntity auth = tokenManager.auth(OauthConstants.TypePrefix.MEMBER_DOMAIN + memberInfoEntity.getMemberId(), null, null);
MemberLoginResponseDto response = new MemberLoginResponseDto();
response.setMemberId(memberInfoEntity.getMemberId());
response.setNickName(memberInfoEntity.getNickName());
response.setAvatarUrl(memberInfoEntity.getAvatarUrl());
response.setMobilePhone(memberInfoEntity.getMobilePhone());
response.setToken(auth.getToken());
return response;
}
}
package cn.com.poc.user.wecom.service;
import cn.com.poc.common.utils.StringUtils;
import cn.com.poc.user.wecom.api.WecomBaseApi;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@Service
public class WecomComponentService implements WecomBaseApi {
private final Logger logger = Logger.getLogger(this.getClass());
public String oauth2Authorize(String appid, String redirectUri, String responseType, String scope, String state) {
try {
StringBuilder sb = new StringBuilder();
sb.append(OPEN_URI + "/connect/oauth2/authorize?")
.append("appid=").append(appid)
.append("&redirect_uri=").append(URLEncoder.encode(redirectUri, "utf-8"))
.append("&response_type=").append(responseType)
.append("&scope=").append(scope)
.append("&state=").append(state)
.append("#wechat_redirect");
return sb.toString();
} catch (UnsupportedEncodingException e) {
logger.error("oauth2Authorize error", e);
return null;
}
}
public String connectQrconnect(String appid, String redirectUri, String userType, String state) {
try {
StringBuilder sb = new StringBuilder();
sb.append(QY_OPEN_URI + "/wwopen/sso/3rd_qrConnect?")
.append("appid=").append(appid)
.append("&redirect_uri=").append(URLEncoder.encode(redirectUri, "utf-8"))
.append("&usertype=").append(StringUtils.defaultString(userType, "member"))
.append("&state=").append(state == null ? "" : state);
return sb.toString();
} catch (UnsupportedEncodingException e) {
logger.error("connectQrconnect error", e);
return null;
}
}
}
package cn.com.poc.user.wecom.service;
import cn.com.poc.common.utils.Assert;
import cn.com.poc.common.utils.StringUtils;
import cn.com.poc.user.wecom.constant.WecomConstant;
import cn.com.poc.user.wecom.domain.WecomCorpInfo;
import cn.com.poc.user.wecom.domain.WecomLoginInfoResponse;
import cn.com.poc.user.wecom.domain.WecomUserInfo;
import cn.com.poc.user.wecom.domain.WecomUserInfo3rd;
import cn.com.poc.user.wecom.domain.WecomUserProfile;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class WecomLoginService {
@Resource
private WecomTokenService wecomTokenService;
@Resource
private WecomUserService wecomUserService;
public WecomUserProfile getUserProfile(String code, String loginType) {
Assert.notBlank(code, "code");
Assert.notBlank(loginType, "loginType");
if (WecomConstant.LoginType.QR_CODE_LOGIN.equals(loginType)) {
return getProfileByQrCode(code);
}
if (WecomConstant.LoginType.WEB_PAGE_LOGIN.equals(loginType)) {
return getProfileByWebPage(code);
}
throw new I18nMessageException("exception/system.error");
}
private WecomUserProfile getProfileByQrCode(String code) {
String providerToken = wecomTokenService.getProviderToken();
WecomLoginInfoResponse loginInfo = wecomUserService.getLoginInfo(providerToken, code);
WecomUserInfo userInfo = loginInfo.getUser_info();
WecomCorpInfo corpInfo = loginInfo.getCorp_info();
if (userInfo == null || corpInfo == null) {
throw new I18nMessageException("exception/system.error");
}
WecomUserProfile profile = new WecomUserProfile();
profile.setCorpId(corpInfo.getCorpid());
profile.setUserId(userInfo.getUserid());
profile.setOpenUserId(userInfo.getOpen_userid());
profile.setName(userInfo.getName());
profile.setAvatarUrl(userInfo.getAvatar());
profile.setMobile(userInfo.getMobile());
profile.setEmail(userInfo.getEmail());
return profile;
}
private WecomUserProfile getProfileByWebPage(String code) {
String suiteToken = wecomTokenService.getSuiteToken();
WecomUserInfo3rd userInfo = wecomUserService.getUserInfo3rd(suiteToken, code);
if (StringUtils.isBlank(userInfo.getUser_ticket())) {
throw new I18nMessageException("exception/system.error");
}
WecomUserInfo3rd userDetail = wecomUserService.getUserDetail3rd(suiteToken, userInfo.getUser_ticket());
WecomUserProfile profile = new WecomUserProfile();
profile.setCorpId(resolveCorpId(userInfo, userDetail));
profile.setUserId(resolveUserId(userInfo, userDetail));
profile.setOpenUserId(resolveOpenUserId(userInfo, userDetail));
profile.setName(StringUtils.defaultIfBlank(userDetail.getName(), userInfo.getName()));
profile.setAvatarUrl(StringUtils.defaultIfBlank(userDetail.getAvatar(), userInfo.getAvatar()));
profile.setMobile(StringUtils.defaultIfBlank(userDetail.getMobile(), userInfo.getMobile()));
profile.setEmail(StringUtils.defaultIfBlank(userDetail.getEmail(), userInfo.getEmail()));
return profile;
}
private String resolveCorpId(WecomUserInfo3rd userInfo, WecomUserInfo3rd userDetail) {
return StringUtils.defaultIfBlank(
StringUtils.defaultIfBlank(userDetail.getCorpid(), userInfo.getCorpid()),
userInfo.getCorpId());
}
private String resolveUserId(WecomUserInfo3rd userInfo, WecomUserInfo3rd userDetail) {
return StringUtils.defaultIfBlank(
StringUtils.defaultIfBlank(userDetail.getUserid(), userInfo.getUserid()),
userInfo.getUserId());
}
private String resolveOpenUserId(WecomUserInfo3rd userInfo, WecomUserInfo3rd userDetail) {
return StringUtils.defaultIfBlank(
StringUtils.defaultIfBlank(userDetail.getOpen_userid(), userInfo.getOpen_userid()),
userInfo.getOpenId());
}
}
package cn.com.poc.user.wecom.service;
import cn.com.poc.common.service.RedisService;
import cn.com.poc.common.utils.StringUtils;
import cn.com.poc.user.wecom.api.WecomTokenApi;
import cn.com.poc.user.wecom.constant.WecomConstant;
import cn.com.poc.user.wecom.domain.WecomProviderAccessToken;
import cn.com.poc.user.wecom.domain.WecomSuiteAccessToken;
import cn.com.poc.user.wecom.domain.properties.WecomProperties;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class WecomTokenService {
private static final Logger logger = LoggerFactory.getLogger(WecomTokenService.class);
@Resource
private WecomTokenApi wecomTokenApi;
@Resource
private RedisService redisService;
@Resource
private WecomProperties wecomProperties;
public String getProviderToken() {
String corpId = wecomProperties.getCorpId();
String providerSecret = wecomProperties.getProviderSecret();
if (StringUtils.isBlank(corpId) || StringUtils.isBlank(providerSecret)) {
throw new I18nMessageException("exception/system.error");
}
String tokenKey = WecomConstant.RedisKey.PROVIDER_ACCESS_TOKEN + ":" + corpId;
String cachedToken = redisService.get(tokenKey, String.class);
if (StringUtils.isNotBlank(cachedToken)) {
return cachedToken;
}
WecomProviderAccessToken token = wecomTokenApi.getProviderToken(corpId, providerSecret);
if (token != null && token.isSuccess()) {
redisService.set(tokenKey, token.getProvider_access_token(), token.getExpires_in());
return token.getProvider_access_token();
}
logger.error("wecom provider token error: {}", token == null ? "null response" : token.getErrmsg());
throw new I18nMessageException("exception/system.error");
}
public String getSuiteToken() {
String suiteId = wecomProperties.getSuiteId();
String suiteSecret = wecomProperties.getSuiteSecret();
if (StringUtils.isBlank(suiteId) || StringUtils.isBlank(suiteSecret)) {
throw new I18nMessageException("exception/system.error");
}
String tokenKey = WecomConstant.RedisKey.SUITE_ACCESS_TOKEN + ":" + suiteId;
String cachedToken = redisService.get(tokenKey, String.class);
if (StringUtils.isNotBlank(cachedToken)) {
return cachedToken;
}
String ticketKey = WecomConstant.RedisKey.SUITE_TICKET + ":" + suiteId;
String suiteTicket = redisService.get(ticketKey, String.class);
if (StringUtils.isBlank(suiteTicket)) {
logger.error("wecom suite ticket missing for suiteId {}", suiteId);
throw new I18nMessageException("exception/system.error");
}
WecomSuiteAccessToken token = wecomTokenApi.getSuiteToken(suiteId, suiteSecret, suiteTicket);
if (token != null && token.isSuccess()) {
redisService.set(tokenKey, token.getSuite_access_token(), token.getExpires_in());
return token.getSuite_access_token();
}
logger.error("wecom suite token error: {}", token == null ? "null response" : token.getErrmsg());
throw new I18nMessageException("exception/system.error");
}
}
package cn.com.poc.user.wecom.service;
import cn.com.poc.user.wecom.api.WecomServiceApi;
import cn.com.poc.user.wecom.domain.WecomLoginInfoResponse;
import cn.com.poc.user.wecom.domain.WecomUserInfo3rd;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class WecomUserService {
private static final Logger logger = LoggerFactory.getLogger(WecomUserService.class);
@Resource
private WecomServiceApi wecomServiceApi;
public WecomLoginInfoResponse getLoginInfo(String providerAccessToken, String authCode) {
WecomLoginInfoResponse response = wecomServiceApi.getLoginInfo(providerAccessToken, authCode);
if (response != null && response.isSuccess()) {
return response;
}
logger.error("wecom login info error: {}", response == null ? "null response" : response.getErrmsg());
throw new I18nMessageException("exception/system.error");
}
public WecomUserInfo3rd getUserInfo3rd(String suiteAccessToken, String code) {
WecomUserInfo3rd response = wecomServiceApi.getUserInfo3rd(suiteAccessToken, code);
if (response != null && response.isSuccess()) {
return response;
}
logger.error("wecom user info error: {}", response == null ? "null response" : response.getErrmsg());
throw new I18nMessageException("exception/system.error");
}
public WecomUserInfo3rd getUserDetail3rd(String suiteAccessToken, String userTicket) {
WecomUserInfo3rd response = wecomServiceApi.getUserDetail3rd(suiteAccessToken, userTicket);
if (response != null && response.isSuccess()) {
return response;
}
logger.error("wecom user detail error: {}", response == null ? "null response" : response.getErrmsg());
throw new I18nMessageException("exception/system.error");
}
}
package cn.com.poc.user.wecom.util;
/**
* 异常描述
*/
@SuppressWarnings("serial")
public class AesException extends Exception {
public static final int OK = 0;
public static final int ValidateSignatureError = -40001;
public static final int ParseXmlError = -40002;
public static final int ComputeSignatureError = -40003;
public static final int IllegalAesKey = -40004;
public static final int ValidateCorpidError = -40005;
public static final int EncryptAESError = -40006;
public static final int DecryptAESError = -40007;
public static final int IllegalBuffer = -40008;
private int code;
public int getCode() {
return code;
}
public AesException(int code) {
super(getMessage(code));
this.code = code;
}
private static String getMessage(int code) {
switch (code) {
case ValidateSignatureError:
return "签名验证错误";
case ParseXmlError:
return "xml解析失败";
case ComputeSignatureError:
return "sha加密生成签名失败";
case IllegalAesKey:
return "SymmetricKey非法";
case ValidateCorpidError:
return "corpid校验失败";
case EncryptAESError:
return "aes加密失败";
case DecryptAESError:
return "aes解密失败";
case IllegalBuffer:
return "解密后得到的buffer非法";
default:
return "未知错误";
}
}
}
package cn.com.poc.user.wecom.util;
import java.util.ArrayList;
/**
* 用于byte[]数组不断加入byte[]数组
*/
class ByteGroup {
ArrayList<Byte> byteContainer = new ArrayList<Byte>();
public byte[] toBytes() {
byte[] bytes = new byte[byteContainer.size()];
for (int i = 0; i < byteContainer.size(); i++) {
bytes[i] = byteContainer.get(i);
}
return bytes;
}
public ByteGroup addBytes(byte[] bytes) {
for (byte b : bytes) {
byteContainer.add(b);
}
return this;
}
public int size() {
return byteContainer.size();
}
}
package cn.com.poc.user.wecom.util;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* 提供基于PKCS7算法的加解密接口
*/
class PKCS7Encoder {
static Charset CHARSET = Charset.forName("utf-8");
static int BLOCK_SIZE = 32;
/**
* 获得对明文进行补位填充的字节.
*
* @param count 需要进行填充补位操作的明文字节个数
* @return 补齐用的字节数组
*/
static byte[] encode(int count) {
// 计算需要填充的位数
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
if (amountToPad == 0) {
amountToPad = BLOCK_SIZE;
}
// 获得补位所用的字符
char padChr = chr(amountToPad);
StringBuilder tmp = new StringBuilder();
for (int index = 0; index < amountToPad; index++) {
tmp.append(padChr);
}
return tmp.toString().getBytes(CHARSET);
}
/**
* 删除解密后明文的补位字符
*
* @param decrypted 解密后的明文
* @return 删除补位字符后的明文
*/
static byte[] decode(byte[] decrypted) {
int pad = decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
static char chr(int a) {
byte target = (byte) (a & 0xFF);
return (char) target;
}
}
package cn.com.poc.user.wecom.util;
import java.security.MessageDigest;
import java.util.Arrays;
class SHA1 {
/**
* 用SHA1算法生成安全签名
*
* @param token 票据
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param encrypt 密文
* @return 安全签名
* @throws AesException
*/
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException {
try {
String[] array = new String[]{token, timestamp, nonce, encrypt};
StringBuilder sb = new StringBuilder();
Arrays.sort(array);
for (String a : array) {
sb.append(a);
}
String str = sb.toString();
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuilder hexstr = new StringBuilder();
String shaHex;
for (byte b : digest) {
shaHex = Integer.toHexString(b & 0xFF);
if (shaHex.length() < 2) {
hexstr.append(0);
}
hexstr.append(shaHex);
}
return hexstr.toString();
} catch (Exception e) {
throw new AesException(AesException.ComputeSignatureError);
}
}
}
/**
* 对企业微信发送给企业后台的消息加解密示例代码.
*
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
package cn.com.poc.user.wecom.util;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Random;
/**
* 提供接收和推送给企业微信消息的加解密接口(UTF8编码的字符串).
*/
public class WXBizMsgCrypt {
static Charset CHARSET = Charset.forName("utf-8");
Base64 base64 = new Base64();
byte[] aesKey;
String token;
String receiveid;
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 构造函数
*
* @param token 企业微信后台,开发者设置的token
* @param encodingAesKey 企业微信后台,开发者设置的EncodingAESKey
* @param receiveid 不同场景含义不同,详见文档
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
*/
public WXBizMsgCrypt(String token, String encodingAesKey, String receiveid) throws AesException {
if (encodingAesKey.length() != 43) {
throw new AesException(AesException.IllegalAesKey);
}
this.token = token;
this.receiveid = receiveid;
aesKey = Base64.decodeBase64(encodingAesKey + "=");
}
byte[] getNetworkBytesOrder(int sourceNumber) {
byte[] orderBytes = new byte[4];
orderBytes[3] = (byte) (sourceNumber & 0xFF);
orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
return orderBytes;
}
int recoverNetworkBytesOrder(byte[] orderBytes) {
int sourceNumber = 0;
for (int i = 0; i < 4; i++) {
sourceNumber <<= 8;
sourceNumber |= orderBytes[i] & 0xff;
}
return sourceNumber;
}
String getRandomStr() {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 16; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
String encrypt(String randomStr, String text) throws AesException {
ByteGroup byteCollector = new ByteGroup();
byte[] randomStrBytes = randomStr.getBytes(CHARSET);
byte[] textBytes = text.getBytes(CHARSET);
byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
byte[] receiveidBytes = receiveid.getBytes(CHARSET);
byteCollector.addBytes(randomStrBytes);
byteCollector.addBytes(networkBytesOrder);
byteCollector.addBytes(textBytes);
byteCollector.addBytes(receiveidBytes);
byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
byteCollector.addBytes(padBytes);
byte[] unencrypted = byteCollector.toBytes();
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] encrypted = cipher.doFinal(unencrypted);
return base64.encodeToString(encrypted);
} catch (Exception e) {
throw new AesException(AesException.EncryptAESError);
}
}
String decrypt(String text) throws AesException {
byte[] original;
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
byte[] encrypted = Base64.decodeBase64(text);
original = cipher.doFinal(encrypted);
} catch (Exception e) {
throw new AesException(AesException.DecryptAESError);
}
String xmlContent;
String from_receiveid;
try {
byte[] bytes = PKCS7Encoder.decode(original);
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
int xmlLength = recoverNetworkBytesOrder(networkOrder);
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
from_receiveid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),
CHARSET);
} catch (Exception e) {
throw new AesException(AesException.IllegalBuffer);
}
logger.info("XMLCONTENT:{}", xmlContent);
if (!from_receiveid.equals(receiveid)) {
logger.info("from_receiveid:{},receiveid{}", from_receiveid, receiveid);
throw new AesException(AesException.ValidateCorpidError);
}
return xmlContent;
}
/**
* 将企业微信回复用户的消息加密打包.
*/
public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {
String encrypt = encrypt(getRandomStr(), replyMsg);
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);
return XMLParse.generate(encrypt, signature, timeStamp, nonce);
}
/**
* 检验消息的真实性,并且获取解密后的明文.
*/
public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
throws AesException {
Object[] encrypt = XMLParse.extract(postData);
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());
if (!signature.equals(msgSignature)) {
throw new AesException(AesException.ValidateSignatureError);
}
return decrypt(encrypt[1].toString());
}
/**
* 验证URL
*/
public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr)
throws AesException {
String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);
if (!signature.equals(msgSignature)) {
throw new AesException(AesException.ValidateSignatureError);
}
return decrypt(echoStr);
}
}
package cn.com.poc.user.wecom.util;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
/**
* XML解析器
*/
class XMLParse {
public static Object[] extract(String xmltext) throws AesException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder db = dbf.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(xmltext.getBytes(StandardCharsets.UTF_8));
Document doc = db.parse(inputStream);
Element root = doc.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
NodeList nodelist2 = root.getElementsByTagName("ToUserName");
if (nodelist1.getLength() == 0) {
throw new AesException(AesException.ParseXmlError);
}
String encrypt = nodelist1.item(0).getTextContent();
String toUserName = nodelist2.item(0).getTextContent();
return new Object[]{toUserName, encrypt};
} catch (Exception e) {
throw new AesException(AesException.ParseXmlError);
}
}
public static String generate(String encrypt, String signature, String timestamp, String nonce) {
return "<xml>\n" +
"<Encrypt><![CDATA[" + encrypt + "]]></Encrypt>\n" +
"<MsgSignature><![CDATA[" + signature + "]]></MsgSignature>\n" +
"<TimeStamp>" + timestamp + "</TimeStamp>\n" +
"<Nonce><![CDATA[" + nonce + "]]></Nonce>\n" +
"</xml>";
}
}
......@@ -30,4 +30,12 @@ google.client.id=867985016759-n9qj00k174n9bibrtrqngvt89vbmnjrp.apps.googleuserco
google.custom.search.key=AIzaSyCV8PTQ10rG5wo4E004dR3mcGD1RM_PrBw
google.custom.search.cx=049026ecb26e840ed
wecom.corpid=ww8c108fef054eadf7
wecom.provider_secret=CFNGYoy2zByD_6Sn77JKO2spuuk6qwCNR64awuPiEbCby7sRnAnHN4XN8q8o5avB
wecom.suite_id=ww082e840d854324af
wecom.suite_secret=RkZgVTuIx6vZxpXWZDHmbIE1KZeSCPYuf9ImfLGeGIE
wecom.redirect_url=https://software-copyright-sit.gsstcloud.com/login
wecom.token=pemLJjdcbM
wecom.encoding_aes_key=eHLt8oIYfnp9E27zINRUhusKyQmvFrc64w3ccPho5HG
dify.software_copyright.callball.url=https://super-modellink-sit.gsstcloud.com/api/rest/bizSoftwareCopyrightRest/callbackGeneratedBaseDoc.json
\ No newline at end of file
......@@ -72,3 +72,11 @@ framemax-frame.job.disable=false
#pay
pay.config.acctId=16653311814572
pay.config.wxAppId=wxea3470b5d2d97eca
wecom.corpid=ww8c108fef054eadf7
wecom.provider_secret=CFNGYoy2zByD_6Sn77JKO2spuuk6qwCNR64awuPiEbCby7sRnAnHN4XN8q8o5avB
wecom.suite_id=ww082e840d854324af
wecom.suite_secret=RkZgVTuIx6vZxpXWZDHmbIE1KZeSCPYuf9ImfLGeGIE
wecom.redirect_url=https://software-copyright-sit.gsstcloud.com/login
wecom.token=pemLJjdcbM
wecom.encoding_aes_key=eHLt8oIYfnp9E27zINRUhusKyQmvFrc64w3ccPho5HG
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment