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()