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