├── src ├── main │ ├── resources │ │ ├── static │ │ │ ├── img │ │ │ │ └── FastBee_Logo.png │ │ │ ├── index.html │ │ │ ├── chatPage2.html │ │ │ ├── chatPage.html │ │ │ └── js │ │ │ │ ├── chatPage.js │ │ │ │ └── chatPage2.js │ │ ├── banner.txt │ │ └── application.yml │ └── java │ │ └── com │ │ └── fastbee │ │ └── fastbeeim │ │ ├── utils │ │ ├── UserUtils.java │ │ ├── RespBean.java │ │ ├── FBApplicationsUtils.java │ │ └── SnowFlake.java │ │ ├── dto │ │ ├── LoginDTO.java │ │ └── FBApplicationDTO.java │ │ ├── pojo │ │ ├── TextMessage.java │ │ ├── FBApplication.java │ │ ├── Message.java │ │ └── User.java │ │ ├── mapper │ │ ├── UserMapper.java │ │ └── FBApplicationMapper.java │ │ ├── FastBeeIMApplication.java │ │ ├── service │ │ ├── IUserService.java │ │ ├── IFBApplicationService.java │ │ └── impl │ │ │ ├── UserServiceImpl.java │ │ │ └── FBApplicationServiceImpl.java │ │ ├── config │ │ ├── WSServerConfig.java │ │ ├── InterceptorConfig.java │ │ ├── MybatisPlusConfig.java │ │ ├── RedisConfig.java │ │ └── SwaggerConfig.java │ │ ├── common │ │ ├── GetInformationFromJWT.java │ │ ├── JWTInterceptor.java │ │ └── FSIMWebSocketServer.java │ │ └── controller │ │ ├── ApplicationController.java │ │ ├── UserController.java │ │ ├── WebSocketController.java │ │ └── LoginController.java └── test │ └── java │ └── com │ └── fastbee │ └── fastbeeim │ └── FastBeeImApplicationTests.java ├── .gitignore ├── README.md ├── LICENSE ├── .github └── workflows │ └── maven-publish.yml └── pom.xml /src/main/resources/static/img/FastBee_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Art0white/FastBeeIM/HEAD/src/main/resources/static/img/FastBee_Logo.png -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/utils/UserUtils.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.utils; 2 | 3 | import com.fastbee.fastbeeim.pojo.User; 4 | 5 | /** 6 | * User工具类 7 | * @author Lovsog 8 | */ 9 | public class UserUtils { 10 | } -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | ______ ______ __ ____ 3 | / \ / / ____/___ ______/ /_ / __ )___ ___ 4 | _____/_____/ / /_ / __ `/ ___/ __/ / __ / _ \/ _ \ 5 | / / __/ / /_/ (__ ) /_ / /_/ / __/ __/ 6 | ___/ /_/ \__,_/____/\__/ /_____/\___/\___/ 7 | -------------------------------------------------------------------------------- /src/test/java/com/fastbee/fastbeeim/FastBeeImApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class FastBeeImApplicationTests { 8 | @Test 9 | void contextLoads() { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/dto/LoginDTO.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.dto; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | public class LoginDTO { 11 | @NotBlank(message = "用户名不能为空") 12 | private String username; 13 | @NotBlank(message = "密码不能为空") 14 | private String password; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/dto/FBApplicationDTO.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.dto; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | public class FBApplicationDTO { 11 | @NotBlank(message = "AppId不能为空") 12 | private Integer appId; 13 | @NotBlank(message = "AppName不能为空") 14 | private String appName; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/pojo/TextMessage.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.pojo; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.experimental.Accessors; 6 | 7 | /** 8 | * 消息类 9 | * @author Lovsog 10 | */ 11 | @Data 12 | @EqualsAndHashCode(callSuper = false) 13 | @Accessors(chain = true) 14 | public class TextMessage extends Message { 15 | private String content; 16 | private String fromNickName; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.fastbee.fastbeeim.pojo.User; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * UserMapper 11 | * @author Lovsog 12 | */ 13 | public interface UserMapper extends BaseMapper { 14 | List getAllUsers(@Param("id") Integer id, @Param("keywords") String keywords); 15 | } -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FastBeeIM 6 | 7 | 8 |

FastBeeIM

9 |

building.....

10 |

11 | 陕ICP备2022012680号-1 14 |

15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | ### application ### 36 | application-db.yml 37 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/pojo/FBApplication.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | @EqualsAndHashCode(callSuper = false) 10 | public class FBApplication { 11 | 12 | @TableId(value = "app_id", type = IdType.AUTO) 13 | private Integer appId; 14 | 15 | private String appName; 16 | 17 | private String appKey; 18 | 19 | private String appSecret; 20 | 21 | private boolean enabled; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/pojo/Message.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.pojo; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.experimental.Accessors; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Data 10 | @EqualsAndHashCode(callSuper = false) 11 | @Accessors(chain = true) 12 | public class Message { 13 | private Integer from; 14 | private Integer to; 15 | private LocalDateTime date; 16 | private int messageType; 17 | /** 18 | * 1 19 | */ 20 | private int sendMessageType; 21 | /** 22 | * 1 P2PChat 23 | * 2 GroupChat 24 | */ 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/FastBeeIMApplication.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 7 | 8 | 9 | /** 10 | * SpringBoot启动类 11 | * @author Lovsog 12 | */ 13 | @EnableSwagger2 14 | @SpringBootApplication 15 | public class FastBeeIMApplication { 16 | public static void main(String[] args) { 17 | SpringApplication.run(FastBeeIMApplication.class, args); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/mapper/FBApplicationMapper.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.fastbee.fastbeeim.pojo.FBApplication; 5 | import org.apache.ibatis.annotations.Select; 6 | 7 | /** 8 | * FBApplicationMapper 9 | * @author Lovsog 10 | */ 11 | 12 | public interface FBApplicationMapper extends BaseMapper { 13 | @Select("SELECT\n" + 14 | " AUTO_INCREMENT\n" + 15 | "FROM\n" + 16 | " INFORMATION_SCHEMA.TABLES\n" + 17 | "WHERE\n" + 18 | " TABLE_NAME = 'f_b_application'") 19 | Integer getNextAutoIncrementOfApp(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.fastbee.fastbeeim.utils.RespBean; 5 | import com.fastbee.fastbeeim.pojo.User; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import java.util.List; 9 | 10 | /** 11 | * UserService接口 12 | * @author Lovsog 13 | */ 14 | public interface IUserService extends IService { 15 | RespBean login(String username, String password, HttpServletRequest request); 16 | 17 | User getUserByUserName(String username); 18 | 19 | User getUserByUserId(Integer userId); 20 | 21 | String getPasswordByUserName(String username); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/config/WSServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.socket.server.standard.ServerEndpointExporter; 6 | 7 | /** 8 | * WebSocket 服务端配置类 9 | * @author Lovsog 10 | */ 11 | @Configuration 12 | public class WSServerConfig { 13 | 14 | /** 15 | * ServerEndpointExporter bean 注入 16 | */ 17 | @Bean 18 | public ServerEndpointExporter serverEndpointExporter() { 19 | ServerEndpointExporter serverEndpointExporter = new ServerEndpointExporter(); 20 | return serverEndpointExporter; 21 | } 22 | } 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/service/IFBApplicationService.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.fastbee.fastbeeim.dto.FBApplicationDTO; 5 | import com.fastbee.fastbeeim.pojo.FBApplication; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | public interface IFBApplicationService extends IService { 10 | 11 | FBApplication getFBApplicationByAppId(Integer appId); 12 | 13 | FBApplication getFBApplicationByAppName(String appName); 14 | 15 | String getAppKey(FBApplication application); 16 | 17 | String getAppSecret(FBApplication application); 18 | 19 | FBApplication createApp(String appName, String secret); 20 | 21 | Integer getAppIdByAppKey(HttpServletRequest request); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/config/InterceptorConfig.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.config; 2 | 3 | import com.fastbee.fastbeeim.common.JWTInterceptor; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | /** 9 | * JWT 拦截器配置类 10 | * @author Lovsog 11 | */ 12 | 13 | @Configuration 14 | public class InterceptorConfig implements WebMvcConfigurer { 15 | @Override 16 | public void addInterceptors(InterceptorRegistry registry) { 17 | registry.addInterceptor(new JWTInterceptor()) 18 | .addPathPatterns("/**") 19 | .excludePathPatterns( 20 | "/api/app/createApp" 21 | ); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/common/GetInformationFromJWT.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.common; 2 | 3 | import com.auth0.jwt.interfaces.DecodedJWT; 4 | import com.fastbee.fastbeeim.utils.FBApplicationsUtils; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | @Component 10 | public class GetInformationFromJWT { 11 | 12 | public Integer getAppIdByAppKey(HttpServletRequest request){ 13 | String appKey = request.getHeader("appKey"); 14 | String appSecret = request.getHeader("appSecret"); 15 | DecodedJWT verify = null; 16 | try { 17 | verify = FBApplicationsUtils.verify(appKey, appSecret); 18 | }catch (Exception e) { 19 | System.out.println(e); 20 | } 21 | return Integer.parseInt(verify.getClaim("appId").asString()); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/pojo/User.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 用户类 12 | * @author Lovsog 13 | */ 14 | @Data 15 | @EqualsAndHashCode(callSuper = false) 16 | public class User implements Serializable { 17 | private static final long serialVersionUID = 1L; 18 | 19 | @TableId(value = "user_id", type = IdType.AUTO) 20 | private Integer userId; 21 | 22 | private String name; 23 | 24 | private String phone; 25 | 26 | private String telephone; 27 | 28 | private String address; 29 | 30 | private Boolean enabled; 31 | 32 | private String username; 33 | 34 | private String password; 35 | 36 | private String userFace; 37 | 38 | private String remark; 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastBeeIM 2 |

3 | FastBee_Logo 4 |

5 | 6 | spring-boot 7 | 8 | 9 | mybatis-plus 10 | 11 |

12 |

13 |

极蜜IM, 快速尝到甜头!

14 | 提供给开发者快速实现IM服务端、客户端的能力, 达到简单配置、快速部署、高效接入的效果! 15 |
16 |

🚗当前进度

17 |
    18 |
  • 通过appKey来分别不同应用
  • 19 |
  • 实现了P2P RESTful API
  • 20 |
  • 实现了群发 RESTful API
  • 21 |
22 |
23 |

🚀接下来的计划

24 |
    25 |
  • 设计实现用户、好友、群组等逻辑
  • 26 |
  • 设计封装js SDK
  • 27 |
  • 设计封装java SDK
  • 28 |
29 |
30 |
31 |
32 | 33 | > 每天完成一小步, 积累Up Up! 34 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/utils/RespBean.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.utils; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * 公共返回对象 9 | * 10 | * @author Lovsog 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class RespBean { 16 | private long code; //状态码 17 | private String message; //提示信息 18 | private Object obj; //返回对象 19 | 20 | public static RespBean success(String message) { 21 | return new RespBean(200, message,null); 22 | } 23 | 24 | public static RespBean success(String message, Object obj) { 25 | return new RespBean(200, message, obj); 26 | } 27 | 28 | public static RespBean error(String message) { 29 | return new RespBean(404, message, null); 30 | } 31 | 32 | public static RespBean error(String message, Object obj) { 33 | return new RespBean(404, message, obj); 34 | } 35 | 36 | public static RespBean JWTError(String message, Object obj) { 37 | return new RespBean(505, message, obj); 38 | } 39 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lovsog 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: FastBee DeBug 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set up JDK 8 21 | uses: actions/setup-java@v3 22 | with: 23 | java-version: '8' 24 | distribution: 'temurin' 25 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 26 | settings-path: ${{ github.workspace }} # location for the settings.xml file 27 | 28 | - name: Build with Maven 29 | run: mvn -B package --file pom.xml 30 | 31 | - name: Publish to GitHub Packages Apache Maven 32 | run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml 33 | env: 34 | GITHUB_TOKEN: ${{ github.token }} 35 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/controller/ApplicationController.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.controller; 2 | import com.fastbee.fastbeeim.pojo.FBApplication; 3 | import com.fastbee.fastbeeim.service.impl.FBApplicationServiceImpl; 4 | import com.fastbee.fastbeeim.utils.RespBean; 5 | import io.swagger.annotations.Api; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * 应用相关API 14 | * @author Lovsog 15 | */ 16 | 17 | @Api(tags = "应用相关API") 18 | @RestController 19 | @RequestMapping("/api/app") 20 | public class ApplicationController { 21 | @Resource 22 | private FBApplicationServiceImpl fbApplicationService; 23 | 24 | @PostMapping("/createApp") 25 | public RespBean creatApplication(String appName) { 26 | String mySecret = System.currentTimeMillis() + ""; //当前时间戳 27 | FBApplication fbApplication = fbApplicationService.createApp(appName, mySecret); 28 | return new RespBean(200, "创建应用成功", fbApplication); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.config; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize; 5 | import org.mybatis.spring.annotation.MapperScan; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * Mybatis-Plus 配置类 11 | * @author Lovsog 12 | */ 13 | @Configuration 14 | @MapperScan("com.fastbee.fastbeeim.mapper") 15 | public class MybatisPlusConfig { 16 | @Bean 17 | public PaginationInterceptor paginationInterceptor() { 18 | PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); 19 | // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false 20 | // paginationInterceptor.setOverflow(false); 21 | // 设置最大单页限制数量,默认 500 条,-1 不受限制 22 | paginationInterceptor.setLimit(500); 23 | // 开启 count 的 join 优化,只针对部分 left join 24 | paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); 25 | return paginationInterceptor; 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/utils/FBApplicationsUtils.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.utils; 2 | import com.auth0.jwt.JWT; 3 | import com.auth0.jwt.JWTCreator; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import com.auth0.jwt.interfaces.DecodedJWT; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Date; 9 | import java.util.Map; 10 | 11 | 12 | @Component 13 | public class FBApplicationsUtils { 14 | /** 15 | * 生成 AppKey 16 | */ 17 | public static String createAppKey(Map map, String appSecret) { 18 | Date date = new Date(System.currentTimeMillis() + 86400 * 7 * 1000); // 86400L * 365 * 60 * 1000 19 | Algorithm algorithm = Algorithm.HMAC256(appSecret); 20 | //创建jwt builder 21 | JWTCreator.Builder builder = JWT.create(); 22 | //payload 23 | map.forEach((k, v) -> { 24 | builder.withClaim(k, v); 25 | }); 26 | // 附带username信息 27 | return builder 28 | //到期时间 29 | .withExpiresAt(date) 30 | //创建一个新的JWT,并使用给定的算法进行标记 31 | .sign(algorithm); 32 | } 33 | 34 | /** 35 | * 校验 AppKey 是否正确 36 | */ 37 | public static DecodedJWT verify(String appKey, String appSecret) { 38 | return JWT.require(Algorithm.HMAC256(appSecret)).build().verify(appKey); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; 8 | import org.springframework.data.redis.serializer.StringRedisSerializer; 9 | 10 | /** 11 | * Redis 配置类 12 | * @author Lovsog 13 | */ 14 | 15 | @Configuration 16 | public class RedisConfig { 17 | @Bean 18 | public RedisTemplate redisTemplate(LettuceConnectionFactory redisConnectionFactory){ 19 | RedisTemplate redisTemplate = new RedisTemplate<>(); 20 | //为string类型key设置序列器 21 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 22 | //为string类型value设置序列器 23 | redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); 24 | //为hash类型key设置序列器 25 | redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 26 | //为hash类型value设置序列器 27 | redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); 28 | redisTemplate.setConnectionFactory(redisConnectionFactory); 29 | return redisTemplate; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.controller; 2 | 3 | import com.fastbee.fastbeeim.pojo.User; 4 | import com.fastbee.fastbeeim.service.IUserService; 5 | import com.fastbee.fastbeeim.utils.RespBean; 6 | import io.swagger.annotations.Api; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * 获取信息相关API 15 | * @author Lovsog 16 | */ 17 | 18 | @Api(tags = "获取消息相关API") 19 | @RestController 20 | @RequestMapping("/api/info") 21 | public class UserController { 22 | @Resource 23 | private IUserService userService; 24 | 25 | @PostMapping("/getUserByUserName") 26 | public RespBean getUserByUserName(String username) { 27 | User user = userService.getUserByUserName(username); 28 | if(user == null) { 29 | return RespBean.error("不存在用户 username==" + username); 30 | } 31 | return RespBean.success("存在用户 username==" + username, user); 32 | } 33 | 34 | @PostMapping("/getUserByUserId") 35 | public RespBean getUserByUserId(Integer userId) { 36 | User user = userService.getUserByUserId(userId); 37 | if(user == null) { 38 | return RespBean.error("不存在用户 id==" + userId); 39 | } 40 | return RespBean.success("存在用户 id==" + userId, user); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.PathSelectors; 7 | import springfox.documentation.builders.RequestHandlerSelectors; 8 | import springfox.documentation.service.ApiInfo; 9 | import springfox.documentation.service.Contact; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Swagger2 配置类 19 | * http://localhost:9292/swagger-ui.html 20 | * @author Lovsog 21 | */ 22 | @Configuration 23 | @EnableSwagger2 24 | public class SwaggerConfig { 25 | @Bean 26 | public Docket createAPI() { 27 | return new Docket(DocumentationType.SWAGGER_2) 28 | .apiInfo(apiInfo()) 29 | .select() 30 | //指定哪个包下面生成接口文档 31 | .apis(RequestHandlerSelectors.basePackage("com.fastbee.fastbeeim.controller")) 32 | .paths(PathSelectors.any()) 33 | .build(); 34 | } 35 | 36 | /** 37 | * 文档基本信息 38 | */ 39 | private ApiInfo apiInfo() { 40 | return new ApiInfoBuilder() 41 | .version("1.0") 42 | .title("极蜜IM接口文档") 43 | .description("极蜜IM接口文档") 44 | .contact(new Contact("Lovsog", "localhost:9292/doc.html", "lovsog@163.com")) 45 | .build(); 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import com.fastbee.fastbeeim.common.GetInformationFromJWT; 6 | import com.fastbee.fastbeeim.mapper.UserMapper; 7 | import com.fastbee.fastbeeim.utils.RespBean; 8 | import com.fastbee.fastbeeim.pojo.User; 9 | import com.fastbee.fastbeeim.service.IUserService; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.data.redis.core.ValueOperations; 12 | import org.springframework.stereotype.Service; 13 | 14 | import javax.annotation.Resource; 15 | import javax.servlet.http.HttpServletRequest; 16 | import java.util.Objects; 17 | 18 | /** 19 | * UserService实现 20 | * @author Lovsog 21 | */ 22 | @Service 23 | public class UserServiceImpl extends ServiceImpl implements IUserService { 24 | 25 | @Resource 26 | private UserMapper userMapper; 27 | 28 | @Override 29 | public RespBean login(String username, String password, HttpServletRequest request) { 30 | return null; 31 | } 32 | 33 | @Override 34 | public User getUserByUserName(String username) { 35 | return userMapper.selectOne(new QueryWrapper().eq("username", username).eq("enabled", true)); 36 | } 37 | 38 | @Override 39 | public User getUserByUserId(Integer userId) { 40 | return userMapper.selectOne(new QueryWrapper().eq("user_id", userId).eq("enabled", true)); 41 | } 42 | 43 | @Override 44 | public String getPasswordByUserName(String username) { 45 | User user = userMapper.selectOne(new QueryWrapper().eq("username", username).eq("enabled", true)); 46 | return user.getPassword(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/common/JWTInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.common; 2 | 3 | import com.auth0.jwt.exceptions.AlgorithmMismatchException; 4 | import com.auth0.jwt.exceptions.SignatureVerificationException; 5 | import com.auth0.jwt.exceptions.TokenExpiredException; 6 | import com.fastbee.fastbeeim.utils.FBApplicationsUtils; 7 | import com.fastbee.fastbeeim.utils.RespBean; 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.web.servlet.HandlerInterceptor; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | 15 | @Component 16 | public class JWTInterceptor implements HandlerInterceptor { 17 | @Override 18 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 19 | //获取请求头中令牌 20 | RespBean respBean = null; 21 | String appKey = request.getHeader("appKey"); 22 | String appSecret = request.getHeader("appSecret"); 23 | try { 24 | FBApplicationsUtils.verify(appKey, appSecret); 25 | return true; 26 | }catch (SignatureVerificationException e){ 27 | respBean = RespBean.JWTError("无效签名", false); 28 | }catch (TokenExpiredException e){ 29 | respBean = RespBean.JWTError("appKey过期", false); 30 | }catch (AlgorithmMismatchException e){ 31 | respBean = RespBean.JWTError("appKey算法不一致", false); 32 | }catch (Exception e){ 33 | respBean = RespBean.JWTError("appKey无效", false); 34 | } 35 | 36 | String json = new ObjectMapper().writeValueAsString(respBean); 37 | response.setContentType("application/json;charset=UTF-8"); 38 | response.getWriter().println(json); 39 | return false; 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/controller/WebSocketController.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.controller; 2 | 3 | import com.fastbee.fastbeeim.common.FSIMWebSocketServer; 4 | import com.fastbee.fastbeeim.utils.RespBean; 5 | import io.swagger.annotations.Api; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | 12 | /** 13 | * WebSocket相关 14 | * @author Lovsog 15 | */ 16 | @Api(tags = "发送消息相关API") 17 | @Controller 18 | @RequestMapping("/api/msg") 19 | public class WebSocketController { 20 | @Autowired 21 | private FSIMWebSocketServer websocketServer; 22 | 23 | @ResponseBody 24 | @PostMapping(value = "/sendP2PMessage") 25 | public RespBean sendP2PMessage(String content, 26 | Integer from, 27 | String fromNick, 28 | Integer to, 29 | int messageType) { 30 | int res = websocketServer.sendP2PMessage(content, from, fromNick, to, messageType, 1); 31 | if(res == 0) { 32 | return RespBean.error("不存在该接收方"); 33 | } 34 | return RespBean.success("发送P2P消息成功", content); 35 | } 36 | 37 | @ResponseBody 38 | @PostMapping(value = "/sendGroupMessage") 39 | public RespBean sendGroupMessage(String content, 40 | Integer from, 41 | String fromNick, 42 | int messageType) { 43 | websocketServer.sendGroupMessage(content, from, fromNick, messageType, 2); 44 | return RespBean.success("群发消息成功",content); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/resources/static/chatPage2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | websocket 7 | 8 | 9 | 38 | 39 | 40 | 41 |
42 | 发送给: 43 | 44 | 45 | 46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 |

54 | 陕ICP备2022012680号-1 57 |

58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/resources/static/chatPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | websocket 7 | 8 | 9 | 38 | 39 | 40 | 41 |
42 | 发送给: 43 | 44 | 45 | 46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 |

58 | 陕ICP备2022012680号-1 61 |

62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.controller; 2 | 3 | import com.fastbee.fastbeeim.dto.LoginDTO; 4 | import com.fastbee.fastbeeim.pojo.User; 5 | import com.fastbee.fastbeeim.service.impl.UserServiceImpl; 6 | import com.fastbee.fastbeeim.utils.RespBean; 7 | import io.swagger.annotations.Api; 8 | import org.springframework.util.ObjectUtils; 9 | import org.springframework.validation.annotation.Validated; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.annotation.Resource; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | 19 | /** 20 | * 服务端不需要,之后移到客户端SDK 21 | */ 22 | 23 | 24 | //@Api(tags = "用户登录相关API") 25 | //@RestController 26 | //@Validated 27 | //@CrossOrigin 28 | //public class LoginController { 29 | // @Resource 30 | // private UserServiceImpl userService; 31 | // 32 | // @PostMapping("/login") 33 | // public RespBean login(LoginDTO loginDTO, HttpServletResponse response) { 34 | //// String salt = userService.getUserByUserName(loginDTO.getUsername()); 35 | //// String password = new SimpleHash("MD5", loginDTO.getPassword(), salt, 1).toHex(); 36 | // String password = loginDTO.getPassword(); 37 | // String realPassword = userService.getPasswordByUserName(loginDTO.getUsername()); 38 | // if(ObjectUtils.isEmpty(realPassword)){ 39 | // return new RespBean(400, "用户名错误",0); 40 | // } else if (!realPassword.equals(password)) { 41 | // return new RespBean(400, "密码错误", 0); 42 | // } else { 43 | // User user = userService.getUserByUserName(loginDTO.getUsername()); 44 | // Map tokenMap = new HashMap<>(); 45 | // tokenMap.put("token", userService.getToken(user)); 46 | // return new RespBean(200,"success", tokenMap); 47 | // } 48 | // } 49 | // 50 | // @PostMapping("/mytest") 51 | // public RespBean test(HttpServletRequest request) { 52 | // String username = userService.getUsernameByJWT(request); 53 | // return new RespBean(200,"success", username); 54 | // } 55 | //} -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9292 3 | 4 | spring: 5 | profiles: 6 | include: db 7 | datasource: 8 | type: com.alibaba.druid.pool.DruidDataSource 9 | driver-class-name: com.mysql.jdbc.Driver 10 | druid: 11 | # 数据库访问配置, 使用druid数据源 12 | # 连接池配置 13 | initial-size: 5 14 | min-idle: 5 15 | max-active: 20 16 | # 连接等待超时时间 17 | max-wait: 30000 18 | # 配置检测可以关闭的空闲连接间隔时间 19 | time-between-eviction-runs-millis: 60000 20 | # 配置连接在池中的最小生存时间 21 | min-evictable-idle-time-millis: 300000 22 | test-while-idle: true 23 | test-on-borrow: false 24 | test-on-return: false 25 | # 打开PSCache,并且指定每个连接上PSCache的大小 26 | pool-prepared-statements: true 27 | max-open-prepared-statements: 20 28 | max-pool-prepared-statement-per-connection-size: 20 29 | # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙 30 | filters: stat,wall 31 | # Spring监控AOP切入点,如x.y.z.service.*,配置多个英文逗号分隔 32 | aop-patterns: com.fastbee.fastbeeim.service.* 33 | 34 | # WebStatFilter配置 35 | web-stat-filter: 36 | enabled: true 37 | # 添加过滤规则 38 | url-pattern: /* 39 | # 忽略过滤的格式 40 | exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' 41 | 42 | # StatViewServlet配置 43 | stat-view-servlet: 44 | enabled: true 45 | # 访问路径为/druid时,跳转到StatViewServlet 46 | url-pattern: /druid/* 47 | # 是否能够重置数据 48 | reset-enable: false 49 | # 需要账号密码才能访问控制台 50 | login-username: druid 51 | login-password: druid 52 | # IP白名单 53 | # allow: 127.0.0.1 54 | # IP黑名单(共同存在时,deny优先于allow) 55 | # deny: 192.168.1.218 56 | redis: 57 | lettuce: 58 | pool: 59 | # 连接池最大连接数(使用负值表示没有限制) 60 | max-active: 1024 61 | # 连接池最大阻塞等待时间(使用负值表示没有限制) 62 | max-wait: 10000ms 63 | # 连接池中的最大空闲连接 64 | max-idle: 200 65 | # 连接池中的最小空闲连接 66 | min-idle: 5 67 | # 连接超时时间(毫秒) 68 | timeout: 10000 69 | 70 | mybatis-plus: 71 | mapper-locations: mapper/*.xml 72 | type-aliases-package: com.fastbee.fastbeeim.pojo 73 | 74 | logging: 75 | level: 76 | com: 77 | springboot: 78 | mapper: debug 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/resources/static/js/chatPage.js: -------------------------------------------------------------------------------- 1 | function getQueryString(name) { 2 | const url_string = window.location.href; // window.location.href 3 | const url = new URL(url_string); 4 | return url.searchParams.get(name); 5 | } 6 | 7 | var webSocket; 8 | var appid = 11; 9 | var username = getQueryString("username"); 10 | alert("我的,名字叫 " + username); 11 | var userTo = document.getElementById('userTo').value; 12 | if ("WebSocket" in window) 13 | { 14 | webSocket = new WebSocket("ws://localhost:9292/ws/" + appid + "/" + username); 15 | 16 | //连通之后的回调事件 17 | webSocket.onopen = function() 18 | { 19 | //webSocket.send( document.getElementById('username').value+"已经上线了"); 20 | console.log("已经连通了websocket"); 21 | setMessageInnerHTML("已经连通了websocket"); 22 | }; 23 | 24 | //接收后台服务端的消息 25 | webSocket.onmessage = function (evt) 26 | { 27 | var received_msg = evt.data; 28 | console.log("数据已接收:" + received_msg); 29 | var obj = JSON.parse(received_msg).message; 30 | console.log("可以解析成json:"+obj.messageType); 31 | /** 32 | * 1: 文本消息 33 | */ 34 | if(obj.messageType==1){ 35 | var divMsg = ""+obj.from+":
"+obj.content+"" 36 | $("#message").append(divMsg); 37 | } 38 | }; 39 | 40 | //连接关闭的回调事件 41 | webSocket.onclose = function() 42 | { 43 | console.log("连接已关闭..."); 44 | setMessageInnerHTML("连接已经关闭...."); 45 | }; 46 | } 47 | 48 | else{ 49 | // 浏览器不支持 WebSocket 50 | alert("您的浏览器不支持 WebSocket!"); 51 | } 52 | //将消息显示在网页上 53 | function setMessageInnerHTML(innerHTML) { 54 | document.getElementById('userInfo').innerHTML += innerHTML + '
'; 55 | } 56 | 57 | function closeWebSocket() { 58 | //直接关闭websocket的连接 59 | webSocket.close(); 60 | } 61 | 62 | function send(){ 63 | var userTo = document.getElementById('userTo').value; 64 | var userFrom = username 65 | var divMsg = ""+userFrom+":
"+$("#text").val()+"" 66 | $("#message").append(divMsg); 67 | var message = { 68 | "content":document.getElementById('text').value, 69 | "from":userFrom, 70 | "to":userTo, 71 | "messageType":1, 72 | "sendMessageType":1, 73 | }; 74 | webSocket.send(JSON.stringify(message)); 75 | $("#text").val(""); 76 | } 77 | window.onload = function() { 78 | var userTo = document.getElementById('userTo').value; 79 | var li = "
  • "+userTo+"

  • "; 80 | $("#onLineUser").append(li); 81 | } 82 | -------------------------------------------------------------------------------- /src/main/resources/static/js/chatPage2.js: -------------------------------------------------------------------------------- 1 | function getQueryString(name) { 2 | const url_string = window.location.href; // window.location.href 3 | const url = new URL(url_string); 4 | return url.searchParams.get(name); 5 | } 6 | 7 | var webSocket; 8 | var appid = 12; 9 | var username = getQueryString("username"); 10 | alert("我的,名字叫 " + username); 11 | var userTo = document.getElementById('userTo').value; 12 | if ("WebSocket" in window) 13 | { 14 | webSocket = new WebSocket("ws://localhost:9292/ws/" + appid + "/" + username); 15 | 16 | //连通之后的回调事件 17 | webSocket.onopen = function() 18 | { 19 | //webSocket.send( document.getElementById('username').value+"已经上线了"); 20 | console.log("已经连通了websocket"); 21 | setMessageInnerHTML("已经连通了websocket"); 22 | }; 23 | 24 | //接收后台服务端的消息 25 | webSocket.onmessage = function (evt) 26 | { 27 | var received_msg = evt.data; 28 | console.log("数据已接收:" + received_msg); 29 | var obj = JSON.parse(received_msg).message; 30 | console.log("可以解析成json:"+obj.messageType); 31 | /** 32 | * 1: 文本消息 33 | */ 34 | if(obj.messageType==1){ 35 | var divMsg = ""+obj.from+":
    "+obj.content+"" 36 | $("#message").append(divMsg); 37 | } 38 | }; 39 | 40 | //连接关闭的回调事件 41 | webSocket.onclose = function() 42 | { 43 | console.log("连接已关闭..."); 44 | setMessageInnerHTML("连接已经关闭...."); 45 | }; 46 | } 47 | 48 | else{ 49 | // 浏览器不支持 WebSocket 50 | alert("您的浏览器不支持 WebSocket!"); 51 | } 52 | //将消息显示在网页上 53 | function setMessageInnerHTML(innerHTML) { 54 | document.getElementById('userInfo').innerHTML += innerHTML + '
    '; 55 | } 56 | 57 | function closeWebSocket() { 58 | //直接关闭websocket的连接 59 | webSocket.close(); 60 | } 61 | 62 | function send(){ 63 | var userTo = document.getElementById('userTo').value; 64 | var userFrom = username 65 | var divMsg = ""+userFrom+":
    "+$("#text").val()+"" 66 | $("#message").append(divMsg); 67 | var message = { 68 | "content":document.getElementById('text').value, 69 | "from":userFrom, 70 | "to":userTo, 71 | "messageType":1, 72 | "sendMessageType":1, 73 | }; 74 | webSocket.send(JSON.stringify(message)); 75 | $("#text").val(""); 76 | } 77 | window.onload = function() { 78 | var userTo = document.getElementById('userTo').value; 79 | var li = "
  • "+userTo+"

  • "; 80 | $("#onLineUser").append(li); 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/service/impl/FBApplicationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import com.fastbee.fastbeeim.common.GetInformationFromJWT; 6 | import com.fastbee.fastbeeim.mapper.FBApplicationMapper; 7 | import com.fastbee.fastbeeim.pojo.FBApplication; 8 | import com.fastbee.fastbeeim.service.IFBApplicationService; 9 | import com.fastbee.fastbeeim.utils.FBApplicationsUtils; 10 | import org.springframework.data.redis.core.ValueOperations; 11 | import org.springframework.stereotype.Service; 12 | 13 | import javax.annotation.Resource; 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | @Service 19 | public class FBApplicationServiceImpl extends ServiceImpl implements IFBApplicationService { 20 | 21 | @Resource 22 | private FBApplicationMapper fbApplicationMapper; 23 | 24 | @Resource 25 | private GetInformationFromJWT getInformationFromJWT; 26 | 27 | @Override 28 | public FBApplication getFBApplicationByAppId(Integer appId) { 29 | return fbApplicationMapper.selectOne(new QueryWrapper().eq("app_id", appId).eq("enabled", true)); 30 | } 31 | 32 | @Override 33 | public FBApplication getFBApplicationByAppName(String appName) { 34 | return fbApplicationMapper.selectOne(new QueryWrapper().eq("app_name", appName).eq("enabled", true)); 35 | } 36 | 37 | @Override 38 | public String getAppKey(FBApplication application) { 39 | return null; 40 | } 41 | 42 | @Override 43 | public String getAppSecret(FBApplication application) { 44 | return null; 45 | } 46 | 47 | @Override 48 | public FBApplication createApp(String appName, String secret) { 49 | FBApplication fbApplication = new FBApplication(); 50 | Integer appId = fbApplicationMapper.getNextAutoIncrementOfApp(); 51 | 52 | Map payload = new HashMap<>(); 53 | payload.put("appId", appId+""); 54 | payload.put("appName", appName); 55 | String appKey = FBApplicationsUtils.createAppKey(payload, secret); 56 | 57 | fbApplication.setAppId(appId); 58 | fbApplication.setAppName(appName); 59 | fbApplication.setAppKey(appKey); 60 | fbApplication.setAppSecret(secret); 61 | fbApplication.setEnabled(true); 62 | 63 | try { 64 | fbApplicationMapper.insert(fbApplication); 65 | }catch (Exception e) { 66 | System.out.println("insert: " + e); 67 | } 68 | 69 | return fbApplication; 70 | } 71 | 72 | @Override 73 | public Integer getAppIdByAppKey(HttpServletRequest request) { 74 | return this.getInformationFromJWT.getAppIdByAppKey(request); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/utils/SnowFlake.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.utils; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.text.ParseException; 6 | 7 | /** 8 | * Twitter的分布式自增ID雪花算法 9 | */ 10 | @Component 11 | public class SnowFlake { 12 | 13 | /** 14 | * 起始的时间戳 15 | */ 16 | private final static long START_STMP = 1622505600000L; // 2021-06-01 08:00:00 17 | 18 | /** 19 | * 每一部分占用的位数 20 | */ 21 | private final static long SEQUENCE_BIT = 12; //序列号占用的位数 22 | private final static long MACHINE_BIT = 5; //机器标识占用的位数 23 | private final static long DATACENTER_BIT = 5;//数据中心占用的位数 24 | 25 | /** 26 | * 每一部分的最大值 27 | */ 28 | private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); 29 | private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); 30 | private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); 31 | 32 | /** 33 | * 每一部分向左的位移 34 | */ 35 | private final static long MACHINE_LEFT = SEQUENCE_BIT; 36 | private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; 37 | private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; 38 | 39 | private long datacenterId = 1; //数据中心 40 | private long machineId = 1; //机器标识 41 | private long sequence = 0L; //序列号 42 | private long lastStmp = -1L;//上一次时间戳 43 | 44 | public SnowFlake() { 45 | } 46 | 47 | public SnowFlake(long datacenterId, long machineId) { 48 | if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { 49 | throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); 50 | } 51 | if (machineId > MAX_MACHINE_NUM || machineId < 0) { 52 | throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); 53 | } 54 | this.datacenterId = datacenterId; 55 | this.machineId = machineId; 56 | } 57 | 58 | /** 59 | * 产生下一个ID 60 | * 61 | * @return 62 | */ 63 | public synchronized long nextId() { 64 | //取定义的时间戳 65 | long currStmp = getNewstmp(); 66 | if (currStmp < lastStmp) { 67 | throw new RuntimeException("Clock moved backwards. Refusing to generate id"); 68 | } 69 | 70 | if (currStmp == lastStmp) { 71 | //相同毫秒内,序列号自增 72 | sequence = (sequence + 1) & MAX_SEQUENCE; 73 | //同一毫秒的序列数已经达到最大 74 | if (sequence == 0L) { 75 | currStmp = getNextMill(); 76 | } 77 | } else { 78 | //不同毫秒内,序列号置为0 79 | sequence = 0L; 80 | } 81 | 82 | lastStmp = currStmp; 83 | 84 | return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 85 | | datacenterId << DATACENTER_LEFT //数据中心部分 86 | | machineId << MACHINE_LEFT //机器标识部分 87 | | sequence; //序列号部分 88 | } 89 | 90 | private long getNextMill() { 91 | long mill = getNewstmp(); 92 | while (mill <= lastStmp) { 93 | mill = getNewstmp(); 94 | } 95 | return mill; 96 | } 97 | 98 | private long getNewstmp() { 99 | return System.currentTimeMillis(); 100 | } 101 | 102 | public static void main(String[] args) throws ParseException { 103 | // 时间戳 104 | // System.out.println(System.currentTimeMillis()); 105 | // System.out.println(new Date().getTime()); 106 | // 107 | // String dateTime = "2021-06-01 08:00:00"; 108 | // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); 109 | // System.out.println(sdf.parse(dateTime).getTime()); 110 | 111 | /** 112 | * 数据中心和机器中心不能写死,后期还需要写一个配置类或者通过redis缓存读取数据 113 | */ 114 | SnowFlake snowFlake = new SnowFlake(1, 1); 115 | long start = System.currentTimeMillis(); 116 | for (int i = 0; i < 10; i++) { 117 | System.out.println(snowFlake.nextId()); 118 | System.out.println(System.currentTimeMillis() - start); 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.0.0 10 | 11 | 12 | 13 | com.fastbee 14 | FastBeeIM 15 | 0.0.1-SNAPSHOT 16 | jar 17 | 18 | FastBeeIM 19 | FastBeeIM 20 | 21 | 22 | 1.8 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-websocket 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-thymeleaf 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-validation 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-jdbc 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-test 53 | test 54 | 55 | 56 | 57 | 58 | org.projectlombok 59 | lombok 60 | 61 | 62 | 63 | 64 | mysql 65 | mysql-connector-java 66 | runtime 67 | 68 | 69 | 70 | 71 | com.alibaba 72 | druid-spring-boot-starter 73 | 1.2.14 74 | 75 | 76 | 77 | 78 | com.baomidou 79 | mybatis-plus 80 | 3.3.1 81 | 82 | 83 | com.baomidou 84 | mybatis-plus-boot-starter 85 | 3.3.1 86 | 87 | 88 | 89 | 90 | org.java-websocket 91 | Java-WebSocket 92 | 1.5.0 93 | 94 | 95 | 96 | 97 | com.alibaba 98 | fastjson 99 | 2.0.17 100 | 101 | 102 | 103 | 104 | com.auth0 105 | java-jwt 106 | 3.4.1 107 | 108 | 109 | 110 | 111 | io.springfox 112 | springfox-swagger2 113 | 2.6.1 114 | 115 | 116 | io.springfox 117 | springfox-swagger-ui 118 | 2.6.1 119 | 120 | 121 | 122 | 123 | org.springframework.boot 124 | spring-boot-starter-data-redis 125 | 126 | 127 | org.apache.commons 128 | commons-pool2 129 | 130 | 131 | io.lettuce 132 | lettuce-core 133 | 6.1.8.RELEASE 134 | 135 | 136 | 137 | 138 | 139 | 140 | org.springframework.boot 141 | spring-boot-maven-plugin 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/main/java/com/fastbee/fastbeeim/common/FSIMWebSocketServer.java: -------------------------------------------------------------------------------- 1 | package com.fastbee.fastbeeim.common; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.fastbee.fastbeeim.pojo.TextMessage; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Component; 9 | 10 | import javax.websocket.*; 11 | import javax.websocket.server.PathParam; 12 | import javax.websocket.server.ServerEndpoint; 13 | import java.io.IOException; 14 | import java.time.LocalDateTime; 15 | import java.util.Map; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | 18 | @Component 19 | @ServerEndpoint("/ws/{appId}/{clientId}") 20 | public class FSIMWebSocketServer { 21 | /** 22 | * 日志 23 | */ 24 | private Logger logger = LoggerFactory.getLogger(FSIMWebSocketServer.class); 25 | 26 | /** 27 | * 在线数 28 | */ 29 | private static int onlineCount = 0; 30 | 31 | /** 32 | * 线程安全的存储连接session的Map 33 | */ 34 | private static Map clients = new ConcurrentHashMap(); 35 | 36 | /** 37 | * session 38 | */ 39 | private Session session; 40 | 41 | /** 42 | * 客户端端标识 43 | */ 44 | private String clientId; 45 | 46 | /** 47 | * 客户端连接时方法 48 | * @param clientId 49 | * @throws IOException 50 | */ 51 | @OnOpen 52 | public void onOpen(@PathParam("clientId") String clientId, Session session) { 53 | logger.info("onOpen: has new client connect -" + clientId); 54 | this.clientId = clientId; 55 | this.session = session; 56 | addOnlineCount(); 57 | clients.put(clientId, this); 58 | logger.info("onOpen: now has " + onlineCount + " client online"); 59 | } 60 | 61 | /** 62 | * 客户端断开连接时方法 63 | * @throws IOException 64 | */ 65 | @OnClose 66 | public void onClose() { 67 | logger.info("onClose: has new client close connection -" + clientId); 68 | clients.remove(clientId); 69 | subOnlineCount(); 70 | logger.info("onClose: now has " + onlineCount + " client online"); 71 | } 72 | 73 | /** 74 | * 收到消息时 75 | * @param message 76 | * @throws IOException 77 | */ 78 | @OnMessage 79 | public void onMessage(String message) { 80 | logger.info("onMessage: [clientId: " + clientId + " ,message:" + message + "]"); 81 | JSONObject jsonObject = JSON.parseObject(message); 82 | 83 | int smType = (int) jsonObject.get("sendMessageType"); 84 | int mType = (int) jsonObject.get("messageType"); 85 | if(smType == 1) { 86 | if(mType == 1) { 87 | sendP2PMessage((String) jsonObject.get("content"), 88 | Integer.valueOf((String) jsonObject.get("from")), 89 | "lmx", 90 | Integer.valueOf((String) jsonObject.get("to")), 91 | smType, 92 | mType); 93 | } 94 | } else if (smType == 2) { 95 | if(mType == 1) { 96 | sendGroupMessage((String) jsonObject.get("content"), 97 | Integer.valueOf((String) jsonObject.get("from")), 98 | "lmx", 99 | smType, 100 | mType); 101 | } 102 | } 103 | } 104 | 105 | /** 106 | * 发生error时 107 | * @param error 108 | */ 109 | @OnError 110 | public void onError(Throwable error) { 111 | logger.info("onError: [clientId: " + clientId + " ,error:" + error.getCause() + "]"); 112 | } 113 | 114 | /** 115 | * P2P发送 116 | * @param content 117 | * @param from 118 | * @param fromNick 119 | * @param to 120 | * @throws IOException 121 | */ 122 | public int sendP2PMessage(String content, 123 | Integer from, 124 | String fromNick, 125 | Integer to, 126 | int messageType, 127 | int sendMessageType) { 128 | JSONObject json = new JSONObject(); 129 | TextMessage textMessage = new TextMessage(); 130 | textMessage.setFrom(from); 131 | textMessage.setFromNickName(fromNick); 132 | textMessage.setTo(to); 133 | textMessage.setContent(content); 134 | textMessage.setDate(LocalDateTime.now()); 135 | textMessage.setMessageType(messageType); 136 | textMessage.setSendMessageType(sendMessageType); 137 | json.put("message", textMessage); 138 | 139 | int i = 0; 140 | for (FSIMWebSocketServer item : clients.values()) { 141 | if (item.clientId.equals(to.toString())) { 142 | i++; 143 | System.out.println(json.toJSONString()); 144 | item.session.getAsyncRemote().sendText(json.toJSONString()); 145 | break; 146 | } 147 | } 148 | return i; 149 | } 150 | 151 | /** 152 | * 群发 153 | * @param content 154 | * @param from 155 | * @param fromNick 156 | */ 157 | public void sendGroupMessage(String content, 158 | Integer from, 159 | String fromNick, 160 | int messageType, 161 | int sendMessageType) { 162 | JSONObject json = new JSONObject(); 163 | TextMessage textMessage = new TextMessage(); 164 | textMessage.setFrom(from); 165 | textMessage.setFromNickName(fromNick); 166 | textMessage.setContent(content); 167 | textMessage.setDate(LocalDateTime.now()); 168 | textMessage.setMessageType(messageType); 169 | textMessage.setSendMessageType(sendMessageType); 170 | 171 | json.put("message", textMessage); 172 | int i = 0; 173 | for (FSIMWebSocketServer item : clients.values()) { 174 | if(item.clientId.equals(from.toString())) { 175 | continue; 176 | } 177 | item.session.getAsyncRemote().sendText(json.toJSONString()); 178 | } 179 | } 180 | 181 | /** 182 | * 获取在线总数 183 | */ 184 | public static synchronized int getOnlineCount() { 185 | return onlineCount; 186 | } 187 | 188 | public static synchronized void addOnlineCount() { 189 | FSIMWebSocketServer.onlineCount++; 190 | } 191 | 192 | public static synchronized void subOnlineCount() { 193 | FSIMWebSocketServer.onlineCount--; 194 | } 195 | 196 | public static synchronized Map getClients() { 197 | return clients; 198 | } 199 | } 200 | --------------------------------------------------------------------------------