From 76208975bffec39eb62f8599a68d583a5cb6da18 Mon Sep 17 00:00:00 2001
From: leesam <leesam@leesam.cn>
Date: 星期二, 19 三月 2024 16:53:01 +0800
Subject: [PATCH] [add]支持其他平台通过ApiKey调用系统相关接口
---
web_src/src/components/dialog/addUserApiKey.vue | 139 ++++++
web_src/config/index.js | 2
web_src/src/components/dialog/remarkUserApiKey.vue | 93 ++++
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java | 106 ++++
web_src/src/components/UserManager.vue | 7
web_src/src/components/UserApiKeyManager.vue | 296 +++++++++++++
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java | 2
数据库/初始化-mysql.sql | 12
src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java | 5
数据库/2.7.0/初始化-postgresql-kingbase-2.7.0.sql | 12
src/main/java/com/genersoft/iot/vmp/service/IUserService.java | 2
pom.xml | 4
打包/config/wvp-application.yml | 2
src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java | 7
数据库/初始化-postgresql-kingbase.sql | 12
src/main/java/com/genersoft/iot/vmp/service/IUserApiKeyService.java | 25 +
web_src/src/router/index.js | 8
src/main/java/com/genersoft/iot/vmp/storager/dao/UserApiKeyMapper.java | 60 ++
src/main/java/com/genersoft/iot/vmp/service/impl/UserApiKeyServiceImpl.java | 80 +++
数据库/2.7.0/初始化-mysql-2.7.0.sql | 11
src/main/java/com/genersoft/iot/vmp/storager/dao/dto/UserApiKey.java | 151 ++++++
src/main/resources/application-dev.yml | 2
src/main/resources/application-docker.yml | 2
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserApiKeyController.java | 251 +++++++++++
24 files changed, 1,267 insertions(+), 24 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7ec73b8..f38d693 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,6 +101,10 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-cache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
index be57316..262910b 100644
--- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
+++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
@@ -9,6 +9,7 @@
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -24,6 +25,7 @@
@ServletComponentScan("com.genersoft.iot.vmp.conf")
@SpringBootApplication
@EnableScheduling
+@EnableCaching
public class VManageBootstrap extends SpringBootServletInitializer {
private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
index f45f89a..274a19f 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
@@ -51,8 +51,11 @@
if (StringUtils.isBlank(jwt)) {
jwt = request.getParameter(JwtUtils.getHeader());
if (StringUtils.isBlank(jwt)) {
- chain.doFilter(request, response);
- return;
+ jwt = request.getHeader(JwtUtils.getApiKeyHeader());
+ if (StringUtils.isBlank(jwt)) {
+ chain.doFilter(request, response);
+ return;
+ }
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
index fcd1946..949bcba 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
@@ -1,8 +1,12 @@
package com.genersoft.iot.vmp.conf.security;
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
+import com.genersoft.iot.vmp.service.IUserApiKeyService;
import com.genersoft.iot.vmp.service.IUserService;
import com.genersoft.iot.vmp.storager.dao.dto.User;
+import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
+import org.jose4j.jwk.JsonWebKey;
+import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
@@ -20,8 +24,18 @@
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Map;
@Component
public class JwtUtils implements InitializingBean {
@@ -30,6 +44,8 @@
public static final String HEADER = "access-token";
+ public static final String API_KEY_HEADER = "api-key";
+
private static final String AUDIENCE = "Audience";
private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae";
@@ -37,15 +53,26 @@
/**
* token杩囨湡鏃堕棿(鍒嗛挓)
*/
- public static final long expirationTime = 30 * 24 * 60;
+ public static final long EXPIRATION_TIME = 30 * 24 * 60;
private static RsaJsonWebKey rsaJsonWebKey;
private static IUserService userService;
+ private static IUserApiKeyService userApiKeyService;
+
+ public static String getApiKeyHeader() {
+ return API_KEY_HEADER;
+ }
+
@Resource
public void setUserService(IUserService userService) {
JwtUtils.userService = userService;
+ }
+
+ @Resource
+ public void setUserApiKeyService(IUserApiKeyService userApiKeyService) {
+ JwtUtils.userApiKeyService = userApiKeyService;
}
@Override
@@ -62,14 +89,38 @@
* @throws JoseException JoseException
*/
private RsaJsonWebKey generateRsaJsonWebKey() throws JoseException {
- // 鐢熸垚涓�涓猂SA瀵嗛挜瀵癸紝璇ュ瘑閽ュ灏嗙敤浜嶫WT鐨勭鍚嶅拰楠岃瘉锛屽寘瑁呭湪JWK涓�
- RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
- // 缁橨WK涓�涓瘑閽D
- rsaJsonWebKey.setKeyId(keyId);
+ RsaJsonWebKey rsaJsonWebKey = null;
+ try {
+ URL url = getClass().getClassLoader().getResource("jwk.json");
+ if (url != null) {
+ URI uri = url.toURI();
+ Path path = Paths.get(uri);
+ if (Files.exists(path)) {
+ byte[] allBytes = Files.readAllBytes(path);
+ String jwkJson = new String(allBytes, StandardCharsets.UTF_8);
+ final JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwkJson);
+ List<JsonWebKey> jsonWebKeys = jsonWebKeySet.getJsonWebKeys();
+ if (!jsonWebKeys.isEmpty()) {
+ JsonWebKey jsonWebKey = jsonWebKeys.get(0);
+ if (jsonWebKey instanceof RsaJsonWebKey) {
+ rsaJsonWebKey = (RsaJsonWebKey) jsonWebKey;
+ }
+ }
+ }
+ }
+ } catch (URISyntaxException | IOException e) {
+ // ignored
+ }
+ if (rsaJsonWebKey == null) {
+ // 鐢熸垚涓�涓猂SA瀵嗛挜瀵癸紝璇ュ瘑閽ュ灏嗙敤浜嶫WT鐨勭鍚嶅拰楠岃瘉锛屽寘瑁呭湪JWK涓�
+ rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
+ // 缁橨WK涓�涓瘑閽D
+ rsaJsonWebKey.setKeyId(keyId);
+ }
return rsaJsonWebKey;
}
- public static String createToken(String username) {
+ public static String createToken(String username, Long expirationTime, Map<String, Object> extra) {
try {
/*
* 鈥渋ss鈥� (issuer) 鍙戣浜�
@@ -83,13 +134,17 @@
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
// 浠ょ墝灏嗚繃鏈熺殑鏃堕棿 鍒嗛挓
- claims.setExpirationTimeMinutesInTheFuture(expirationTime);
+ if (expirationTime != null) {
+ claims.setExpirationTimeMinutesInTheFuture(expirationTime);
+ }
claims.setNotBeforeMinutesInThePast(0);
claims.setSubject("login");
claims.setAudience(AUDIENCE);
//娣诲姞鑷畾涔夊弬鏁�,蹇呴』鏄瓧绗︿覆绫诲瀷
claims.setClaim("userName", username);
-
+ if (extra != null) {
+ extra.forEach(claims::setClaim);
+ }
//jws
JsonWebSignature jws = new JsonWebSignature();
//绛惧悕绠楁硶RS256
@@ -104,8 +159,15 @@
} catch (JoseException e) {
logger.error("[Token鐢熸垚澶辫触]锛� {}", e.getMessage());
}
-
return null;
+ }
+
+ public static String createToken(String username, Long expirationTime) {
+ return createToken(username, expirationTime, null);
+ }
+
+ public static String createToken(String username) {
+ return createToken(username, EXPIRATION_TIME);
}
public static String getHeader() {
@@ -118,8 +180,8 @@
try {
JwtConsumer consumer = new JwtConsumerBuilder()
- .setRequireExpirationTime()
- .setMaxFutureValidityInMinutes(5256000)
+ //.setRequireExpirationTime()
+ //.setMaxFutureValidityInMinutes(5256000)
.setAllowedClockSkewInSeconds(30)
.setRequireSubject()
//.setExpectedIssuer("")
@@ -129,15 +191,27 @@
JwtClaims claims = consumer.processToClaims(token);
NumericDate expirationTime = claims.getExpirationTime();
- // 鍒ゆ柇鏄惁鍗冲皢杩囨湡, 榛樿鍓╀綑鏃堕棿灏忎簬5鍒嗛挓鏈嵆灏嗚繃鏈�
- // 鍓╀綑鏃堕棿 锛堢锛�
- long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
- if (timeRemaining < 5 * 60) {
- jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
+ if (expirationTime != null) {
+ // 鍒ゆ柇鏄惁鍗冲皢杩囨湡, 榛樿鍓╀綑鏃堕棿灏忎簬5鍒嗛挓鏈嵆灏嗚繃鏈�
+ // 鍓╀綑鏃堕棿 锛堢锛�
+ long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
+ if (timeRemaining < 5 * 60) {
+ jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
+ } else {
+ jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
+ }
} else {
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
}
+ Long apiKeyId = claims.getClaimValue("apiKeyId", Long.class);
+ if (apiKeyId != null) {
+ UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(apiKeyId.intValue());
+ if (userApiKey == null || !userApiKey.isEnable()) {
+ jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
+ }
+ }
+
String username = (String) claims.getClaimValue("userName");
User user = userService.getUserByUsername(username);
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IUserApiKeyService.java b/src/main/java/com/genersoft/iot/vmp/service/IUserApiKeyService.java
new file mode 100644
index 0000000..b3cc580
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/IUserApiKeyService.java
@@ -0,0 +1,25 @@
+package com.genersoft.iot.vmp.service;
+
+import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
+import com.github.pagehelper.PageInfo;
+
+public interface IUserApiKeyService {
+ int addApiKey(UserApiKey userApiKey);
+
+ boolean isApiKeyExists(String apiKey);
+
+ PageInfo<UserApiKey> getUserApiKeys(int page, int count);
+
+ int enable(Integer id);
+
+ int disable(Integer id);
+
+ int remark(Integer id, String remark);
+
+ int delete(Integer id);
+
+ UserApiKey getUserApiKeyById(Integer id);
+
+ int reset(Integer id, String apiKey);
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java
index 7e2a839..1e9b724 100755
--- a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java
@@ -11,6 +11,8 @@
boolean changePassword(int id, String password);
+ User getUserById(int id);
+
User getUserByUsername(String username);
int addUser(User user);
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/UserApiKeyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/UserApiKeyServiceImpl.java
new file mode 100644
index 0000000..85ee4f0
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/UserApiKeyServiceImpl.java
@@ -0,0 +1,80 @@
+package com.genersoft.iot.vmp.service.impl;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.genersoft.iot.vmp.service.IUserApiKeyService;
+import com.genersoft.iot.vmp.storager.dao.UserApiKeyMapper;
+import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@DS("master")
+public class UserApiKeyServiceImpl implements IUserApiKeyService {
+
+ @Autowired
+ UserApiKeyMapper userApiKeyMapper;
+
+ @Autowired
+ private RedisTemplate<Object, Object> redisTemplate;
+
+ @Override
+ public int addApiKey(UserApiKey userApiKey) {
+ return userApiKeyMapper.add(userApiKey);
+ }
+
+ @Override
+ public boolean isApiKeyExists(String apiKey) {
+ return userApiKeyMapper.isApiKeyExists(apiKey);
+ }
+
+ @Override
+ public PageInfo<UserApiKey> getUserApiKeys(int page, int count) {
+ PageHelper.startPage(page, count);
+ List<UserApiKey> userApiKeys = userApiKeyMapper.getUserApiKeys();
+ return new PageInfo<>(userApiKeys);
+ }
+
+ @Cacheable(cacheNames = "userApiKey", key = "#id", sync = true)
+ @Override
+ public UserApiKey getUserApiKeyById(Integer id) {
+ return userApiKeyMapper.selectById(id);
+ }
+
+ @CacheEvict(cacheNames = "userApiKey", key = "#id")
+ @Override
+ public int enable(Integer id) {
+ return userApiKeyMapper.enable(id);
+ }
+
+ @CacheEvict(cacheNames = "userApiKey", key = "#id")
+ @Override
+ public int disable(Integer id) {
+ return userApiKeyMapper.disable(id);
+ }
+
+ @CacheEvict(cacheNames = "userApiKey", key = "#id")
+ @Override
+ public int remark(Integer id, String remark) {
+ return userApiKeyMapper.remark(id, remark);
+ }
+
+ @CacheEvict(cacheNames = "userApiKey", key = "#id")
+ @Override
+ public int delete(Integer id) {
+ return userApiKeyMapper.delete(id);
+ }
+
+ @CacheEvict(cacheNames = "userApiKey", key = "#id")
+ @Override
+ public int reset(Integer id, String apiKey) {
+ return userApiKeyMapper.apiKey(id, apiKey);
+ }
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java
index 400b19b..fb97db9 100755
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java
@@ -32,6 +32,11 @@
}
@Override
+ public User getUserById(int id) {
+ return userMapper.selectById(id);
+ }
+
+ @Override
public User getUserByUsername(String username) {
return userMapper.getUserByUsername(username);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserApiKeyMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserApiKeyMapper.java
new file mode 100644
index 0000000..d235475
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserApiKeyMapper.java
@@ -0,0 +1,60 @@
+package com.genersoft.iot.vmp.storager.dao;
+
+import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Mapper
+@Repository
+public interface UserApiKeyMapper {
+
+ @SelectKey(statement = "SELECT LAST_INSERT_ID() AS id", keyProperty = "id", before = false, resultType = Integer.class)
+ @Insert("INSERT INTO wvp_user_api_key (user_id, app, api_key, expired_at, remark, enable, create_time, update_time) VALUES" +
+ "(#{userId}, #{app}, #{apiKey}, #{expiredAt}, #{remark}, #{enable}, #{createTime}, #{updateTime})")
+ int add(UserApiKey userApiKey);
+
+ @Update(value = {"<script>" +
+ "UPDATE wvp_user_api_key " +
+ "SET update_time = #{updateTime} " +
+ "<if test=\"app != null\">, app = #{app}</if>" +
+ "<if test=\"apiKey != null\">, api_key = #{apiKey}</if>" +
+ "<if test=\"expiredAt != null\">, expired_at = #{expiredAt}</if>" +
+ "<if test=\"remark != null\">, username = #{remark}</if>" +
+ "<if test=\"enable != null\">, enable = #{enable}</if>" +
+ "WHERE id = #{id}" +
+ " </script>"})
+ int update(UserApiKey userApiKey);
+
+ @Update("UPDATE wvp_user_api_key SET enable = true WHERE id = #{id}")
+ int enable(@Param("id") int id);
+
+ @Update("UPDATE wvp_user_api_key SET enable = false WHERE id = #{id}")
+ int disable(@Param("id") int id);
+
+ @Update("UPDATE wvp_user_api_key SET api_key = #{apiKey} WHERE id = #{id}")
+ int apiKey(@Param("id") int id, @Param("apiKey") String apiKey);
+
+ @Update("UPDATE wvp_user_api_key SET remark = #{remark} WHERE id = #{id}")
+ int remark(@Param("id") int id, @Param("remark") String remark);
+
+ @Delete("DELETE FROM wvp_user_api_key WHERE id = #{id}")
+ int delete(@Param("id") int id);
+
+ @Select("SELECT uak.id, uak.user_id, uak.app, uak.api_key, uak.expired_at, uak.remark, uak.enable, uak.create_time, uak.update_time, u.username AS username FROM wvp_user_api_key uak LEFT JOIN wvp_user u on u.id = uak.user_id WHERE uak.id = #{id}")
+ UserApiKey selectById(@Param("id") int id);
+
+ @Select("SELECT uak.id, uak.user_id, uak.app, uak.api_key, uak.expired_at, uak.remark, uak.enable, uak.create_time, uak.update_time, u.username AS username FROM wvp_user_api_key uak LEFT JOIN wvp_user u on u.id = uak.user_id WHERE uak.api_key = #{apiKey}")
+ UserApiKey selectByApiKey(@Param("apiKey") String apiKey);
+
+ @Select("SELECT uak.id, uak.user_id, uak.app, uak.api_key, uak.expired_at, uak.remark, uak.enable, uak.create_time, uak.update_time, u.username AS username FROM wvp_user_api_key uak LEFT JOIN wvp_user u on u.id = uak.user_id")
+ List<UserApiKey> selectAll();
+
+ @Select("SELECT uak.id, uak.user_id, uak.app, uak.api_key, uak.expired_at, uak.remark, uak.enable, uak.create_time, uak.update_time, u.username AS username FROM wvp_user_api_key uak LEFT JOIN wvp_user u on u.id = uak.user_id")
+ List<UserApiKey> getUserApiKeys();
+
+ @Select("SELECT COUNT(0) FROM wvp_user_api_key WHERE api_key = #{apiKey}")
+ boolean isApiKeyExists(@Param("apiKey") String apiKey);
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/UserApiKey.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/UserApiKey.java
new file mode 100644
index 0000000..470c8b6
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/UserApiKey.java
@@ -0,0 +1,151 @@
+package com.genersoft.iot.vmp.storager.dao.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.io.Serializable;
+
+/**
+ * 鐢ㄦ埛淇℃伅
+ */
+@Schema(description = "鐢ㄦ埛ApiKey淇℃伅")
+public class UserApiKey implements Serializable {
+
+ /**
+ * Id
+ */
+ @Schema(description = "Id")
+ private int id;
+
+ /**
+ * 鐢ㄦ埛Id
+ */
+ @Schema(description = "鐢ㄦ埛Id")
+ private int userId;
+
+ /**
+ * 搴旂敤鍚�
+ */
+ @Schema(description = "搴旂敤鍚�")
+ private String app;
+
+ /**
+ * ApiKey
+ */
+ @Schema(description = "ApiKey")
+ private String apiKey;
+
+ /**
+ * 杩囨湡鏃堕棿锛坣ull=姘镐笉杩囨湡锛�
+ */
+ @Schema(description = "杩囨湡鏃堕棿锛坣ull=姘镐笉杩囨湡锛�")
+ private String expiredAt;
+
+ /**
+ * 澶囨敞淇℃伅
+ */
+ @Schema(description = "澶囨敞淇℃伅")
+ private String remark;
+
+ /**
+ * 鏄惁鍚敤
+ */
+ @Schema(description = "鏄惁鍚敤")
+ private boolean enable;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private String createTime;
+
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ @Schema(description = "鏇存柊鏃堕棿")
+ private String updateTime;
+
+ /**
+ * 鐢ㄦ埛鍚�
+ */
+ private String username;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getUserId() {
+ return userId;
+ }
+
+ public void setUserId(int userId) {
+ this.userId = userId;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public String getApiKey() {
+ return apiKey;
+ }
+
+ public void setApiKey(String apiKey) {
+ this.apiKey = apiKey;
+ }
+
+ public String getExpiredAt() {
+ return expiredAt;
+ }
+
+ public void setExpiredAt(String expiredAt) {
+ this.expiredAt = expiredAt;
+ }
+
+ public String getRemark() {
+ return remark;
+ }
+
+ public void setRemark(String remark) {
+ this.remark = remark;
+ }
+
+ public boolean isEnable() {
+ return enable;
+ }
+
+ public void setEnable(boolean enable) {
+ this.enable = enable;
+ }
+
+ public String getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(String createTime) {
+ this.createTime = createTime;
+ }
+
+ public String getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(String updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserApiKeyController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserApiKeyController.java
new file mode 100644
index 0000000..44690ad
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserApiKeyController.java
@@ -0,0 +1,251 @@
+package com.genersoft.iot.vmp.vmanager.user;
+
+import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.conf.security.JwtUtils;
+import com.genersoft.iot.vmp.conf.security.SecurityUtils;
+import com.genersoft.iot.vmp.service.IUserApiKeyService;
+import com.genersoft.iot.vmp.service.IUserService;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
+import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
+import com.genersoft.iot.vmp.utils.DateUtil;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
+import com.github.pagehelper.PageInfo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Tag(name = "鐢ㄦ埛ApiKey绠$悊")
+@RestController
+@RequestMapping("/api/userApiKey")
+public class UserApiKeyController {
+
+ public static final int EXPIRATION_TIME = Integer.MAX_VALUE;
+ @Autowired
+ private IUserService userService;
+
+ @Autowired
+ private IUserApiKeyService userApiKeyService;
+
+ /**
+ * 娣诲姞鐢ㄦ埛ApiKey
+ *
+ * @param userId
+ * @param app
+ * @param remark
+ * @param expiresAt
+ * @param enable
+ */
+ @PostMapping("/add")
+ @Operation(summary = "娣诲姞鐢ㄦ埛ApiKey", security = @SecurityRequirement(name = JwtUtils.HEADER))
+ @Parameter(name = "userId", description = "鐢ㄦ埛Id", required = true)
+ @Parameter(name = "app", description = "搴旂敤鍚嶇О", required = false)
+ @Parameter(name = "remark", description = "澶囨敞淇℃伅", required = false)
+ @Parameter(name = "expiredAt", description = "杩囨湡鏃堕棿锛堜笉浼犱唬琛ㄦ案涓嶈繃鏈燂級", required = false)
+ @Transactional
+ public synchronized void add(
+ @RequestParam(required = true) int userId,
+ @RequestParam(required = false) String app,
+ @RequestParam(required = false) String remark,
+ @RequestParam(required = false) String expiresAt,
+ @RequestParam(required = false) Boolean enable
+ ) {
+ User user = userService.getUserById(userId);
+ if (user == null) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "鐢ㄦ埛涓嶅瓨鍦�");
+ }
+
+ Long expirationTime = null;
+ if (expiresAt != null) {
+ long timestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(expiresAt);
+ expirationTime = (timestamp - System.currentTimeMillis()) / (60 * 1000);
+ if (expirationTime < 0) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "杩囨湡鏃堕棿涓嶈兘鏃╀簬褰撳墠鏃堕棿");
+ }
+ }
+
+ UserApiKey userApiKey = new UserApiKey();
+ userApiKey.setUserId(userId);
+ userApiKey.setApp(app);
+ userApiKey.setApiKey(null);
+ userApiKey.setRemark(remark);
+ userApiKey.setExpiredAt(expiresAt);
+ userApiKey.setEnable(enable != null ? enable : false);
+ userApiKey.setCreateTime(DateUtil.getNow());
+ userApiKey.setUpdateTime(DateUtil.getNow());
+
+ int addResult = userApiKeyService.addApiKey(userApiKey);
+
+ if (addResult <= 0) {
+ throw new ControllerException(ErrorCode.ERROR100);
+ }
+
+ String apiKey;
+ do {
+ Map<String, Object> extra = new HashMap<>(1);
+ extra.put("apiKeyId", userApiKey.getId());
+ apiKey = JwtUtils.createToken(user.getUsername(), expirationTime, extra);
+ } while (userApiKeyService.isApiKeyExists(apiKey));
+
+ int resetResult = userApiKeyService.reset(userApiKey.getId(), apiKey);
+
+ if (resetResult <= 0) {
+ throw new ControllerException(ErrorCode.ERROR100);
+ }
+ }
+
+ /**
+ * 鍒嗛〉鏌ヨApiKey
+ *
+ * @param page 褰撳墠椤�
+ * @param count 姣忛〉鏌ヨ鏁伴噺
+ * @return 鍒嗛〉ApiKey鍒楄〃
+ */
+ @GetMapping("/userApiKeys")
+ @Operation(summary = "鍒嗛〉鏌ヨ鐢ㄦ埛", security = @SecurityRequirement(name = JwtUtils.HEADER))
+ @Parameter(name = "page", description = "褰撳墠椤�", required = true)
+ @Parameter(name = "count", description = "姣忛〉鏌ヨ鏁伴噺", required = true)
+ @Transactional
+ public PageInfo<UserApiKey> userApiKeys(@RequestParam(required = true) int page, @RequestParam(required = true) int count) {
+ return userApiKeyService.getUserApiKeys(page, count);
+ }
+
+ @PostMapping("/enable")
+ @Operation(summary = "鍚敤鐢ㄦ埛ApiKey", security = @SecurityRequirement(name = JwtUtils.HEADER))
+ @Parameter(name = "id", description = "鐢ㄦ埛ApiKeyId", required = true)
+ @Transactional
+ public void enable(@RequestParam(required = true) Integer id) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛id
+ int currenRoleId = SecurityUtils.getUserInfo().getRole().getId();
+ if (currenRoleId != 1) {
+ // 鍙敤瑙掕壊id涓�1鎵嶅彲浠ョ鐞哢serApiKey
+ throw new ControllerException(ErrorCode.ERROR403);
+ }
+ UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(id);
+ if (userApiKey == null) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "ApiKey涓嶅瓨鍦�");
+ }
+
+ int enableResult = userApiKeyService.enable(id);
+
+ if (enableResult <= 0) {
+ throw new ControllerException(ErrorCode.ERROR100);
+ }
+ }
+
+ @PostMapping("/disable")
+ @Operation(summary = "鍋滅敤鐢ㄦ埛ApiKey", security = @SecurityRequirement(name = JwtUtils.HEADER))
+ @Parameter(name = "id", description = "鐢ㄦ埛ApiKeyId", required = true)
+ @Transactional
+ public void disable(@RequestParam(required = true) Integer id) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛id
+ int currenRoleId = SecurityUtils.getUserInfo().getRole().getId();
+ if (currenRoleId != 1) {
+ // 鍙敤瑙掕壊id涓�1鎵嶅彲浠ョ鐞哢serApiKey
+ throw new ControllerException(ErrorCode.ERROR403);
+ }
+ UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(id);
+ if (userApiKey == null) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "ApiKey涓嶅瓨鍦�");
+ }
+
+ int disableResult = userApiKeyService.disable(id);
+
+ if (disableResult <= 0) {
+ throw new ControllerException(ErrorCode.ERROR100);
+ }
+ }
+
+ @PostMapping("/reset")
+ @Operation(summary = "閲嶇疆鐢ㄦ埛ApiKey", security = @SecurityRequirement(name = JwtUtils.HEADER))
+ @Parameter(name = "id", description = "鐢ㄦ埛ApiKeyId", required = true)
+ @Transactional
+ public void reset(@RequestParam(required = true) Integer id) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛id
+ int currenRoleId = SecurityUtils.getUserInfo().getRole().getId();
+ if (currenRoleId != 1) {
+ // 鍙敤瑙掕壊id涓�1鎵嶅彲浠ョ鐞哢serApiKey
+ throw new ControllerException(ErrorCode.ERROR403);
+ }
+ UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(id);
+ if (userApiKey == null) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "ApiKey涓嶅瓨鍦�");
+ }
+ User user = userService.getUserById(userApiKey.getUserId());
+ if (user == null) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "鐢ㄦ埛涓嶅瓨鍦�");
+ }
+ Long expirationTime = null;
+ if (userApiKey.getExpiredAt() != null) {
+ long timestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(userApiKey.getExpiredAt());
+ expirationTime = (timestamp - System.currentTimeMillis()) / (60 * 1000);
+ if (expirationTime < 0) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "ApiKey宸插け鏁�");
+ }
+ }
+ String apiKey;
+ do {
+ Map<String, Object> extra = new HashMap<>(1);
+ extra.put("apiKeyId", userApiKey.getId());
+ apiKey = JwtUtils.createToken(user.getUsername(), expirationTime, extra);
+ } while (userApiKeyService.isApiKeyExists(apiKey));
+
+ int resetResult = userApiKeyService.reset(id, apiKey);
+
+ if (resetResult <= 0) {
+ throw new ControllerException(ErrorCode.ERROR100);
+ }
+ }
+
+ @PostMapping("/remark")
+ @Operation(summary = "澶囨敞鐢ㄦ埛ApiKey", security = @SecurityRequirement(name = JwtUtils.HEADER))
+ @Parameter(name = "id", description = "鐢ㄦ埛ApiKeyId", required = true)
+ @Parameter(name = "remark", description = "鐢ㄦ埛ApiKey澶囨敞", required = false)
+ @Transactional
+ public void remark(@RequestParam(required = true) Integer id, @RequestParam(required = false) String remark) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛id
+ int currenRoleId = SecurityUtils.getUserInfo().getRole().getId();
+ if (currenRoleId != 1) {
+ // 鍙敤瑙掕壊id涓�1鎵嶅彲浠ョ鐞哢serApiKey
+ throw new ControllerException(ErrorCode.ERROR403);
+ }
+ UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(id);
+ if (userApiKey == null) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "ApiKey涓嶅瓨鍦�");
+ }
+ int remarkResult = userApiKeyService.remark(id, remark);
+
+ if (remarkResult <= 0) {
+ throw new ControllerException(ErrorCode.ERROR100);
+ }
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "鍒犻櫎鐢ㄦ埛ApiKey", security = @SecurityRequirement(name = JwtUtils.HEADER))
+ @Parameter(name = "id", description = "鐢ㄦ埛ApiKeyId", required = true)
+ @Transactional
+ public void delete(@RequestParam(required = true) Integer id) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛id
+ int currenRoleId = SecurityUtils.getUserInfo().getRole().getId();
+ if (currenRoleId != 1) {
+ // 鍙敤瑙掕壊id涓�1鎵嶅彲浠ョ鐞哢serApiKey
+ throw new ControllerException(ErrorCode.ERROR403);
+ }
+ UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(id);
+ if (userApiKey == null) {
+ throw new ControllerException(ErrorCode.ERROR400.getCode(), "ApiKey涓嶅瓨鍦�");
+ }
+
+ int deleteResult = userApiKeyService.delete(id);
+
+ if (deleteResult <= 0) {
+ throw new ControllerException(ErrorCode.ERROR100);
+ }
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 8f9661b..558bd14 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -10,6 +10,8 @@
multipart:
max-file-size: 10MB
max-request-size: 100MB
+ cache:
+ type: redis
# REDIS鏁版嵁搴撻厤缃�
redis:
# [蹇呴』淇敼] Redis鏈嶅姟鍣↖P, REDIS瀹夎鍦ㄦ湰鏈虹殑,浣跨敤127.0.0.1
diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml
index aeabc8d..51f7226 100644
--- a/src/main/resources/application-docker.yml
+++ b/src/main/resources/application-docker.yml
@@ -4,6 +4,8 @@
multipart:
max-file-size: 10MB
max-request-size: 100MB
+ cache:
+ type: redis
# REDIS鏁版嵁搴撻厤缃�
redis:
# [蹇呴』淇敼] Redis鏈嶅姟鍣↖P, REDIS瀹夎鍦ㄦ湰鏈虹殑,浣跨敤127.0.0.1
diff --git a/web_src/config/index.js b/web_src/config/index.js
index 9d24d1b..ad5f940 100644
--- a/web_src/config/index.js
+++ b/web_src/config/index.js
@@ -12,7 +12,7 @@
assetsPublicPath: '/',
proxyTable: {
'/debug': {
- target: 'http://127.0.0.1:18082',
+ target: 'http://127.0.0.1:8080',
changeOrigin: true,
pathRewrite: {
'^/debug': '/'
diff --git a/web_src/src/components/UserApiKeyManager.vue b/web_src/src/components/UserApiKeyManager.vue
new file mode 100644
index 0000000..b3eb80d
--- /dev/null
+++ b/web_src/src/components/UserApiKeyManager.vue
@@ -0,0 +1,296 @@
+<template>
+ <div id="app" style="width: 100%">
+ <div class="page-header" style="margin-bottom: 0">
+ <div class="page-title">
+ <el-page-header @back="goBack" content="ApiKey鍒楄〃"></el-page-header>
+ </div>
+ <div class="page-header-btn">
+ <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addUserApiKey">
+ 娣诲姞ApiKey
+ </el-button>
+ </div>
+ </div>
+ <!--ApiKey鍒楄〃-->
+ <el-table :data="userList" style="width: 100%;font-size: 12px;" :height="winHeight"
+ header-row-class-name="table-header">
+ <el-table-column prop="user.username" label="鐢ㄦ埛鍚�" min-width="120"/>
+ <el-table-column prop="app" label="搴旂敤鍚�" min-width="160"/>
+ <el-table-column prop="apiKey" label="ApiKey" min-width="480"/>
+ <el-table-column prop="enable" label="鍚敤" width="120">
+ <template #default="scope">
+ <el-tag v-if="scope.row.enable">
+ 鍚敤
+ </el-tag>
+ <el-tag v-else type="info">
+ 鍋滅敤
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="expiredAt" label="杩囨湡鏃堕棿" width="160"/>
+ <el-table-column prop="remark" label="澶囨敞淇℃伅" min-width="160"/>
+ <el-table-column label="鎿嶄綔" min-width="160" fixed="right">
+ <template #default="scope">
+ <el-button v-if="scope.row.enable"
+ size="medium" icon="el-icon-circle-close" type="text" @click="disableUserApiKey(scope.row)">
+ 鍋滅敤
+ </el-button>
+ <el-button v-else
+ size="medium" icon="el-icon-circle-check" type="text" @click="enableUserApiKey(scope.row)">
+ 鍚敤
+ </el-button>
+ <el-divider direction="vertical"></el-divider>
+ <el-button size="medium" icon="el-icon-refresh" type="text" @click="resetUserApiKey(scope.row)">
+ 閲嶇疆
+ </el-button>
+ <el-divider direction="vertical"></el-divider>
+ <el-button size="medium" icon="el-icon-edit" type="text" @click="remarkUserApiKey(scope.row)">
+ 澶囨敞
+ </el-button>
+ <el-divider direction="vertical"></el-divider>
+ <el-button size="medium" icon="el-icon-delete" type="text" @click="deleteUserApiKey(scope.row)"
+ style="color: #f56c6c">
+ 鍒犻櫎
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <addUserApiKey ref="addUserApiKey"></addUserApiKey>
+ <remarkUserApiKey ref="remarkUserApiKey"></remarkUserApiKey>
+ <el-pagination
+ style="float: right"
+ @size-change="handleSizeChange"
+ @current-change="currentChange"
+ :current-page="currentPage"
+ :page-size="count"
+ :page-sizes="[15, 25, 35, 50]"
+ layout="total, sizes, prev, pager, next"
+ :total="total">
+ </el-pagination>
+ </div>
+</template>
+
+<script>
+import uiHeader from '../layout/UiHeader.vue'
+import addUserApiKey from "./dialog/addUserApiKey.vue";
+import remarkUserApiKey from './dialog/remarkUserApiKey.vue'
+
+export default {
+ name: 'userApiKeyManager',
+ components: {
+ uiHeader,
+ addUserApiKey,
+ remarkUserApiKey
+ },
+ data() {
+ return {
+ userList: [], //璁惧鍒楄〃
+ currentUser: {}, //褰撳墠鎿嶄綔璁惧瀵硅薄
+ winHeight: window.innerHeight - 200,
+ currentPage: 1,
+ count: 15,
+ total: 0,
+ getUserApiKeyListLoading: false
+ };
+ },
+ mounted() {
+ this.initParam();
+ this.initData();
+ },
+ methods: {
+ goBack() {
+ this.$router.back()
+ },
+ initParam() {
+ this.userId = this.$route.params.userId;
+ },
+ initData() {
+ this.getUserApiKeyList();
+ },
+ currentChange(val) {
+ this.currentPage = val;
+ this.getUserApiKeyList();
+ },
+ handleSizeChange(val) {
+ this.count = val;
+ this.getUserApiKeyList();
+ },
+ getUserApiKeyList() {
+ let that = this;
+ this.getUserApiKeyListLoading = true;
+ this.$axios({
+ method: 'get',
+ url: `/api/userApiKey/userApiKeys`,
+ params: {
+ page: that.currentPage,
+ count: that.count
+ }
+ }).then((res) => {
+ if (res.data.code === 0) {
+ that.total = res.data.data.total;
+ that.userList = res.data.data.list;
+ }
+ that.getUserApiKeyListLoading = false;
+ }).catch((error) => {
+ that.getUserApiKeyListLoading = false;
+ });
+ },
+ addUserApiKey() {
+ this.$refs.addUserApiKey.openDialog(this.userId, () => {
+ this.$refs.addUserApiKey.close();
+ this.$message({
+ showClose: true,
+ message: "ApiKey娣诲姞鎴愬姛",
+ type: "success",
+ });
+ setTimeout(this.getUserApiKeyList, 200)
+ })
+ },
+ remarkUserApiKey(row) {
+ this.$refs.remarkUserApiKey.openDialog(row.id, () => {
+ this.$refs.remarkUserApiKey.close();
+ this.$message({
+ showClose: true,
+ message: "澶囨敞淇敼鎴愬姛",
+ type: "success",
+ });
+ setTimeout(this.getUserApiKeyList, 200)
+ })
+ },
+ enableUserApiKey(row) {
+ let msg = "纭畾鍚敤姝piKey锛�"
+ if (row.online !== 0) {
+ msg = "<strong>纭畾鍚敤姝piKey锛�</strong>"
+ }
+ this.$confirm(msg, '鎻愮ず', {
+ dangerouslyUseHTMLString: true,
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ center: true,
+ type: 'warning'
+ }).then(() => {
+ this.$axios({
+ method: 'post',
+ url: `/api/userApiKey/enable?id=${row.id}`
+ }).then((res) => {
+ this.$message({
+ showClose: true,
+ message: '鍚敤鎴愬姛',
+ type: 'success'
+ });
+ this.getUserApiKeyList();
+ }).catch((error) => {
+ this.$message({
+ showClose: true,
+ message: '鍚敤澶辫触',
+ type: 'error'
+ });
+ console.error(error);
+ });
+ }).catch(() => {
+ });
+ },
+ disableUserApiKey(row) {
+ let msg = "纭畾鍋滅敤姝piKey锛�"
+ if (row.online !== 0) {
+ msg = "<strong>纭畾鍋滅敤姝piKey锛�</strong>"
+ }
+ this.$confirm(msg, '鎻愮ず', {
+ dangerouslyUseHTMLString: true,
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ center: true,
+ type: 'warning'
+ }).then(() => {
+ this.$axios({
+ method: 'post',
+ url: `/api/userApiKey/disable?id=${row.id}`
+ }).then((res) => {
+ this.$message({
+ showClose: true,
+ message: '鍋滅敤鎴愬姛',
+ type: 'success'
+ });
+ this.getUserApiKeyList();
+ }).catch((error) => {
+ this.$message({
+ showClose: true,
+ message: '鍋滅敤澶辫触',
+ type: 'error'
+ });
+ console.error(error);
+ });
+ }).catch(() => {
+ });
+ },
+ resetUserApiKey(row) {
+ let msg = "纭畾閲嶇疆姝piKey锛�"
+ if (row.online !== 0) {
+ msg = "<strong>纭畾閲嶇疆姝piKey锛�</strong>"
+ }
+ this.$confirm(msg, '鎻愮ず', {
+ dangerouslyUseHTMLString: true,
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ center: true,
+ type: 'warning'
+ }).then(() => {
+ this.$axios({
+ method: 'post',
+ url: `/api/userApiKey/reset?id=${row.id}`
+ }).then((res) => {
+ this.$message({
+ showClose: true,
+ message: '閲嶇疆鎴愬姛',
+ type: 'success'
+ });
+ this.getUserApiKeyList();
+ }).catch((error) => {
+ this.$message({
+ showClose: true,
+ message: '閲嶇疆澶辫触',
+ type: 'error'
+ });
+ console.error(error);
+ });
+ }).catch(() => {
+ });
+ },
+ deleteUserApiKey(row) {
+ let msg = "纭畾鍒犻櫎姝piKey锛�"
+ if (row.online !== 0) {
+ msg = "<strong>纭畾鍒犻櫎姝piKey锛�</strong>"
+ }
+ this.$confirm(msg, '鎻愮ず', {
+ dangerouslyUseHTMLString: true,
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ center: true,
+ type: 'warning'
+ }).then(() => {
+ this.$axios({
+ method: 'delete',
+ url: `/api/userApiKey/delete?id=${row.id}`
+ }).then((res) => {
+ this.$message({
+ showClose: true,
+ message: '鍒犻櫎鎴愬姛',
+ type: 'success'
+ });
+ this.getUserApiKeyList();
+ }).catch((error) => {
+ this.$message({
+ showClose: true,
+ message: '鍒犻櫎澶辫触',
+ type: 'error'
+ });
+ console.error(error);
+ });
+ }).catch(() => {
+ });
+ },
+ }
+}
+</script>
+<style>
+
+</style>
diff --git a/web_src/src/components/UserManager.vue b/web_src/src/components/UserManager.vue
index c0fa695..19ba087 100755
--- a/web_src/src/components/UserManager.vue
+++ b/web_src/src/components/UserManager.vue
@@ -23,6 +23,8 @@
<el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-edit" type="text" @click="changePushKey(scope.row)">淇敼pushkey</el-button>
<el-divider direction="vertical"></el-divider>
+ <el-button size="medium" icon="el-icon-edit" type="text" @click="showUserApiKeyManager(scope.row)">绠$悊ApiKey</el-button>
+ <el-divider direction="vertical"></el-divider>
<el-button size="medium" icon="el-icon-delete" type="text" @click="deleteUser(scope.row)"
style="color: #f56c6c">鍒犻櫎
</el-button>
@@ -178,7 +180,10 @@
setTimeout(this.getUserList, 200)
})
- }
+ },
+ showUserApiKeyManager: function (row) {
+ this.$router.push(`/userApiKeyManager/${row.id}`)
+ },
}
}
</script>
diff --git a/web_src/src/components/dialog/addUserApiKey.vue b/web_src/src/components/dialog/addUserApiKey.vue
new file mode 100644
index 0000000..5dd94c0
--- /dev/null
+++ b/web_src/src/components/dialog/addUserApiKey.vue
@@ -0,0 +1,139 @@
+<template>
+ <div id="addUserApiKey" v-loading="isLoading">
+ <el-dialog
+ title="娣诲姞ApiKey"
+ width="40%"
+ top="2rem"
+ :close-on-click-modal="false"
+ :visible.sync="showDialog"
+ :destroy-on-close="true"
+ @close="close()"
+ >
+ <div id="shared" style="margin-right: 20px;">
+ <el-form ref="formRef" :model="form" :rules="rules" status-icon label-width="80px">
+ <el-form-item label="搴旂敤鍚�" prop="app">
+ <el-input
+ v-model="form.app"
+ property="app"
+ autocomplete="off"/>
+ </el-form-item>
+ <el-form-item label="鍚敤鐘舵��" prop="enable" style="text-align: left">
+ <el-switch
+ v-model="form.enable"
+ property="enable"
+ active-text="鍚敤"
+ inactive-text="鍋滅敤"/>
+ </el-form-item>
+ <el-form-item label="杩囨湡鏃堕棿" prop="expiresAt" style="text-align: left">
+ <el-date-picker v-model="form.expiresAt"
+ style="width: 100%"
+ property="expiresAt"
+ type="datetime"
+ value-format="yyyy-MM-dd HH:mm:ss"
+ format="yyyy-MM-dd HH:mm:ss"
+ placeholder="閫夋嫨杩囨湡鏃堕棿"/>
+ </el-form-item>
+ <el-form-item label="澶囨敞淇℃伅" prop="remark">
+ <el-input v-model="form.remark"
+ type="textarea"
+ property="remark"
+ autocomplete="off"
+ :autosize="{ minRows: 5}"
+ maxlength="255"
+ show-word-limit/>
+ </el-form-item>
+ <el-form-item>
+ <div style="float: right;">
+ <el-button type="primary" @click="onSubmit">淇濆瓨</el-button>
+ <el-button @click="close">鍙栨秷</el-button>
+ </div>
+ </el-form-item>
+ </el-form>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+
+export default {
+ name: 'addUserApiKey',
+ props: {},
+ computed: {},
+ created() {
+ },
+ data() {
+ return {
+ userId: null,
+ form: {
+ app: null,
+ enable: true,
+ expiresAt: null,
+ remark: null
+ },
+ rules: {
+ app: [{required: true, trigger: 'blur', message: '搴旂敤鍚嶄笉鑳戒负绌�'}]
+ },
+ listChangeCallback: null,
+ showDialog: false,
+ isLoading: false
+ };
+ },
+ methods: {
+ resetForm() {
+ this.form = {
+ app: null,
+ enable: true,
+ expiresAt: null,
+ remark: null
+ }
+ },
+ openDialog(userId, callback) {
+ this.resetForm()
+ this.userId = userId
+ this.listChangeCallback = callback
+ this.showDialog = true
+ },
+ onSubmit() {
+ this.$refs.formRef.validate((valid) => {
+ if (valid) {
+ this.$axios({
+ method: 'post',
+ url: '/api/userApiKey/add',
+ params: {
+ userId: this.userId,
+ app: this.form.app,
+ enable: this.form.enable,
+ expiresAt: this.form.expiresAt,
+ remark: this.form.remark,
+ }
+ }).then((res) => {
+ if (res.data.code === 0) {
+ this.$message({
+ showClose: true,
+ message: '娣诲姞鎴愬姛',
+ type: 'success'
+ });
+ this.showDialog = false
+ if (this.listChangeCallback) {
+ this.listChangeCallback()
+ }
+ } else {
+ this.$message({
+ showClose: true,
+ message: res.data.msg,
+ type: 'error'
+ });
+ }
+ }).catch((error) => {
+ console.error(error)
+ });
+ }
+ });
+ },
+ close() {
+ this.showDialog = false
+ }
+ },
+};
+</script>
diff --git a/web_src/src/components/dialog/remarkUserApiKey.vue b/web_src/src/components/dialog/remarkUserApiKey.vue
new file mode 100644
index 0000000..6236a2e
--- /dev/null
+++ b/web_src/src/components/dialog/remarkUserApiKey.vue
@@ -0,0 +1,93 @@
+<template>
+ <div id="remarkUserApiKey" v-loading="isLoading">
+ <el-dialog
+ title="ApiKey澶囨敞"
+ width="40%"
+ top="2rem"
+ :close-on-click-modal="false"
+ :visible.sync="showDialog"
+ :destroy-on-close="true"
+ @close="close()"
+ >
+ <div id="shared" style="margin-right: 20px;">
+ <el-form ref="form" :rules="rules" status-icon label-width="80px">
+ <el-form-item label="澶囨敞" prop="oldPassword">
+ <el-input type="textarea" v-model="form.remark" autocomplete="off" :autosize="{ minRows: 5}" maxlength="255" show-word-limit></el-input>
+ </el-form-item>
+ <el-form-item>
+ <div style="float: right;">
+ <el-button type="primary" @click="onSubmit">淇濆瓨</el-button>
+ <el-button @click="close">鍙栨秷</el-button>
+ </div>
+ </el-form-item>
+ </el-form>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+export default {
+ name: "remarkUserApiKey",
+ props: {},
+ computed: {},
+ created() {
+ },
+ data() {
+ return {
+ userApiKeyId: null,
+ form: {
+ remark: null
+ },
+ rules: {},
+ listChangeCallback: null,
+ showDialog: false,
+ isLoading: false
+ };
+ },
+ methods: {
+ resetForm() {
+ this.form = {
+ remark: null
+ }
+ },
+ openDialog(userApiKeyId, callback) {
+ this.resetForm()
+ this.userApiKeyId = userApiKeyId
+ this.listChangeCallback = callback
+ this.showDialog = true
+ },
+ onSubmit() {
+ this.$axios({
+ method: 'post',
+ url: "/api/userApiKey/remark",
+ params: {
+ id: this.userApiKeyId,
+ remark: this.form.remark
+ }
+ }).then((res) => {
+ if (res.data.code === 0) {
+ this.$message({
+ showClose: true,
+ message: '澶囨敞淇敼鎴愬姛!',
+ type: 'success'
+ });
+ this.showDialog = false;
+ this.listChangeCallback()
+ } else {
+ this.$message({
+ showClose: true,
+ message: '澶囨敞淇敼澶辫触',
+ type: 'error'
+ });
+ }
+ }).catch((error) => {
+ console.error(error)
+ });
+ },
+ close() {
+ this.showDialog = false
+ },
+ },
+};
+</script>
diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js
index d5e9f7e..fa96b2b 100755
--- a/web_src/src/router/index.js
+++ b/web_src/src/router/index.js
@@ -20,7 +20,7 @@
import live from '../components/live.vue'
import deviceTree from '../components/common/DeviceTree.vue'
import userManager from '../components/UserManager.vue'
-
+import userApiKeyManager from '../components/UserApiKeyManager.vue'
import wasmPlayer from '../components/common/jessibuca.vue'
import rtcPlayer from '../components/dialog/rtcPlayer.vue'
@@ -125,7 +125,13 @@
path: '/userManager',
name: 'userManager',
component: userManager,
+ },
+ {
+ path: '/userApiKeyManager/:userId',
+ name: 'userApiKeyManager',
+ component: userApiKeyManager,
}
+ ,
]
},
{
diff --git "a/\346\211\223\345\214\205/config/wvp-application.yml" "b/\346\211\223\345\214\205/config/wvp-application.yml"
index 8083e36..71b5da2 100755
--- "a/\346\211\223\345\214\205/config/wvp-application.yml"
+++ "b/\346\211\223\345\214\205/config/wvp-application.yml"
@@ -4,6 +4,8 @@
multipart:
max-file-size: 10MB
max-request-size: 100MB
+ cache:
+ type: redis
# REDIS鏁版嵁搴撻厤缃�
redis:
# [鍙�塢 瓒呮椂鏃堕棿
diff --git "a/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-mysql-2.7.0.sql" "b/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-mysql-2.7.0.sql"
index a3f4a1d..6e1f83b 100644
--- "a/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-mysql-2.7.0.sql"
+++ "b/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-mysql-2.7.0.sql"
@@ -314,6 +314,17 @@
parentId integer,
path character varying(255)
);
+create table wvp_user_api_key (
+ id serial primary key ,
+ user_id bigint,
+ app character varying(255) ,
+ api_key text,
+ expired_at bigint,
+ remark character varying(255),
+ enable bool default true,
+ create_time character varying(50),
+ update_time character varying(50)
+);
/*鍒濆鏁版嵁*/
diff --git "a/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-postgresql-kingbase-2.7.0.sql" "b/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-postgresql-kingbase-2.7.0.sql"
index 9f41667..17ef270 100644
--- "a/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-postgresql-kingbase-2.7.0.sql"
+++ "b/\346\225\260\346\215\256\345\272\223/2.7.0/\345\210\235\345\247\213\345\214\226-postgresql-kingbase-2.7.0.sql"
@@ -314,7 +314,17 @@
parentId integer,
path character varying(255)
);
-
+create table wvp_user_api_key (
+ id serial primary key ,
+ user_id bigint,
+ app character varying(255) ,
+ api_key text,
+ expired_at bigint,
+ remark character varying(255),
+ enable bool default true,
+ create_time character varying(50),
+ update_time character varying(50)
+);
/*鍒濆鏁版嵁*/
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
diff --git "a/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-mysql.sql" "b/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-mysql.sql"
index a3f4a1d..f3247c1 100644
--- "a/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-mysql.sql"
+++ "b/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-mysql.sql"
@@ -314,7 +314,17 @@
parentId integer,
path character varying(255)
);
-
+create table wvp_user_api_key (
+ id serial primary key ,
+ user_id bigint,
+ app character varying(255) ,
+ api_key text,
+ expired_at bigint,
+ remark character varying(255),
+ enable bool default true,
+ create_time character varying(50),
+ update_time character varying(50)
+);
/*鍒濆鏁版嵁*/
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
diff --git "a/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-postgresql-kingbase.sql" "b/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-postgresql-kingbase.sql"
index 9f41667..17ef270 100644
--- "a/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-postgresql-kingbase.sql"
+++ "b/\346\225\260\346\215\256\345\272\223/\345\210\235\345\247\213\345\214\226-postgresql-kingbase.sql"
@@ -314,7 +314,17 @@
parentId integer,
path character varying(255)
);
-
+create table wvp_user_api_key (
+ id serial primary key ,
+ user_id bigint,
+ app character varying(255) ,
+ api_key text,
+ expired_at bigint,
+ remark character varying(255),
+ enable bool default true,
+ create_time character varying(50),
+ update_time character varying(50)
+);
/*鍒濆鏁版嵁*/
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
--
Gitblit v1.8.0