WindChat/windchat-business/src/main/java/com/windchat/im/business/impl/tai/ApiSiteService.java

490 lines
23 KiB
Java
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright 2018-2028 WindChat Group
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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<Integer, String> 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 registerget 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<String> 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<String> 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<Integer, String> 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);
}
}
}