├── 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 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
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 |
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 |
--------------------------------------------------------------------------------