From 45862fa4c741ef58c2cc4f3245d4678daddf656b Mon Sep 17 00:00:00 2001 From: mengyxu Date: Mon, 19 May 2025 19:53:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7=E5=92=8C?= =?UTF-8?q?=E5=AD=98=E6=A1=A3=E5=90=8E=E5=8F=B0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- java/application.properties | 5 +- .../vip/xumy/idle/server/conf/Configer.java | 46 ++++ .../idle/server/ctrl/ArchiveController.java | 49 +++++ .../idle/server/ctrl/PublicController.java | 22 ++ .../idle/server/mapper/IArchiveMapper.java | 18 ++ .../xumy/idle/server/mapper/IUserMapper.java | 4 +- .../vip/xumy/idle/server/pojo/Archive.java | 26 +++ .../java/vip/xumy/idle/server/pojo/User.java | 18 +- .../idle/server/service/ArchiveService.java | 38 ++++ .../xumy/idle/server/service/UserService.java | 51 +++++ .../vip/xumy/idle/server/util/LoginUtil.java | 205 ++++++++++++++++++ .../src/main/resources/application.properties | 1 - src/api/base.ts | 15 ++ src/api/index.ts | 11 + src/views/archive.vue | 10 +- vue.config.js | 14 +- 16 files changed, 519 insertions(+), 14 deletions(-) create mode 100644 java/src/main/java/vip/xumy/idle/server/conf/Configer.java create mode 100644 java/src/main/java/vip/xumy/idle/server/ctrl/ArchiveController.java create mode 100644 java/src/main/java/vip/xumy/idle/server/mapper/IArchiveMapper.java create mode 100644 java/src/main/java/vip/xumy/idle/server/pojo/Archive.java create mode 100644 java/src/main/java/vip/xumy/idle/server/service/ArchiveService.java create mode 100644 java/src/main/java/vip/xumy/idle/server/service/UserService.java create mode 100644 java/src/main/java/vip/xumy/idle/server/util/LoginUtil.java create mode 100644 src/api/base.ts create mode 100644 src/api/index.ts diff --git a/java/application.properties b/java/application.properties index 41a6bdf..a30da1e 100644 --- a/java/application.properties +++ b/java/application.properties @@ -1,4 +1,7 @@ # 程序自身数据源配置 spring.datasource.url=jdbc:mysql://10.100.0.108:3306/idle_game?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true spring.datasource.username=root -spring.datasource.password=mBMjp2v74c7MzhC/5RGeXF1V3E+AcRFs1f5OH6PDHi3nWaM3njaaoewswq8Sl7/AY631t0sONVDG672pX4mFpZx/8xex8BJP3uX1wUu9NGpTCSHskb5cD6KZMNV4jNsfoqxFRyJq8yk86djPsCfR4ch0sPnLH+LCg9XY1DAw8szh93QnKDkiImq2JLkaIwG3R3t8dNWv1wVoMR6vVecPsEUyPJu5vsrvJhjP3uAL7jZYJVGZG6bkrpV3R6I6mja0QnTdF41NCL5Ex0TINSi7GKnrALjZ0qWcT9fe2bTUCKq50+CtVEAQnEz2ZJpYUaBZXJbHh4OrUyqjAmA5s7IelQ== +spring.datasource.password=123456 + +login.token.timeout=10080 +login.token.domain= \ No newline at end of file diff --git a/java/src/main/java/vip/xumy/idle/server/conf/Configer.java b/java/src/main/java/vip/xumy/idle/server/conf/Configer.java new file mode 100644 index 0000000..9041476 --- /dev/null +++ b/java/src/main/java/vip/xumy/idle/server/conf/Configer.java @@ -0,0 +1,46 @@ +package vip.xumy.idle.server.conf; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; + +/** Ownership belongs to the company + * + * @author:mengyxu + * @date:2025年5月19日 + */ + +@Configuration +public class Configer { + + @Bean + public HttpMessageConverter configureMessageConverters() { + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); + FastJsonConfig config = new FastJsonConfig(); + config.setSerializerFeatures( + SerializerFeature.WriteMapNullValue, + SerializerFeature.WriteNullStringAsEmpty, + SerializerFeature.WriteNullNumberAsZero, + SerializerFeature.WriteNullListAsEmpty, + SerializerFeature.WriteNullBooleanAsFalse, + SerializerFeature.DisableCircularReferenceDetect + ); + + converter.setFastJsonConfig(config); + converter.setDefaultCharset(Charset.forName("UTF-8")); + List mediaTypeList = new ArrayList<>(); + mediaTypeList.add(MediaType.APPLICATION_JSON); + converter.setSupportedMediaTypes(mediaTypeList); + return converter; + } + +} diff --git a/java/src/main/java/vip/xumy/idle/server/ctrl/ArchiveController.java b/java/src/main/java/vip/xumy/idle/server/ctrl/ArchiveController.java new file mode 100644 index 0000000..4c7bd31 --- /dev/null +++ b/java/src/main/java/vip/xumy/idle/server/ctrl/ArchiveController.java @@ -0,0 +1,49 @@ +package vip.xumy.idle.server.ctrl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import vip.xumy.core.pojo.com.BaseResponse; +import vip.xumy.idle.server.pojo.Archive; +import vip.xumy.idle.server.service.ArchiveService; +import vip.xumy.idle.server.util.LoginUtil; + +/** + * Ownership belongs to the company + * + * @author:mengyxu + * @date:2025年5月19日 + */ + +@RestController +@RequestMapping("archive") +public class ArchiveController { + @Autowired + private ArchiveService archiveService; + + @PostMapping + public BaseResponse saveArchive(@RequestBody Archive archive) { + String userId = LoginUtil.getUserId(); + if (userId == null) { + return new BaseResponse(true, "请先登录"); + } + archive.setUsername(userId); + archiveService.saveOrUpdate(archive); + return new BaseResponse(true, "存档保存成功"); + } + + @GetMapping + public BaseResponse getArchive(String version) { + String userId = LoginUtil.getUserId(); + Archive archive = archiveService.getByKey(userId, version); + if (archive == null) { + return new BaseResponse(false, "未查询到你的存档"); + } + return new BaseResponse(true, archive.getArchive()); + } + +} diff --git a/java/src/main/java/vip/xumy/idle/server/ctrl/PublicController.java b/java/src/main/java/vip/xumy/idle/server/ctrl/PublicController.java index feb7f8e..d23cbaf 100644 --- a/java/src/main/java/vip/xumy/idle/server/ctrl/PublicController.java +++ b/java/src/main/java/vip/xumy/idle/server/ctrl/PublicController.java @@ -1,5 +1,6 @@ package vip.xumy.idle.server.ctrl; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -8,7 +9,10 @@ import org.springframework.web.bind.annotation.RestController; import vip.xumy.core.pojo.com.BaseResponse; import vip.xumy.idle.server.pojo.SocketMsg; +import vip.xumy.idle.server.pojo.User; +import vip.xumy.idle.server.service.UserService; import vip.xumy.idle.server.service.WebSocketService; +import vip.xumy.idle.server.util.LoginUtil; /** * Ownership belongs to the company @@ -20,6 +24,8 @@ import vip.xumy.idle.server.service.WebSocketService; @RestController @RequestMapping("public") public class PublicController { + @Autowired + private UserService userService; @GetMapping("socket/num") public BaseResponse getSocketNum() { @@ -31,5 +37,21 @@ public class PublicController { WebSocketService.sendMessage(new SocketMsg("update", notify)); return new BaseResponse(); } + + @PostMapping("login") + public BaseResponse login(@RequestBody User param) { + User user = userService.login(param); + if (user == null) { + return new BaseResponse(false, "登录失败,用户名或密码错误"); + } + LoginUtil.saveLoginInfo(user); + return new BaseResponse(user); + } + + @GetMapping("login") + public BaseResponse getLogin() { + User user = LoginUtil.getUserInfo(); + return new BaseResponse(user); + } } diff --git a/java/src/main/java/vip/xumy/idle/server/mapper/IArchiveMapper.java b/java/src/main/java/vip/xumy/idle/server/mapper/IArchiveMapper.java new file mode 100644 index 0000000..5ffcff3 --- /dev/null +++ b/java/src/main/java/vip/xumy/idle/server/mapper/IArchiveMapper.java @@ -0,0 +1,18 @@ +package vip.xumy.idle.server.mapper; + +import org.apache.ibatis.annotations.Mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import vip.xumy.idle.server.pojo.Archive; + +/** Ownership belongs to the company + * + * @author:mengyxu + * @date:2025年5月19日 + */ + +@Mapper +public interface IArchiveMapper extends BaseMapper { + +} diff --git a/java/src/main/java/vip/xumy/idle/server/mapper/IUserMapper.java b/java/src/main/java/vip/xumy/idle/server/mapper/IUserMapper.java index 2110678..3fe0763 100644 --- a/java/src/main/java/vip/xumy/idle/server/mapper/IUserMapper.java +++ b/java/src/main/java/vip/xumy/idle/server/mapper/IUserMapper.java @@ -16,8 +16,8 @@ import vip.xumy.idle.server.pojo.User; public interface IUserMapper extends BaseMapper { @Select(""" - SELECT username, state FROM user WHERE username = #{username} AND password = #{password} + UPDATE user SET last_login = NOW() WHERE username = #{username}; """) - User login(User param); + User login(String username); } diff --git a/java/src/main/java/vip/xumy/idle/server/pojo/Archive.java b/java/src/main/java/vip/xumy/idle/server/pojo/Archive.java new file mode 100644 index 0000000..b22f33a --- /dev/null +++ b/java/src/main/java/vip/xumy/idle/server/pojo/Archive.java @@ -0,0 +1,26 @@ +package vip.xumy.idle.server.pojo; + +import com.baomidou.mybatisplus.annotation.TableName; + +import lombok.Getter; +import lombok.Setter; + +/** + * Ownership belongs to the company + * + * @author:mengyxu + * @date:2025年5月19日 + */ + +@Setter +@Getter +@TableName("archive") +public class Archive { + + private String username; + private String version; + private int lv; + private long coins; + private String archive; + +} diff --git a/java/src/main/java/vip/xumy/idle/server/pojo/User.java b/java/src/main/java/vip/xumy/idle/server/pojo/User.java index 70385df..4d6406d 100644 --- a/java/src/main/java/vip/xumy/idle/server/pojo/User.java +++ b/java/src/main/java/vip/xumy/idle/server/pojo/User.java @@ -1,29 +1,33 @@ package vip.xumy.idle.server.pojo; +import com.alibaba.fastjson.annotation.JSONField; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; import lombok.Setter; -/** Ownership belongs to the company +/** + * Ownership belongs to the company * - * @author:mengyxu - * @date:2025年4月18日 + * @author:mengyxu + * @date:2025年4月18日 */ @Setter @Getter @TableName("user") public class User { - - private String useranme; + + @TableId + private String username; + @JSONField(serialize = false) private String password; private String state; private String phone; private String idCard; private String name; private String registerTime; - private String last_login; - + private String lastLogin; } diff --git a/java/src/main/java/vip/xumy/idle/server/service/ArchiveService.java b/java/src/main/java/vip/xumy/idle/server/service/ArchiveService.java new file mode 100644 index 0000000..017201a --- /dev/null +++ b/java/src/main/java/vip/xumy/idle/server/service/ArchiveService.java @@ -0,0 +1,38 @@ +package vip.xumy.idle.server.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; + +import vip.xumy.idle.server.mapper.IArchiveMapper; +import vip.xumy.idle.server.pojo.Archive; + +/** Ownership belongs to the company + * + * @author:mengyxu + * @date:2025年5月19日 + */ + +@Service +public class ArchiveService { + @Autowired + private IArchiveMapper archiveMapper; + + public void saveOrUpdate(Archive archive) { + Archive exit = getByKey(archive.getUsername(), archive.getVersion()); + if(exit == null) { + archiveMapper.insert(archive); + }else { + archiveMapper.updateById(archive); + } + } + + public Archive getByKey(String username, String version) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("username", username); + wrapper.eq("version", version); + return archiveMapper.selectOne(wrapper); + } + +} diff --git a/java/src/main/java/vip/xumy/idle/server/service/UserService.java b/java/src/main/java/vip/xumy/idle/server/service/UserService.java new file mode 100644 index 0000000..92297aa --- /dev/null +++ b/java/src/main/java/vip/xumy/idle/server/service/UserService.java @@ -0,0 +1,51 @@ +package vip.xumy.idle.server.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import vip.xumy.core.utils.DateUtil; +import vip.xumy.core.utils.StringUtil; +import vip.xumy.idle.server.mapper.IUserMapper; +import vip.xumy.idle.server.pojo.User; + +/** + * Ownership belongs to the company + * + * @author:mengyxu + * @date:2025年5月19日 + */ + +@Service +public class UserService { + @Autowired + private IUserMapper userMapper; + + public User login(User param) { + String username = param.getUsername(); + String password = param.getPassword(); + if (StringUtil.isEmpty(username, password)) { + return null; + } + User user = getById(username); + if (user != null) { + if (password.equals(user.getPassword())) { + userMapper.login(username); + return user; + } else { + return null; + } + } else { + String time = DateUtil.format(); + param.setState("0"); + param.setRegisterTime(time); + param.setLastLogin(time); + userMapper.insert(param); + return param; + } + } + + public User getById(String username) { + return userMapper.selectById(username); + } + +} diff --git a/java/src/main/java/vip/xumy/idle/server/util/LoginUtil.java b/java/src/main/java/vip/xumy/idle/server/util/LoginUtil.java new file mode 100644 index 0000000..0fd19a6 --- /dev/null +++ b/java/src/main/java/vip/xumy/idle/server/util/LoginUtil.java @@ -0,0 +1,205 @@ +package vip.xumy.idle.server.util; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import vip.xumy.core.golbal.GlobalBuffer; +import vip.xumy.core.pojo.com.Cache; +import vip.xumy.core.utils.StringUtil; +import vip.xumy.idle.server.pojo.User; + +/** + * Ownership belongs to the company + * + * @author:mengyxu + * @date:2024年3月5日 + */ + +//@Log4j2 +@Component +public class LoginUtil { + public static final String TIKEN_KEY_PREFIX = "token-"; + public static final String LOGIN_COOKIE_NAME = "token"; + + @Value("${login.token.timeout}") + public void setTimeout(Integer timeout) { + LoginUtil.timeout = timeout; + } + + private static Integer timeout; + + @Value("${login.token.domain}") + public void setDomain(String domain) { + LoginUtil.domain = domain; + } + + private static String domain; + + private static HttpServletRequest request; + + @Autowired + public void setRequest(HttpServletRequest request) { + LoginUtil.request = request; + } + + private static HttpServletResponse response; + + @Autowired + public void setResponse(HttpServletResponse response) { + LoginUtil.response = response; + } + + private static String getTokenKey(String userId) { + return TIKEN_KEY_PREFIX + userId; + } + + public static boolean isLogin(String userId) { + Cache cache = GlobalBuffer.getCache(getTokenKey(userId)); + return !Cache.isEmpty(cache); + } + + public static void remveLoginInfo(String userId) { + String tokenKey = LoginUtil.getTokenKey(userId); + Cache token = GlobalBuffer.getCache(tokenKey); + if (!Cache.isEmpty(token)) { + String value = token.getValue(); + GlobalBuffer.removeCache(value.substring(0, 10)); + GlobalBuffer.removeCache(tokenKey); + } + } + + public static void saveLoginInfo(User user) { + // 删除旧的token信息 + String userId = user.getUsername(); + Cache exitToken = GlobalBuffer.getCache(LoginUtil.getTokenKey(userId)); + if (!Cache.isEmpty(exitToken)) { + String value = exitToken.getValue(); + GlobalBuffer.removeCache(value.substring(0, 10)); + } + + // 计算新的token + String ip = getIpAddr(); + String seq = GlobalBuffer.getSeq(); + String token = seq + StringUtil.md5(user.getUsername() + ip + System.currentTimeMillis(), false); + + // 保存用户token + GlobalBuffer.addCache(LoginUtil.getTokenKey(userId), new Cache(token, timeout * 60)); + // 保存token的用户信息 + GlobalBuffer.addCache(seq, new Cache(user, timeout * 60)); + + LoginUtil.addToken(token, response); + + } + + private static void addToken(String token, HttpServletResponse response) { + Cookie cookie = new Cookie(LOGIN_COOKIE_NAME, token); + if (!StringUtil.isEmpty(domain)) { + cookie.setDomain(domain); + } + cookie.setPath("/"); + response.addCookie(cookie); + } + + public static User getUserInfo(String token, boolean refresh) { + if (StringUtil.isEmpty(token)) { + token = getToken(); + } + User empty = new User(); + if (StringUtil.isEmpty(token) || token.length() != 42) { + return empty; + } + Cache userCache = GlobalBuffer.getCache(token.substring(0, 10)); + if (Cache.isEmpty(userCache)) { + return empty; + } + User user = userCache.getValue(); + String userId = user.getUsername(); + Cache exitToken = GlobalBuffer.getCache(LoginUtil.getTokenKey(userId)); + if (Cache.isEmpty(exitToken)) { + return empty; + } + if (StringUtil.equals(token, exitToken.getValue())) { + if (refresh) { + userCache.refresh(); + exitToken.refresh(); + } + return user; + } + return empty; + } + + public static String getUserId() { + return getUserInfo(null, false).getUsername(); + } + + public static String getUserIdAndRefresh() { + return getUserInfo(null, true).getUsername(); + } + + public static User getUserInfo() { + return getUserInfo(null, false); + } + + public static void loginOut() { + String token = LoginUtil.getToken(); + if (!StringUtil.isEmpty(token)) { + String seq = token.substring(0, 10); + Cache userCache = GlobalBuffer.getCache(seq); + if (!Cache.isEmpty(userCache)) { + User user = userCache.getValue(); + String userId = user.getUsername(); + GlobalBuffer.removeCache(getTokenKey(userId)); + } + GlobalBuffer.removeCache(seq); + } + } + + public static String getToken() { + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return null; + } + for (Cookie cookie : cookies) { + if (LOGIN_COOKIE_NAME.equals(cookie.getName())) { + return cookie.getValue(); + } + } + return null; + } + + /** + * 获取访问者IP + * + * 在一般情况下使用Request.getRemoteAddr()即可,但是经过nginx等反向代理软件后,这个方法会失效。 + * + * 本方法先从Header中获取X-Real-IP,如果不存在再从X-Forwarded-For获得第一个IP(用,分割), 如果还不存在则调用Request + * .getRemoteAddr()。 + * + * @param request + * @return + */ + public static String getIpAddr() { + String ip = request.getHeader("X-Real-IP"); + if (!StringUtil.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) { + return ip; + } + ip = request.getHeader("X-Forwarded-For"); + if (!StringUtil.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) { + // 多次反向代理后会有多个IP值,第一个为真实IP。 + int index = ip.indexOf(','); + if (index != -1) { + return ip.substring(0, index); + } else { + return ip; + } + } else { + return request.getRemoteAddr(); + } + } + +} diff --git a/java/src/main/resources/application.properties b/java/src/main/resources/application.properties index 176d108..4e4d757 100644 --- a/java/src/main/resources/application.properties +++ b/java/src/main/resources/application.properties @@ -35,4 +35,3 @@ pagehelper.params=count=countSql pagehelper.returnPageInfo=check version.num=1.0.1 version.date=2025-03-07 -disk.info.path= \ No newline at end of file diff --git a/src/api/base.ts b/src/api/base.ts new file mode 100644 index 0000000..4adea1c --- /dev/null +++ b/src/api/base.ts @@ -0,0 +1,15 @@ +export const post = (url, data) => { + const options = { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json', + }, + }; + return new Promise((reslove, reject) => { + fetch(url, options) + .then((response) => response.json()) + .then((data) => console.log(data)) + .catch((error) => console.error('Error:', error)); + }); +}; diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..604417d --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,11 @@ +import { post } from './base'; + +let commit; + +export const setCommit = (data) => { + commit = data; +}; + +export const login = (param) => { + post('api/public/login', param); +}; diff --git a/src/views/archive.vue b/src/views/archive.vue index f1e7853..c3dafdd 100644 --- a/src/views/archive.vue +++ b/src/views/archive.vue @@ -10,10 +10,13 @@ + @@ -25,10 +28,12 @@ import { useI18n } from "vue3-i18n"; import { Tooltip, Dialog } from "@/components" import { menu_icons } from "@/config"; import { getArchive, AES_CBC_ENCRYPT, AES_CBC_DECRYPT, checkImportArchive, saveArchive, replace, archive_version } from "@/tool"; +import { setCommit, login } from "@/api"; const { t } = useI18n(); const { state, commit, dispatch } = useStore(); +setCommit(commit); const showArchive = computed(() => { return state.curMenu == 'archive'; }); @@ -67,7 +72,7 @@ const copyArchive = (archive) => { } } -const copyOldArchive = () => { +const uploadArchive = () => { const archive = localStorage.getItem('transmigration_game_archive') || ''; copyArchive(archive); } @@ -118,6 +123,7 @@ const keydown = (e) => { } onMounted(() => { document.addEventListener('keydown', keydown); + login({ username: 'mengyxu', password: '123456' }); }); onBeforeUnmount(() => { document.removeEventListener('keydown', keydown); diff --git a/vue.config.js b/vue.config.js index 027c02e..ed4927b 100644 --- a/vue.config.js +++ b/vue.config.js @@ -2,5 +2,17 @@ const { defineConfig } = require('@vue/cli-service'); module.exports = defineConfig({ productionSourceMap: false, transpileDependencies: true, + devServer: { + proxy: { + '/api': { + target: process.env.VUE_APP_BASE_URL, + // 允许跨域 + changeOrigin: true, + ws: true, + pathRewrite: { + '^/api': '', + }, + }, + }, + }, }); -