PageRenderTime 7652ms CodeModel.GetById 41ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/com/linbox/im/server/connector/tcp/handler/AuthHandler.java

https://gitlab.com/Mr.Tomato/linbox_server
Java | 264 lines | 203 code | 56 blank | 5 comment | 21 complexity | b0a1feb74021439305cc871e7ac30868 MD5 | raw file
  1. package com.linbox.im.server.connector.tcp.handler;
  2. import com.alibaba.fastjson.JSON;
  3. import com.linbox.im.message.*;
  4. import com.linbox.im.server.connector.tcp.constant.HandlerName;
  5. import com.linbox.im.server.service.IOutboxService;
  6. import com.linbox.im.server.storage.dao.IServerDAO;
  7. import com.linbox.im.server.storage.dao.IUserDAO;
  8. import io.netty.channel.Channel;
  9. import io.netty.channel.ChannelFuture;
  10. import io.netty.channel.ChannelHandlerContext;
  11. import io.netty.channel.ChannelInboundHandlerAdapter;
  12. import io.netty.util.concurrent.Future;
  13. import io.netty.util.concurrent.GenericFutureListener;
  14. import org.apache.commons.lang.StringUtils;
  15. import org.slf4j.Logger;
  16. import org.slf4j.LoggerFactory;
  17. import org.springframework.context.support.ClassPathXmlApplicationContext;
  18. import java.net.InetSocketAddress;
  19. import java.util.Base64;
  20. import java.util.concurrent.ScheduledExecutorService;
  21. import java.util.concurrent.ScheduledFuture;
  22. import java.util.concurrent.TimeUnit;
  23. /**
  24. * Created by lrsec on 6/28/15.
  25. */
  26. public class AuthHandler extends ChannelInboundHandlerAdapter {
  27. private static Logger logger = LoggerFactory.getLogger(AuthHandler.class);
  28. private ScheduledExecutorService executor;
  29. private int loopRatio = 500;
  30. private int maxHandleTimeInMills = 5 * 1000;
  31. private ScheduledFuture task;
  32. private long rId = -1;
  33. private String userId = null;
  34. private AES aes = null;
  35. private Base64.Encoder base64Encoder = null;
  36. private IOutboxService outboxService;
  37. private IUserDAO userDAO;
  38. private IServerDAO serverDAO;
  39. public AuthHandler(ClassPathXmlApplicationContext applicationContext, ScheduledExecutorService executor, int loopRatio, int maxHandleTimeInMills, AES aes) {
  40. this.executor = executor;
  41. this.loopRatio = loopRatio;
  42. this.maxHandleTimeInMills = maxHandleTimeInMills;
  43. this.aes = aes;
  44. this.outboxService = (IOutboxService)applicationContext.getBean("outboxService");
  45. this.userDAO = (IUserDAO) applicationContext.getBean("userDAO");
  46. this.serverDAO = (IServerDAO) applicationContext.getBean("serverDAO");
  47. this.base64Encoder = Base64.getEncoder();
  48. }
  49. @Override
  50. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  51. logger.error("Exception in AuthHandler. Close the connection for user: " + StringUtils.trimToEmpty(userId), cause);
  52. if (rId >= 0) {
  53. sendFailResponse(ctx, 500, cause.getMessage());
  54. }
  55. ctx.close();
  56. }
  57. @Override
  58. public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
  59. logger.debug("Received AuthRequest. Message: {}", JSON.toJSONString(msg));
  60. MessageWrapper wrapper = (MessageWrapper) msg;
  61. if (wrapper == null) {
  62. logger.error("Get an null message wrapper in message handler");
  63. ctx.close();
  64. return;
  65. }
  66. RequestResponseType type = wrapper.type;
  67. if (type == null || type != RequestResponseType.AuthRequestMsg) {
  68. logger.error("The first message from client: {} is not AuthRequest. Close the connection. Message type is {}", ctx.channel().remoteAddress(), msg.getClass().getName());
  69. ctx.close();
  70. return;
  71. }
  72. AuthRequest authRequest = (AuthRequest) wrapper.content;
  73. rId = authRequest.rId;
  74. boolean authenticated = isCertified(authRequest);
  75. if (authenticated) {
  76. logger.debug("User {} authenticated.", userId);
  77. InetSocketAddress address = (InetSocketAddress)ctx.channel().localAddress();
  78. String addressRecord = address.toString() + Long.toString(System.currentTimeMillis());
  79. serverDAO.registerConnection(userId, addressRecord);
  80. logger.debug("Create connection for user: {}. Remote address: {}.", userId, ctx.channel().remoteAddress());
  81. //TODO 动态密码的生成策略,在测试时关闭
  82. // resetPassword(ctx);
  83. task = executor.scheduleAtFixedRate(new SendMessageChecker(ctx.channel(), addressRecord), 0, loopRatio, TimeUnit.MILLISECONDS);
  84. IMMessageHandler imMsgHandler = (IMMessageHandler) ctx.pipeline().get(HandlerName.HANDLER_MSG);
  85. imMsgHandler.setUserId(userId);
  86. IMIdleStateHandler imIdleStateHandler = (IMIdleStateHandler) ctx.pipeline().get(HandlerName.HANDLER_IDLE);
  87. imIdleStateHandler.setUserId(userId);
  88. ctx.pipeline().remove(this);
  89. sendSuccessResponse(ctx);
  90. return;
  91. } else {
  92. logger.error("User {} is not certified. Close the connection.", userId);
  93. sendFailResponse(ctx, 401, "User is not certified");
  94. ctx.close();
  95. return;
  96. }
  97. }
  98. private boolean isCertified(AuthRequest request) {
  99. userId = request.userId;
  100. return userDAO.isUserValid(request.userId, request.token);
  101. }
  102. private void resetPassword(final ChannelHandlerContext ctx) {
  103. String password = serverDAO.getPassword(Long.parseLong(userId));
  104. if (StringUtils.isBlank(password)) {
  105. logger.error("Can not get im password for user: {}", userId);
  106. sendFailResponse(ctx, 400, "Can not find user im password");
  107. return;
  108. } else {
  109. aes.resetPassword(password);
  110. }
  111. }
  112. private class SendMessageChecker implements Runnable {
  113. private Logger logger = LoggerFactory.getLogger(SendMessageChecker.class);
  114. private volatile boolean isConnectionClosed = false;
  115. private Channel ch;
  116. private String linkRecord;
  117. public SendMessageChecker(Channel ch, String linkRecord) {
  118. this.ch = ch;
  119. this.linkRecord = linkRecord;
  120. }
  121. public void run() {
  122. long start = System.currentTimeMillis();
  123. while ((System.currentTimeMillis() - start <= maxHandleTimeInMills)) {
  124. try {
  125. if(shouldClose()) {
  126. terminate();
  127. return;
  128. }
  129. final String msg = outboxService.get(userId);
  130. if (StringUtils.isBlank(msg)) {
  131. return;
  132. }
  133. final MessageWrapper wrapper = JSON.parseObject(msg, MessageWrapper.class);
  134. ChannelFuture future = ch.writeAndFlush(wrapper);
  135. logger.debug("Tcp sender send message for {}. Message type: {}. Message body: {}", userId, wrapper.type, msg);
  136. future.addListener(new GenericFutureListener() {
  137. public void operationComplete(Future future) throws Exception {
  138. if (!future.isSuccess()) {
  139. logger.info("Network I/O write fail. Should close connection for user {}.", userId);
  140. isConnectionClosed = true;
  141. Throwable t = future.cause();
  142. if (t != null) {
  143. logger.info("Send message fail", t);
  144. }
  145. } else {
  146. logger.debug("Tcp sender send message success. user: {}. Message type: {}. Message body: {}", userId, wrapper.type, msg);
  147. }
  148. }
  149. });
  150. } catch (Exception e) {
  151. logger.error("Exception in sending task for user: " + userId + ". But the sending loop will continue to work", e);
  152. return;
  153. }
  154. }
  155. }
  156. private boolean shouldClose() {
  157. if (!ch.isActive()) {
  158. logger.warn("Connection for user {} is inactive, should terminate the sending task.", userId);
  159. return true;
  160. }
  161. if(isConnectionClosed) {
  162. logger.warn("Connection for user {} is closed, should terminate the sending task.", userId);
  163. return true;
  164. }
  165. String record = serverDAO.getConnection(userId);
  166. if(!StringUtils.equals(record, linkRecord)) {
  167. logger.warn("Connection is updated, should terminate the sending task for user {}. Local address {}. New connection address: {}", userId, StringUtils.trimToEmpty(linkRecord), StringUtils.trimToEmpty(record));
  168. sendOfflineInfo();
  169. return true;
  170. }
  171. return false;
  172. }
  173. private void terminate() {
  174. logger.info("Terminate sending task for user {}", userId);
  175. task.cancel(false);
  176. try {
  177. ch.close();
  178. } catch (Exception e) {
  179. logger.error("Exception when terminate sending task", e);
  180. }
  181. }
  182. private void sendOfflineInfo() {
  183. OfflineInfo offlineInfo = new OfflineInfo();
  184. offlineInfo.userId = userId;
  185. MessageWrapper wrapper = offlineInfo.toWrapper();
  186. ch.writeAndFlush(wrapper);
  187. }
  188. }
  189. private void sendSuccessResponse(ChannelHandlerContext ctx) {
  190. AuthResponse response = new AuthResponse();
  191. response.rId = rId;
  192. response.userId = userId;
  193. response.status = 200;
  194. response.sendTime = System.currentTimeMillis();
  195. MessageWrapper responseWrapper = response.toWrapper();
  196. ctx.channel().writeAndFlush(responseWrapper);
  197. }
  198. private void sendFailResponse(ChannelHandlerContext ctx, int status, String errCode) {
  199. AuthResponse response = new AuthResponse();
  200. response.rId = rId;
  201. response.userId = userId;
  202. response.status = status;
  203. response.errMsg = errCode;
  204. response.sendTime = System.currentTimeMillis();
  205. MessageWrapper responseWrapper = response.toWrapper();
  206. ctx.channel().writeAndFlush(responseWrapper);
  207. }
  208. }