登录流程优化

This commit is contained in:
马超 2020-10-27 17:47:54 +08:00
parent aa1024e491
commit 1f0a579c29
11 changed files with 277 additions and 155 deletions

View File

@ -73,6 +73,11 @@
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
</dependencies>

View File

@ -0,0 +1,94 @@
package com.qiwenshare.common.util;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.net.util.Base64;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class JjwtUtil {
// jtijwt的唯一身份标识
public static final String JWT_ID = UUID.randomUUID().toString();
// 加密密文私钥
public static final String JWT_SECRET = "jiamimiwen";
// 过期时间单位毫秒
public static final int EXPIRE_TIME = 60 * 60 * 1000; // 一个小时
// public static final long EXPIRE_TIME = 7 * 24 * 3600 * 1000; // 一个星期
// 由字符串生成加密key
public static SecretKey generalKey() {
String secret = JWT_SECRET;
// 本地的密码解码
byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
// 根据给定的字节数组使用AES加密算法构造一个密钥
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
// 创建jwt
public static String createJWT(String issuer, String audience, String subject) throws Exception {
// 设置头部信息
// Map<String, Object> header = new HashMap<String, Object>();
// header.put("typ", "JWT");
// header.put("alg", "HS256");
//
// 指定header那部分签名的时候使用的签名算法jjwt已经将这部分内容封装好了只有{"alg":"HS256"}
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 创建payload的私有声明根据特定的业务需要添加如果要拿这个做验证一般是需要和jwt的接收方提前沟通好验证的方式
Map<String, Object> claims = new HashMap<>();
claims.put("username", "admin");
claims.put("password", "010203");
// jti用户id例如20da39f8-b74e-4a9b-9a0f-a39f1f73fe64
String jwtId = JWT_ID;
// 生成JWT的时间
long nowTime = System.currentTimeMillis();
Date issuedAt = new Date(nowTime);
// 生成签名的时候使用的秘钥secret切记这个秘钥不能外露是你服务端的私钥在任何场景都不应该流露出去一旦客户端得知这个secret那就意味着客户端是可以自我签发jwt的
SecretKey key = generalKey();
// 为payload添加各种标准声明和私有声明
JwtBuilder builder = Jwts.builder() // 表示new一个JwtBuilder设置jwt的body
// .setHeader(header) // 设置头部信息
.setClaims(claims) // 如果有私有声明一定要先设置自己创建的这个私有声明这是给builder的claim赋值一旦写在标准的声明赋值之后就是覆盖了那些标准的声明
.setId(jwtId) // jti(JWT ID)jwt的唯一身份标识根据业务需要可以设置为一个不重复的值主要用来作为一次性token从而回避重放攻击
.setIssuedAt(issuedAt) // iat(issuedAt)jwt的签发时间
.setIssuer(issuer) // iss(issuer)jwt签发者
.setSubject(subject) // sub(subject)jwt所面向的用户放登录的用户名一个json格式的字符串可存放useridroldid之类作为用户的唯一标志
.signWith(signatureAlgorithm, key); // 设置签名使用的是签名算法和签名使用的秘钥
// 设置过期时间
long expTime = EXPIRE_TIME;
if (expTime >= 0) {
long exp = nowTime + expTime;
builder.setExpiration(new Date(exp));
}
// 设置jwt接收者
if (audience == null || "".equals(audience)) {
builder.setAudience("Tom");
} else {
builder.setAudience(audience);
}
return builder.compact();
}
// 解密jwt
public static Claims parseJWT(String jwt) throws Exception {
SecretKey key = generalKey(); // 签名秘钥和生成的签名的秘钥一模一样
Claims claims = Jwts.parser() // 得到DefaultJwtParser
.setSigningKey(key) // 设置签名的秘钥
.parseClaimsJws(jwt).getBody(); // 设置需要解析的jwt
return claims;
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This is the JRebel configuration file. It maps the running application to your IDE workspace, enabling JRebel reloading for this project.
Refer to https://manuals.jrebel.com/jrebel/standalone/config.html for more information.
-->
<application generated-by="intellij" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_3.xsd">
<id>file-common</id>
<classpath>
<dir name="E:/workspace/qiwen-file/file-common/target/classes">
</dir>
</classpath>
</application>

View File

@ -31,10 +31,6 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-config-client</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -10,6 +10,9 @@ import java.util.List;
public interface IUserService extends IService<UserBean> {
UserBean getUserBeanByToken(String token);
/**
* 用户注册
*

View File

@ -11,6 +11,7 @@ import com.qiwenshare.common.util.PathUtil;
import com.qiwenshare.file.api.IFileService;
import com.qiwenshare.file.api.IFiletransferService;
import com.qiwenshare.file.api.IRemoteUserService;
import com.qiwenshare.file.api.IUserService;
import com.qiwenshare.file.config.QiwenFileConfig;
import com.qiwenshare.file.domain.FileBean;
import com.qiwenshare.file.domain.TreeNode;
@ -33,6 +34,8 @@ public class FileController {
@Resource
IFileService fileService;
@Resource
IUserService userService;
@Autowired
IRemoteUserService remoteUserService;
@Resource
@ -58,15 +61,7 @@ public class FileController {
restResult.setSuccess(false);
return restResult;
}
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
fileBean.setUserId(sessionUserBean.getUserId());
@ -89,15 +84,7 @@ public class FileController {
if (!operationCheck(token).isSuccess()){
return operationCheck(token);
}
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
fileBean.setUserId(sessionUserBean.getUserId());
fileBean.setUploadTime(DateUtil.getCurrentTime());
@ -132,16 +119,7 @@ public class FileController {
if(qiwenFileConfig.isShareMode()){
fileBean.setUserId(2L);
}else {
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
if (fileBean == null) {
restResult.setSuccess(false);
return restResult;
@ -169,15 +147,7 @@ public class FileController {
if (!operationCheck(token).isSuccess()) {
return operationCheck(token);
}
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
List<FileBean> fileList = JSON.parseArray(fileBean.getFiles(), FileBean.class);
for (FileBean file : fileList) {
@ -201,15 +171,7 @@ public class FileController {
if (!operationCheck(token).isSuccess()){
return JSON.toJSONString(operationCheck(token));
}
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
fileService.deleteFile(fileBean, sessionUserBean);
result.setSuccess(true);
@ -258,16 +220,7 @@ public class FileController {
}
List<FileBean> fileBeanList = new ArrayList<>();
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
for (int i = 0; i < fileEntryNameList.size(); i++){
String entryName = fileEntryNameList.get(i);
String totalFileUrl = unzipUrl + entryName;
@ -356,16 +309,7 @@ public class FileController {
public RestResult<String> operationCheck(String token){
RestResult<String> result = new RestResult<String>();
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
if (sessionUserBean == null){
result.setSuccess(false);
result.setErrorMessage("未登录");
@ -391,16 +335,7 @@ public class FileController {
@ResponseBody
public RestResult<List<FileBean>> selectFileByFileType(FileBean fileBean, @RequestHeader("token") String token) {
RestResult<List<FileBean>> result = new RestResult<List<FileBean>>();
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
long userId = sessionUserBean.getUserId();
if (qiwenFileConfig.isShareMode()){
userId = 2;
@ -420,16 +355,7 @@ public class FileController {
public RestResult<TreeNode> getFileTree(@RequestHeader("token") String token){
RestResult<TreeNode> result = new RestResult<TreeNode>();
FileBean fileBean = new FileBean();
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
if (qiwenFileConfig.isShareMode()){
fileBean.setUserId(2L);
}else{

View File

@ -11,6 +11,7 @@ import com.qiwenshare.common.operation.ImageOperation;
import com.qiwenshare.file.api.IFileService;
import com.qiwenshare.file.api.IFiletransferService;
import com.qiwenshare.file.api.IRemoteUserService;
import com.qiwenshare.file.api.IUserService;
import com.qiwenshare.file.config.QiwenFileConfig;
import com.qiwenshare.file.domain.FileBean;
import com.qiwenshare.file.domain.StorageBean;
@ -42,6 +43,8 @@ public class FiletransferController {
QiwenFileConfig qiwenFileConfig;
@Resource
IFileService fileService;
@Resource
IUserService userService;
/**
* 上传文件
@ -53,16 +56,7 @@ public class FiletransferController {
@ResponseBody
public String uploadFile(HttpServletRequest request, FileBean fileBean, @RequestHeader("token") String token) {
RestResult<String> restResult = new RestResult<String>();
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
if (sessionUserBean == null){
restResult.setSuccess(false);
restResult.setErrorMessage("未登录");
@ -175,15 +169,7 @@ public class FiletransferController {
public RestResult<StorageBean> getStorage(@RequestHeader("token") String token) {
RestResult<StorageBean> restResult = new RestResult<StorageBean>();
//UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = new UserBean();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
RestResult<UserBean> restUserBean = remoteUserService.checkUserLoginInfo(token);
sessionUserBean = restUserBean.getData();
} else {
sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
}
UserBean sessionUserBean = userService.getUserBeanByToken(token);
StorageBean storageBean = new StorageBean();
if (qiwenFileConfig.isShareMode()){
storageBean.setUserId(2L);

View File

@ -1,14 +1,21 @@
package com.qiwenshare.file.controller;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.qiwenshare.common.cbb.DateUtil;
import com.qiwenshare.common.cbb.RestResult;
import com.qiwenshare.common.domain.AliyunOSS;
import com.qiwenshare.common.util.JjwtUtil;
import com.qiwenshare.file.api.IRemoteUserService;
import com.qiwenshare.file.api.IUserService;
import com.qiwenshare.file.config.QiwenFileConfig;
import com.qiwenshare.file.domain.UserBean;
import com.qiwenshare.file.vo.user.UserLoginVo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -24,6 +31,7 @@ import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Resource
IUserService userService;
@ -47,12 +55,9 @@ public class UserController {
@ResponseBody
public RestResult<String> addUser(@RequestBody UserBean userBean) {
RestResult<String> restResult = null;
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
restResult = remoteUserService.addUser(userBean);
} else {
restResult = userService.registerUser(userBean);
}
return restResult;
}
@ -64,28 +69,32 @@ public class UserController {
*/
@RequestMapping("/userlogin")
@ResponseBody
public RestResult<UserBean> userLogin(@RequestBody UserBean userBean) {
RestResult<UserBean> restResult = new RestResult<UserBean>();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
restResult = remoteUserService.userLogin(userBean);
} else {
restResult.setSuccess(true);
public RestResult<UserLoginVo> userLogin(@RequestBody UserBean userBean) {
RestResult<UserLoginVo> restResult = new RestResult<UserLoginVo>();
UserBean saveUserBean = userService.findUserInfoByTelephone(userBean.getUsername());
String jwt = "";
try {
SecurityUtils.getSubject().login(new UsernamePasswordToken(userBean.getUsername(), userBean.getPassword()));
}catch (Exception e){
jwt = JjwtUtil.createJWT("qiwenshare", "qiwen", JSON.toJSONString(saveUserBean));
} catch (Exception e) {
logger.info("登录失败:{}", e);
restResult.setSuccess(false);
restResult.setErrorMessage("手机号或密码错误!");
restResult.setErrorMessage("登录失败!");
return restResult;
}
UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
if (sessionUserBean != null) {
restResult.setData(sessionUserBean);
String password = new SimpleHash("MD5", userBean.getPassword(), saveUserBean.getSalt(), 1024).toHex();
if (password.equals(saveUserBean.getPassword())) {
UserLoginVo userLoginVo = new UserLoginVo();
BeanUtil.copyProperties(userBean, userLoginVo);
userLoginVo.setToken(jwt);
restResult.setData(userLoginVo);
restResult.setSuccess(true);
} else {
restResult.setSuccess(false);
restResult.setErrorMessage("手机号或密码错误!");
}
}
return restResult;
}
@ -119,29 +128,26 @@ public class UserController {
@ResponseBody
public RestResult<UserBean> checkUserLoginInfo(@RequestHeader("token") String token) {
RestResult<UserBean> restResult = new RestResult<UserBean>();
boolean isRemoteLogin = qiwenFileConfig.isRemoteLogin();
if (isRemoteLogin) {
restResult = remoteUserService.checkUserLoginInfo(token);
} else {
UserBean sessionUserBean = (UserBean) SecurityUtils.getSubject().getPrincipal();
UserBean sessionUserBean = userService.getUserBeanByToken(token);
if (sessionUserBean != null) {
UserBean userInfo = userService.getUserInfoById(sessionUserBean.getUserId());
restResult.setData(userInfo);
restResult.setData(sessionUserBean);
restResult.setSuccess(true);
} else {
restResult.setSuccess(false);
restResult.setErrorMessage("用户暂未登录");
}
}
AliyunOSS oss = qiwenFileConfig.getAliyun().getOss();
String domain = oss.getDomain();
restResult.getData().setViewDomain(domain);
String bucketName = oss.getBucketName();
String endPoint = oss.getEndpoint();
restResult.getData().setDownloadDomain(bucketName + "." + endPoint);
} else {
restResult.setSuccess(false);
restResult.setErrorMessage("用户暂未登录");
}
return restResult;
}

View File

@ -1,15 +1,20 @@
package com.qiwenshare.file.service;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiwenshare.common.cbb.DateUtil;
import com.qiwenshare.common.cbb.RestResult;
import com.qiwenshare.common.domain.TableQueryBean;
import com.qiwenshare.common.util.JjwtUtil;
import com.qiwenshare.common.util.PasswordUtil;
import com.qiwenshare.file.api.IUserService;
import com.qiwenshare.file.controller.UserController;
import com.qiwenshare.file.domain.UserBean;
import com.qiwenshare.file.mapper.UserMapper;
import io.jsonwebtoken.Claims;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -18,10 +23,39 @@ import java.util.regex.Pattern;
@Service
public class UserService extends ServiceImpl<UserMapper, UserBean> implements IUserService {
//private static final Logger log= Logger.getLogger(EssayService.class);
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
@Resource
UserMapper userMapper;
@Override
public UserBean getUserBeanByToken(String token){
Claims c = null;
try {
logger.info("token:" + token);
c = JjwtUtil.parseJWT(token);
} catch (Exception e) {
logger.error("解码异常");
e.printStackTrace();
return null;
}
if (c == null) {
logger.info("解码为空");
return null;
}
String subject = c.getSubject();
logger.info("解析结果:" + subject);
UserBean tokenUserBean = JSON.parseObject(subject, UserBean.class);
UserBean saveUserBean = findUserInfoByTelephone(tokenUserBean.getTelephone());
if (tokenUserBean.getPassword().equals(saveUserBean.getPassword())) {
return saveUserBean;
} else {
return null;
}
}
/**
* 用户注册
*/

View File

@ -0,0 +1,51 @@
package com.qiwenshare.file.vo.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "用户登录Vo",required = true)
public class UserLoginVo {
@Schema(description = "用户id", example = "1")
private long userId;
// @Schema(description = "openId", example = "")
// private String openId;
@Schema(description = "用户名", example = "奇文网盘")
private String username;
@Schema(description = "真实名", example = "张三")
private String realname;
@Schema(description = "qq用户名", example = "水晶之恋")
private String qqUsername;
@Schema(description = "qq用户头像", example = "https://thirdqq.qlogo.cn/g?b=oidb&k=qxLE4dibR9sic8kS7mHLxlLw&s=100&t=1557468980")
private String qqImageUrl;
@Schema(description = "手机号", example = "187****1817")
private String telephone;
@Schema(description = "邮箱", example = "116****483@qq.com")
private String email;
@Schema(description = "性别", example = "")
private String sex;
@Schema(description = "生日", example = "1994-05-06")
private String birthday;
@Schema(description = "", example = "陕西省")
private String addrProvince;
@Schema(description = "", example = "西安市")
private String addrCity;
@Schema(description = "", example = "雁塔区")
private String addrArea;
@Schema(description = "行业", example = "计算机行业")
private String industry;
@Schema(description = "职位", example = "java开发")
private String position;
@Schema(description = "个人介绍", example = "错把陈醋当成墨,写尽半生都是酸")
private String intro;
@Schema(description = "用户头像地址", example = "\\upload\\20200405\\93811586079860974.png")
private String imageUrl;
@Schema(description = "注册时间", example = "2019-12-23 14:21:52")
private String registerTime;
@Schema(description = "最后登录时间", example = "2019-12-23 14:21:52")
private String lastLoginTime;
@Schema(description = "Token 接口访问凭证")
private String token;
}

View File

@ -52,6 +52,11 @@
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.4</version>
</dependency>
<!--jpa-->
<dependency>