/** * Copyright 2018-2028 WindChat Group *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.windchat.im.business.impl.tai; import com.akaxin.proto.core.ConfigProto; import com.akaxin.proto.core.CoreProto; import com.akaxin.proto.core.UserProto; import com.akaxin.proto.site.ApiSiteConfigProto; import com.akaxin.proto.site.ApiSiteLoginProto; import com.akaxin.proto.site.ApiSiteRegisterProto; import com.windchat.common.command.Command; import com.windchat.common.command.CommandResponse; import com.windchat.common.constant.CommandConst; import com.windchat.common.constant.ErrorCode2; import com.windchat.common.constant.IErrorCode; import com.windchat.common.crypto.HashCrypto; import com.windchat.common.crypto.RSACrypto; import com.windchat.common.exceptions.ZalyException2; import com.windchat.common.logs.LogUtils; import com.windchat.common.utils.StringHelper; import com.windchat.common.utils.UserIdUtils; import com.windchat.im.business.bean.ApiActions; import com.windchat.im.business.bean.PlatformPhoneBean; import com.windchat.im.business.dao.SiteConfigDao; import com.windchat.im.business.dao.SiteLoginDao; import com.windchat.im.business.dao.UserFriendDao; import com.windchat.im.business.dao.UserGroupDao; import com.windchat.im.business.dao.UserProfileDao; import com.windchat.im.business.impl.AbstractRequest; import com.windchat.im.business.impl.notice.User2Notice; import com.windchat.im.business.impl.site.PlatformUserPhone; import com.windchat.im.business.impl.site.SiteConfig; import com.windchat.im.business.impl.site.UserUic; import com.windchat.im.storage.api.IUserDeviceDao; import com.windchat.im.storage.bean.ApplyFriendBean; import com.windchat.im.storage.bean.SimpleUserBean; import com.windchat.im.storage.bean.UserDeviceBean; import com.windchat.im.storage.bean.UserProfileBean; import com.windchat.im.storage.bean.UserSessionBean; import com.windchat.im.storage.service.DeviceDaoService; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.PublicKey; import java.security.Signature; import java.util.Base64; import java.util.Map; import java.util.Set; import java.util.UUID; /** * 获取站点信息 * 执行注册&&登陆 * * @author Sam{@link an.guoyue254@gmail.com} * @since 2017-10-17 18:14:10 */ @ApiActions(action = "api.site") public class ApiSiteService extends AbstractRequest { private static final Logger logger = LoggerFactory.getLogger(ApiSiteService.class); private static final int DEFAULT_PORT = 2021; private IUserDeviceDao userDeviceDao = new DeviceDaoService(); /** * 获取站点配置信息 * * @param command * @return */ @ApiActions(action = ".config") public CommandResponse config(Command command) { CommandResponse commandResponse = new CommandResponse().setAction(CommandConst.ACTION_RES); ErrorCode2 errCode = ErrorCode2.ERROR; try { Map configMap = SiteConfig.getConfigMap(); LogUtils.requestDebugLog(logger, command, ""); if (configMap != null) { ConfigProto.SiteConfig.Builder configBuilder = ConfigProto.SiteConfig.newBuilder(); if (StringUtils.isNotBlank(configMap.get(ConfigProto.ConfigKey.SITE_ADDRESS_VALUE))) { configBuilder.setSiteAddress(configMap.get(ConfigProto.ConfigKey.SITE_ADDRESS_VALUE)); } if (StringUtils.isNumeric(configMap.get(ConfigProto.ConfigKey.SITE_PORT_VALUE))) { configBuilder.setSitePort(Integer.valueOf(configMap.get(ConfigProto.ConfigKey.SITE_PORT_VALUE))); } if (StringUtils.isNotBlank(configMap.get(ConfigProto.ConfigKey.SITE_NAME_VALUE))) { configBuilder.setSiteName(configMap.get(ConfigProto.ConfigKey.SITE_NAME_VALUE)); } if (StringUtils.isNotBlank(configMap.get(ConfigProto.ConfigKey.SITE_LOGO_VALUE))) { configBuilder.setSiteLogo(configMap.get(ConfigProto.ConfigKey.SITE_LOGO_VALUE)); } if (StringUtils.isNotBlank(configMap.get(ConfigProto.ConfigKey.SITE_VERSION_VALUE))) { configBuilder.setSiteVersion(configMap.get(ConfigProto.ConfigKey.SITE_VERSION_VALUE)); } if (StringUtils.isNotBlank(configMap.get(ConfigProto.ConfigKey.SITE_INTRODUCTION_VALUE))) { configBuilder.setSiteIntroduction(configMap.get(ConfigProto.ConfigKey.SITE_INTRODUCTION_VALUE)); } if (StringUtils.isNotBlank(configMap.get(ConfigProto.ConfigKey.PIC_SIZE_VALUE))) { configBuilder.setPicSize(configMap.get(ConfigProto.ConfigKey.PIC_SIZE_VALUE)); } if (StringUtils.isNumeric(configMap.get(ConfigProto.ConfigKey.REALNAME_STATUS_VALUE))) { configBuilder.setRealNameConfigValue( Integer.valueOf(configMap.get(ConfigProto.ConfigKey.REALNAME_STATUS_VALUE))); } if (StringUtils.isNumeric(configMap.get(ConfigProto.ConfigKey.INVITE_CODE_STATUS_VALUE))) { configBuilder.setInviteCodeConfigValue( Integer.valueOf(configMap.get(ConfigProto.ConfigKey.INVITE_CODE_STATUS_VALUE))); } ApiSiteConfigProto.ApiSiteConfigResponse response = ApiSiteConfigProto.ApiSiteConfigResponse .newBuilder().setSiteConfig(configBuilder.build()).build(); commandResponse.setParams(response.toByteArray()); errCode = ErrorCode2.SUCCESS; } } catch (Exception e) { errCode = ErrorCode2.ERROR_SYSTEMERROR; LogUtils.requestErrorLog(logger, command, e); } return commandResponse.setErrCode2(errCode); } /** * 注册站点账户 * * @param command * @return */ @ApiActions(action = ".register") public CommandResponse register(Command command) { CommandResponse commandResponse = new CommandResponse().setAction(CommandConst.ACTION_RES); IErrorCode errCode = ErrorCode2.ERROR; try { ApiSiteRegisterProto.ApiSiteRegisterRequest request = ApiSiteRegisterProto.ApiSiteRegisterRequest .parseFrom(command.getParams()); String userIdPubk = request.getUserIdPubk(); String userName = request.getUserName(); String userPhoto = request.getUserPhoto(); String userUic = request.getUserUic(); String applyInfo = request.getApplyInfo(); String phoneToken = request.getPhoneToken(); String fullPhoneId = null;// 通过phoneToken 查询的 counrtyCode:phoneId String siteUserId = UUID.randomUUID().toString();// siteUserId保证各站不同 String siteLoginId = request.getSiteLoginId();// 站点账号 LogUtils.requestDebugLog(logger, command, request.toString()); if (StringUtils.isAnyEmpty(userIdPubk, userName)) { throw new ZalyException2(ErrorCode2.ERROR_PARAMETER); } if (userName.length() > 16) { throw new ZalyException2(ErrorCode2.ERROR_PARAMETER_NICKNAME); } // 是否开启实名 ConfigProto.RealNameConfig realNameConfig = SiteConfig.getRealNameConfig(); switch (realNameConfig) { case REALNAME_YES: if (StringUtils.isNotBlank(phoneToken)) { PlatformPhoneBean bean = PlatformUserPhone.getInstance().getPhoneIdFromPlatform(phoneToken); fullPhoneId = bean.getFullPhoneId(); logger.debug("user realname register,get phoneid from platform:{}", fullPhoneId); if (StringUtils.isEmpty(fullPhoneId)) { throw new ZalyException2(ErrorCode2.ERROR_REGISTER_PHONEID); } } else { throw new ZalyException2(ErrorCode2.ERROR_REGISTER_PHONETOKEN); } break; default: break; } // 是否开启邀请码 ConfigProto.InviteCodeConfig uicConfig = SiteConfig.getUICConfig(); switch (uicConfig) { case UIC_YES: logger.debug("注册方式:邀请码注册"); if (!UserUic.getInstance().checkUic(userUic, siteUserId)) { throw new ZalyException2(ErrorCode2.ERROR_REGISTER_UIC); } break; default: break; } UserProfileBean regBean = new UserProfileBean(); regBean.setSiteUserId(siteUserId); regBean.setUserIdPubk(userIdPubk); regBean.setUserName(userName); regBean.setUserNameInLatin(StringHelper.toLatinPinYin(userName)); regBean.setApplyInfo(applyInfo); regBean.setUserPhoto(userPhoto); regBean.setPhoneId(fullPhoneId); regBean.setUserStatus(UserProto.UserStatus.NORMAL_VALUE); regBean.setRegisterTime(System.currentTimeMillis()); if (SiteLoginDao.getInstance().registerUser(regBean)) { ApiSiteRegisterProto.ApiSiteRegisterResponse response = ApiSiteRegisterProto.ApiSiteRegisterResponse .newBuilder().setSiteUserId(siteUserId).build(); commandResponse.setParams(response.toByteArray()); errCode = ErrorCode2.SUCCESS; } else { errCode = ErrorCode2.ERROR_REGISTER_USERID_UNIQUE; } if (ErrorCode2.SUCCESS == errCode) { addUserDefaultFriendsAndGroups(siteUserId); // 注册成功,需要做一个管理员身份验证 justForAdminUser(siteUserId, command.getHeader()); } } catch (Exception e) { errCode = ErrorCode2.ERROR_SYSTEMERROR; LogUtils.requestErrorLog(logger, command, e); } catch (ZalyException2 e) { errCode = e.getErrCode(); LogUtils.requestErrorLog(logger, command, e); } return commandResponse.setErrCode(errCode); } // 增加默认好友以及群组 private void addUserDefaultFriendsAndGroups(String siteUserId) { try { Set defaultFriends = SiteConfig.getUserDefaultFriends(); if (defaultFriends != null && defaultFriends.size() > 0) { for (String siteFriendId : defaultFriends) { UserFriendDao.getInstance().saveFriendApply(siteFriendId, siteUserId, "我已成为你的好友,给我发消息试试吧"); UserFriendDao.getInstance().agreeApply(siteUserId, siteFriendId, true); ApplyFriendBean applyBean = UserFriendDao.getInstance().agreeApplyWithClear(siteUserId, siteFriendId, true); new User2Notice().addFriendTextMessage(applyBean); } } logger.debug("{} add default friends={}", siteUserId, defaultFriends); } catch (Exception e) { logger.error("add default friends error", e); } try { Set defaultGroups = SiteConfig.getUserDefaultGroups(); if (defaultGroups != null && defaultGroups.size() > 0) { for (String groupId : defaultGroups) { UserGroupDao.getInstance().addDefaultGroupMember(groupId, siteUserId); } } logger.debug("{} add default groups={}", siteUserId, defaultGroups); } catch (Exception e) { logger.error("add user default groups error", e); } } private void justForAdminUser(String siteUserId, Map header) { try { // 如果站点没有管理员 if (SiteConfig.hasNoAdminUser()) { logger.debug("user first time to register site server ,set it as admin:{} map:{}", siteUserId, header); SiteConfigDao.getInstance().updateSiteConfig(ConfigProto.ConfigKey.SITE_ADMIN_VALUE, siteUserId); if (header != null) { String host = header.get(CoreProto.HeaderKey.CLIENT_REQUEST_SERVER_HOST_VALUE); if (StringUtils.isNotEmpty(host)) { SiteConfigDao.getInstance().updateSiteConfig(ConfigProto.ConfigKey.SITE_ADDRESS_VALUE, host); SiteConfigDao.getInstance().updateSiteConfig(ConfigProto.ConfigKey.SITE_NAME_VALUE, host); } String port = header.get(CoreProto.HeaderKey.CLIENT_REQUEST_SERVER_HOST_VALUE); if (StringUtils.isNotBlank(port)) { port = "" + DEFAULT_PORT; SiteConfigDao.getInstance().updateSiteConfig(ConfigProto.ConfigKey.SITE_PORT_VALUE, port); } // 修改邀请码注册方式 SiteConfigDao.getInstance().updateSiteConfig(ConfigProto.ConfigKey.INVITE_CODE_STATUS_VALUE, ConfigProto.InviteCodeConfig.UIC_NO_VALUE + ""); } SiteConfig.updateConfig(); } } catch (Exception e) { logger.error("set site admin error", e); } } /** * 执行用户登录站点行为 * * @param command * @return */ @ApiActions(action = ".login") public CommandResponse login(Command command) { CommandResponse commandResponse = new CommandResponse().setAction(CommandConst.ACTION_RES); IErrorCode errCode = ErrorCode2.ERROR; try { ApiSiteLoginProto.ApiSiteLoginRequest loginRequest = ApiSiteLoginProto.ApiSiteLoginRequest .parseFrom(command.getParams()); String userIdPubk = loginRequest.getUserIdPubk(); String userIdSignBase64 = loginRequest.getUserIdSignBase64(); String userDeviceIdPubk = loginRequest.getUserDeviceIdPubk(); String userDeviceIdSignBase64 = loginRequest.getUserDeviceIdSignBase64(); String userDeviceName = loginRequest.getUserDeviceName(); String userToken = loginRequest.getUserToken(); String phoneToken = loginRequest.getPhoneToken(); LogUtils.requestDebugLog(logger, command, loginRequest.toString()); if (StringUtils.isAnyEmpty(userIdPubk, userIdSignBase64)) { throw new ZalyException2(ErrorCode2.ERROR2_LOGGIN_USERID_EMPTY); } if (StringUtils.isAnyEmpty(userDeviceIdPubk, userDeviceIdSignBase64)) { throw new ZalyException2(ErrorCode2.ERROR2_LOGGIN_DEVICEID_EMPTY); } // if (StringUtils.isEmpty(userToken)) { // throw new ZalyException2(ErrorCode2.ERROR2_LOGGIN_USERTOKEN_EMPTY); // } PublicKey userPubKey = RSACrypto.getRSAPubKeyFromPem(userIdPubk);// 个人身份公钥,解密Sign签名,解密Key Signature userSign = Signature.getInstance("SHA512withRSA"); userSign.initVerify(userPubKey); userSign.update(userIdPubk.getBytes());// 原文 boolean userSignResult = userSign.verify(Base64.getDecoder().decode(userIdSignBase64)); logger.debug("userSignResult={}", userSignResult); if (userSignResult) { Signature userDeviceSign = Signature.getInstance("SHA512withRSA"); userDeviceSign.initVerify(userPubKey); userDeviceSign.update(userDeviceIdPubk.getBytes());// 原文 userSignResult = userDeviceSign.verify(Base64.getDecoder().decode(userDeviceIdSignBase64)); } logger.debug("deviceSignResult={}", userSignResult); // 用户身份校验成功,方可执行登陆操作 if (!userSignResult) { throw new ZalyException2(ErrorCode2.ERROR2_LOGGIN_ERRORSIGN); } // 登陆去平台实名机制校验 String globalUserId = verifyPlatformPhoneAndGetGlobalUserId(userIdPubk, phoneToken); // 判断用户,是否已经注册,从主库中查找 SimpleUserBean subean = UserProfileDao.getInstance().getSimpleProfileByGlobalUserId(globalUserId, true); if (subean == null || StringUtils.isEmpty(subean.getUserId())) { logger.info("login site: new user need to register before login site"); errCode = ErrorCode2.ERROR2_LOGGIN_NOREGISTER;// 未注册,告知用户执行注册行为 return commandResponse.setErrCode(errCode); } if (subean.getUserStatus() == UserProto.UserStatus.SEALUP_VALUE) { logger.info("login site: user no permision as sealed up"); errCode = ErrorCode2.ERROR2_LOGGIN_SEALUPUSER;// 禁封用户禁止登陆 return commandResponse.setErrCode(errCode); } String siteUserId = subean.getUserId(); String deviceId = HashCrypto.MD5(userDeviceIdPubk); // 保存设备信息 UserDeviceBean deviceBean = new UserDeviceBean(); deviceBean.setDeviceId(deviceId); deviceBean.setDeviceName(userDeviceName); deviceBean.setSiteUserId(siteUserId); deviceBean.setUserDevicePubk(userDeviceIdPubk); deviceBean.setUserToken(userToken); deviceBean.setActiveTime(System.currentTimeMillis()); deviceBean.setAddTime(System.currentTimeMillis()); boolean loginResult = SiteLoginDao.getInstance().updateUserDevice(deviceBean); if (!loginResult) {// 更新失败,则重新保存数据 loginResult = SiteLoginDao.getInstance().saveUserDevice(deviceBean); // 在新增设备情况下,控制设备数量 limitUserDevice(siteUserId); } logger.debug("login site: save device result={} deviceBean={}", loginResult, deviceBean.toString()); if (!loginResult) { // 身份校验失败 throw new ZalyException2(ErrorCode2.ERROR2_LOGGIN_UPDATE_DEVICE); } // 生成session String sessionId = UUID.randomUUID().toString(); UserSessionBean sessionBean = new UserSessionBean(); sessionBean.setLoginTime(System.currentTimeMillis()); sessionBean.setSiteUserId(siteUserId); sessionBean.setOnline(true); sessionBean.setSessionId(sessionId); sessionBean.setDeviceId(deviceId); sessionBean.setLoginTime(System.currentTimeMillis());// 上次登陆(auth)时间 loginResult = loginResult && SiteLoginDao.getInstance().saveUserSession(sessionBean); if (loginResult) { ApiSiteLoginProto.ApiSiteLoginResponse response = ApiSiteLoginProto.ApiSiteLoginResponse.newBuilder() .setSiteUserId(siteUserId).setUserSessionId(sessionId).build(); commandResponse.setParams(response.toByteArray()); errCode = ErrorCode2.SUCCESS; } else { errCode = ErrorCode2.ERROR2_LOGGIN_UPDATE_SESSION; } } catch (ZalyException2 e) { errCode = e.getErrCode(); LogUtils.requestErrorLog(logger, command, e); } catch (Exception e) { errCode = ErrorCode2.ERROR_SYSTEMERROR; LogUtils.requestErrorLog(logger, command, e); } return commandResponse.setErrCode(errCode); } private String verifyPlatformPhoneAndGetGlobalUserId(String userIdPubk, String phoneToken) throws ZalyException2 { phoneToken = null; if (StringUtils.isEmpty(phoneToken)) { logger.debug("api.site.login with phoneToken={}", phoneToken); return UserIdUtils.getV1GlobalUserId(userIdPubk); } // 登陆去平台校验实名机制 ConfigProto.RealNameConfig realNameConfig = SiteConfig.getRealNameConfig(); if (ConfigProto.RealNameConfig.REALNAME_YES == realNameConfig) { PlatformPhoneBean bean = PlatformUserPhone.getInstance().getPhoneIdFromPlatform(phoneToken); String fullPhoneId = bean.getFullPhoneId(); String platformUserIdPubk = bean.getUserIdPubk(); logger.debug("get platform realname phone info bean={}", bean); if (StringUtils.isEmpty(fullPhoneId)) { return UserIdUtils.getV1GlobalUserId(userIdPubk); } if (!userIdPubk.equals(platformUserIdPubk)) { logger.error("api.site.login equals={} userIdPubk={} platformUserIdPubk={}", false, userIdPubk, platformUserIdPubk); return UserIdUtils.getV1GlobalUserId(userIdPubk); } // 手机号查询用户身份 UserProfileBean profile = UserProfileDao.getInstance().getUserProfileByFullPhoneId(fullPhoneId); if (profile != null && StringUtils.isNoneEmpty(platformUserIdPubk, profile.getUserIdPubk())) { if (platformUserIdPubk.equals(profile.getUserIdPubk())) { logger.debug("new site realname login verifyPlatformPhone success"); return UserIdUtils.getV1GlobalUserId(platformUserIdPubk); } else { // 更新数据 String globalUserId = UserIdUtils.getV1GlobalUserId(platformUserIdPubk); boolean updateRes = UserProfileDao.getInstance().updateUserIdPubk(profile.getSiteUserId(), globalUserId, userIdPubk); if (!updateRes) { throw new ZalyException2(ErrorCode2.ERROR2_LOGGIN_UPDATENEWPUBK); } } } } return UserIdUtils.getV1GlobalUserId(userIdPubk); } // 控制用户的设备数量 private void limitUserDevice(String siteUserId) { try { int limitNumber = 4; userDeviceDao.limitDeviceNum(siteUserId, limitNumber); } catch (Exception e) { logger.error(StringHelper.format("limit siteUserId={} device num error", siteUserId), e); } } }