1
zj
2024-07-29 ef299388e071ce75de6623009f21e425b5a3b017
1
11 files modified
3 files added
1 files deleted
450 ■■■■ changed files
websocketSerivce/pom.xml 6 ●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/controller/AdminLogin.java 22 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/controller/Login.java 89 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/controller/UserController.java 103 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/ConfigCurrency.java 4 ●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/Log.java 9 ●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/User.java 6 ●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/vo/ConfigCurrencyVo.java 20 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/vo/DeleteConfigVo.java 20 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/pojo/vo/SaveConfigVo.java 9 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/task/MarketDataTask.java 2 ●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/util/JwtUtil.java 93 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/util/LoginInterceptor.java 31 ●●●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/util/WebMvcConfig.java 4 ●●● patch | view | raw | blame | history
websocketSerivce/src/main/java/org/example/websocket/server/WsServer.java 32 ●●●●● patch | view | raw | blame | history
websocketSerivce/pom.xml
@@ -86,7 +86,11 @@
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!-- Mybatis的场景启动器 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
websocketSerivce/src/main/java/org/example/controller/AdminLogin.java
@@ -1,9 +1,11 @@
package org.example.controller;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.example.common.ServerResponse;
import org.example.pojo.User;
import org.example.server.impl.UserServiceImpl;
import org.example.util.JwtUtil;
import org.example.util.MD5Util;
import org.example.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -15,6 +17,8 @@
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * @program: demo
@@ -29,7 +33,7 @@
    private UserServiceImpl userService;
    @PostMapping("/login")
    public ServerResponse saveUser(@RequestParam("account") int account
    public ServerResponse saveUser(@RequestParam("account") String account
            , @RequestParam("password") String password) {
        User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getAccount, account).eq(User::getIsRoot,1));
@@ -40,17 +44,9 @@
        if (!MD5Util.verify(password, user.getPassword())) {
            return ServerResponse.createBySuccessMsg("密码错误");
        }
        String token = generateToken();
        RedisUtil.set(user.getAccount(),token);
        return ServerResponse.createBySuccess(token);
        String token = JwtUtil.getToken(user);
        Map<String,String> map = new HashMap<>();
        map.put("token",token);
        return ServerResponse.createBySuccess(map);
    }
    // 生成指定长度的随机 token
    public static String generateToken() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] token = new byte[16];
        secureRandom.nextBytes(token);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(token);
    }
}
websocketSerivce/src/main/java/org/example/controller/Login.java
File was deleted
websocketSerivce/src/main/java/org/example/controller/UserController.java
@@ -1,17 +1,32 @@
package org.example.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.example.common.ServerResponse;
import org.example.dao.ConfigCurrencyMapper;
import org.example.dao.LogMapper;
import org.example.pojo.ConfigCurrency;
import org.example.pojo.Log;
import org.example.pojo.User;
import org.example.pojo.vo.DeleteConfigVo;
import org.example.pojo.vo.SaveConfigVo;
import org.example.server.impl.UserServiceImpl;
import org.example.util.IpAddressUtil;
import org.example.util.JwtUtil;
import org.example.util.MD5Util;
import org.example.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.security.SecureRandom;
import java.sql.Date;
import java.time.LocalDate;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @program: demo
@@ -28,8 +43,65 @@
    @Autowired
    private UserServiceImpl userService;
    @Autowired
    private LogMapper logMapper;
    @PostMapping("/login")
    public ServerResponse saveUser(@RequestParam("account") String account
            , @RequestParam("password") String password, HttpServletRequest request) {
        User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getAccount, account).eq(User::getIsRoot,0));
        if(null == user){
            return ServerResponse.createBySuccessMsg("用户不存在");
        }
        if (!MD5Util.verify(password, user.getPassword())) {
            return ServerResponse.createBySuccessMsg("密码错误");
        }
        //判断是否锁定
        if(user.getIsLock() == 1){
            return ServerResponse.createBySuccessMsg("账号已被锁定");
        }
        //判断是否到期
        if(new java.util.Date().after(user.getEndTime())){
            return ServerResponse.createBySuccessMsg("账号已到期");
        }
        String token = JwtUtil.getToken(user);
        Map<String,String> map = new HashMap<>();
        map.put("token",token);
        String key = "user_";
        RedisUtil.set(key+user.getId(),token);
        String ip = IpAddressUtil.getIpAddress(request);
        String address = null;
        address = IpAddressUtil.getIpPossessionByFile(ip);
        if(null == address){
            address = IpAddressUtil.getIpAddressByOnline(ip);
        }
        Log log = new Log();
        log.setIp(ip);
        log.setAccount(account);
        log.setLoginTime(new java.util.Date());
        log.setAddress(address);
        logMapper.insert(log);
        return ServerResponse.createBySuccess(map);
    }
    // 生成指定长度的随机 token
    public static String generateToken() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] token = new byte[16];
        secureRandom.nextBytes(token);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(token);
    }
    @PostMapping("/saveConfig")
    public ServerResponse saveConfig(SaveConfigVo saveConfigVo) {
    public ServerResponse saveConfig(@RequestBody SaveConfigVo saveConfigVo) {
        currencyMapper.delete(new LambdaQueryWrapper<ConfigCurrency>().eq(ConfigCurrency::getUserId,saveConfigVo.getUserId()));
        saveConfigVo.getCurrencyList().forEach(f->{
            ConfigCurrency currency = new ConfigCurrency();
            currency.setUserId(saveConfigVo.getUserId());
@@ -38,11 +110,32 @@
            currency.setSell(f.getSell());
            currencyMapper.insert(currency);
        });
        List<ConfigCurrency> configCurrencies = currencyMapper.selectList(new LambdaQueryWrapper<ConfigCurrency>().eq(ConfigCurrency::getUserId, saveConfigVo.getUserId()));
        String key = "config_";
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String json = gson.toJson(configCurrencies);
        RedisUtil.set(key+saveConfigVo.getUserId(),json);
        return ServerResponse.createBySuccess();
    }
    @PostMapping("/deleteConfig")
    public ServerResponse deleteConfig(@RequestBody DeleteConfigVo deleteConfigVo) {
        currencyMapper.deleteBatchIds(deleteConfigVo.getConfigId());
        List<ConfigCurrency> configCurrencies = currencyMapper.selectList(new LambdaQueryWrapper<ConfigCurrency>().eq(ConfigCurrency::getUserId, deleteConfigVo.getUserId()));
        String key = "config_";
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String json = gson.toJson(configCurrencies);
        RedisUtil.set(key+deleteConfigVo.getUserId(),json);
        return ServerResponse.createBySuccess();
    }
    @PostMapping("/saveUser")
    public ServerResponse saveUser(User user) {
        long count = userService.count(new LambdaQueryWrapper<User>().eq(User::getAccount, user.getAccount()));
        if(count > 0){
            return ServerResponse.createByErrorMsg("账号已存在");
        }
        user.setAddTime(Date.valueOf(LocalDate.now()));
        user.setPassword(MD5Util.encrypt(user.getPassword()));
        userService.save(user);
        return ServerResponse.createBySuccess();
websocketSerivce/src/main/java/org/example/pojo/ConfigCurrency.java
@@ -1,5 +1,7 @@
package org.example.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import org.example.pojo.vo.SaveConfigVo;
@@ -12,7 +14,7 @@
 **/
@Data
public class ConfigCurrency {
    @TableId(type = IdType.AUTO)
    private Integer userId;
    private String currency;
websocketSerivce/src/main/java/org/example/pojo/Log.java
@@ -1,5 +1,10 @@
package org.example.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
@@ -11,11 +16,13 @@
 **/
@Data
public class Log {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String account;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") //指定格式
    private Date loginTime;
    private String ip;
websocketSerivce/src/main/java/org/example/pojo/User.java
@@ -1,7 +1,10 @@
package org.example.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.sql.Date;
@@ -14,10 +17,11 @@
@Data
public class User {
    @TableId(type = IdType.AUTO)
    /**
     * id
     */
    @TableId(type = IdType.AUTO)
    private Integer id;
    /**
websocketSerivce/src/main/java/org/example/pojo/vo/ConfigCurrencyVo.java
New file
@@ -0,0 +1,20 @@
package org.example.pojo.vo;
import lombok.Data;
/**
 * @program: demo
 * @description:
 * @create: 2024-07-29 16:48
 **/
@Data
public class ConfigCurrencyVo {
    private String currency;
    private String buy;
    private String sell;
}
websocketSerivce/src/main/java/org/example/pojo/vo/DeleteConfigVo.java
New file
@@ -0,0 +1,20 @@
package org.example.pojo.vo;
import lombok.Data;
import java.util.List;
/**
 * @program: demo
 * @description:
 * @create: 2024-07-29 17:19
 **/
@Data
public class DeleteConfigVo {
    private Integer userId;
    private List<String> configId;
}
websocketSerivce/src/main/java/org/example/pojo/vo/SaveConfigVo.java
@@ -16,13 +16,4 @@
    private Integer userId;
    private List<ConfigCurrencyVo> currencyList;
    @Data
    public class ConfigCurrencyVo{
        private String currency;
        private String buy;
        private String sell;
    }
}
websocketSerivce/src/main/java/org/example/task/MarketDataTask.java
@@ -27,7 +27,7 @@
    private final Lock syncCurrencyLock = new ReentrantLock();
    @Scheduled(initialDelay = 0, fixedRate = Long.MAX_VALUE)
//    @Scheduled(initialDelay = 0, fixedRate = Long.MAX_VALUE)
    public void start() {
        log.info("行情数据计算-------------启动");
websocketSerivce/src/main/java/org/example/util/JwtUtil.java
New file
@@ -0,0 +1,93 @@
package org.example.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.example.pojo.User;
/**
 * @program: demo
 * @description:
 * @create: 2024-07-29 15:45
 **/
@Slf4j
public class JwtUtil {
    /**
     * 使用固定的解密秘钥
     */
    private static final String SECRET = "admin";
    /**
     * @version: V1.0
     * @description: 生成token并验证token并解密token中的信息
     * @param:  userInfo 用户账号和用户Id
     * @return: java.lang.String 返回token
     **/
    public static String getToken(User userInfo) {
        try{
            //用秘钥生成签名
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            //默认头部+载荷(账号/id)+签名=jwt
            String jwtToken= JWT.create()
                    .withClaim("account", userInfo.getAccount())
                    .withClaim("id", userInfo.getId())
                    .withClaim("isRoot",userInfo.getIsRoot())
                    .sign(algorithm);
            log.info("用户{}的token生成成功:{}",userInfo.getId(),jwtToken);
            return jwtToken;
        }catch (Exception e){
            log.error("用户{}的token生成异常:{}",userInfo.getId(),e);
            return null;
        }
    }
    /**
     * @version: V1.0
     * @description: 校验token是否正确
     * @param:  token
     * @param: userPhone
     * @return: UserInfoEntity token中的用户信息(账号/id)
     **/
    public static User verify(String token) {
        try {
            // 根据用户信息userInfo生成JWT效验器
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            // 效验TOKEN
            verifier.verify(token);
            log.info("token:{}校验成功成功",token);
            //返回token内容
            return getTokenInfo(token);
        } catch (Exception exception) {
            log.error("token校验异常:{}",exception);
            return null;
        }
    }
    /**
     * @version: V1.0
     * @Title: getUsername
     * @description: 从Token中解密获得Token中的用户信息
     * @param:  token
     * @return: UserInfoEntity token中的用户信息(账号/id)
     **/
    private static User getTokenInfo(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            User userInfo=new User();
            userInfo.setAccount(jwt.getClaim("account").asString());
            userInfo.setId(jwt.getClaim("id").asInt());
            userInfo.setIsRoot(jwt.getClaim("isRoot").asInt());
            log.info("用户{}从token获取用户信息成功",userInfo.getId());
            return userInfo;
        } catch (JWTDecodeException e) {
            log.error("从token:{}获取用户信息异常:{}",token,e);
            return null;
        }
    }
}
websocketSerivce/src/main/java/org/example/util/LoginInterceptor.java
@@ -1,5 +1,12 @@
package org.example.util;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.example.pojo.User;
import org.example.server.UserService;
import org.example.server.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@@ -12,21 +19,39 @@
 * @description:
 * @create: 2024-07-29 11:15
 **/
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        HttpSession session = request.getSession();
        // 假设登录状态信息保存在 session 中,这里简单示例,实际项目中可能会有自己的登录状态判断逻辑
        if (session.getAttribute("loggedInUser") != null) {
        String token = request.getHeader("token");
        if(null == token){
            // 未登录,返回未授权错误码或重定向到登录页
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  // 返回 401 未授权状态码
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write("请登录");
            return false;
        }
        User user = JwtUtil.verify(token);
        if(null == user){
            // 未登录,返回未授权错误码或重定向到登录页
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  // 返回 401 未授权状态码
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write("请登录");
            return false;
        }
        if (user.getIsRoot() == 1) {
            // 已登录,允许请求继续
            return true;
        } else {
            // 未登录,返回未授权错误码或重定向到登录页
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  // 返回 401 未授权状态码
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write("请登录");
            return false;
        }
    }
websocketSerivce/src/main/java/org/example/util/WebMvcConfig.java
@@ -16,6 +16,8 @@
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  // 拦截所有请求
                .excludePathPatterns("/login", "/admin/login","/logout");  // 排除登录页和登出操作,防止死循环
                .excludePathPatterns("/admin/login",
                        "/user/saveConfig",
                        "/user/login");
    }
}
websocketSerivce/src/main/java/org/example/websocket/server/WsServer.java
@@ -1,17 +1,23 @@
package org.example.websocket.server;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.example.pojo.ConfigCurrency;
import org.example.pojo.MarketDataOut;
import org.example.pojo.bo.WsBo;
import org.example.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -61,6 +67,11 @@
    @OnOpen
    public void onOpen(Session session) {
        WsBo wsBo = getWsBoForSession(session.getId());
        String s = RedisUtil.get("user_" + wsBo.getUserId());
        if(null == s || s.isEmpty()){
            closeSession(session, "用户未登录");
        }
        this.session = session;
        int count = onlineCount.incrementAndGet();
        wsServers.add(this);
@@ -153,7 +164,7 @@
    private Map<Session, Long> lastMessageTimeMap = new ConcurrentHashMap<>();
    private void schedulePushMessage(Session session, String message) {
    private void schedulePushMessage(Session session, String message) throws JsonProcessingException {
        WsBo wsBo = getWsBoForSession(session.getId());
        if (wsBo != null) {
            long currentTime = System.currentTimeMillis();
@@ -171,8 +182,25 @@
        }
    }
    private static final Gson gson = new Gson();
    private String megFiltration(WsBo wsBo,String message){
    private String megFiltration(WsBo wsBo,String message) throws JsonProcessingException {
        List<MarketDataOut> redisValueMap = gson.fromJson(message, new TypeToken<List<MarketDataOut>>() {}.getType());
        String key = "config_";
        String value = RedisUtil.get(key + wsBo.getUserId());
        List<ConfigCurrency> currencies = null;
        if(null != value && !value.isEmpty()){
            ObjectMapper objectMapper = new ObjectMapper();
            currencies = objectMapper.readValue(value, new TypeReference<List<ConfigCurrency>>() {});
        }
        if(!CollectionUtils.isEmpty(currencies)){
            List<String> currency = currencies.stream().map(ConfigCurrency::getCurrency).collect(Collectors.toList());
            List<String> buy = currencies.stream().map(ConfigCurrency::getBuy).collect(Collectors.toList());
            List<String> sell = currencies.stream().map(ConfigCurrency::getSell).collect(Collectors.toList());
            redisValueMap = redisValueMap.stream()
                    .filter(data -> !currency.contains(data.getBaseAsset()) && !buy.contains(data.getBuyingPlatform()) && !sell.contains(data.getSellPlatform()))
                    .collect(Collectors.toList());
        }
        //查询币种
        if(null != wsBo.getCurrency()){
            redisValueMap = redisValueMap.stream()