Correct practices for relationship binding between Channel and users of project system Netty and security scenarios for Channel channel

Preface

Consider a functional business that communicates in real time to a specified user in a web program

In the Socket communication functions used by the Web (e.g. online customer service), to ensure peer-to-peer communication, this seemingly simple channel will actually encounter a lot of problems based on the user finding the channel

  1. The Http protocol in web programs is stateless
  2. socket services and web projects are deployed independently in general projects
  3. socket connections are reconnected, and Channel objects are different each time
  4. Channel is network card bound and cannot be serialized

Solution

By managing a thread-safe user identity (such as a user primary key) and a map chain of corresponding channel s

    private final ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();

So here comes the question...,

  • How do I get this user ID in the netty module?
  • How can the netty socket module safely identify a channel as belonging to a user? (This can be solved in the same way as above)
  • Did the netty socket module receive a message and any other proof that the channel is trustworthy?

There is no authentication or HttpSession in the netty implementation, that is, there is no user who can get a web project login in the netty program thread.

For this reason, refer to the session sharing scheme of the web project cluster. You can save login information in caches such as Redis.

  1. After logging in to a web project, save a token in redis in this key named user id.
  2. Immediately after the client socket channel is established, send a user ID and ASK to the socket server.
  3. The server calculates a token and redis comparison based on ASK. Once the comparison is successful, it binds the relationship between the current channel and the user.
  4. The server then detects whether the current channel has bound user information each time it receives a message

This key is one-time. It's important to think about it. In your foreground project, the user may be offline because the cookie expires or the background has automatically taken the user offline, and your user ID and ASK are exposed. Then the message may be sent by a malicious connection.

It is also negligible that authentication transports such as token and ASK only identify and bind the user's relationship with channels. As long as the user's login status is saved in redis, the first communication established by the channel transfers the current browser's login user identity, and then compares in redis. However, this key in redis is still good for one time.Avoid one user establishing multiple socket channels

Correct binding Channel channel to user relationship

If we only have one ConcurrentHashMap <String, Channel>, it is impossible to quickly and elegantly determine which user the current channel belongs to; I see that the vast majority of other implementations are managed by creating a channelId and user-identified Map

    //key is the long ID of channel, channel.id(). asLongText(); value is the user ID
    private final ConcurrentHashMap<String, String> channelAndUserMap = new ConcurrentHashMap<>();

In fact, this is not the most reasonable way to do this. The correct way to do this is to use AttributeMap provided by Channel object to save the information accompanying this channel. Many people do not know that Channel object provides a Map that binds custom data.
Use examples

    //User id=>channel example
    private final ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();


 /**
     * Determine if a channel is in use by a user
     * Determine if the channel is legitimate when information can be forwarded
     * @param channel
     * @return
     */
    public boolean hasUser(Channel channel) {
        AttributeKey<String> key = AttributeKey.valueOf("user");
        return (channel.hasAttr(key) || channel.attr(key).get() != null);//netty removed the remove method for this map, so be careful here
    }

    /**
     * Online a user
     *
     * @param channel
     * @param userId
     */
    public void online(Channel channel, String userId) {
        //First determine if the user is logged in to the web system?
        //This part of the code is a personal implementation, referring to the validation in redis above
        
            this.channelMap.put(userId, channel);
            AttributeKey<String> key = AttributeKey.valueOf("user");
            channel.attr(key).set(userId);
        

    }

    /**
     * Get the user's channel based on the user id
     *
     * @param userId
     * @return
     */
    public Channel getChannelByUserId(String userId) {
        return this.channelMap.get(userId);
    }

    /**
     * Determine whether a user is online
     *
     * @param userId
     * @return
     */
    public Boolean online(String userId) {
        return this.channelMap.containsKey(userId) && this.channelMap.get(userId) != null;
    }

Be careful!!

Many people use channel.id().asShortText() to record identity channel, which is wrong!!!!! Short ID does not guarantee global uniqueness!!

Tags: Java socket Redis Netty network

Posted on Tue, 10 Mar 2020 09:55:26 -0700 by EPCtech