From de891cbd6c689a492c06b3aefecbbcb1e931ac8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A3=B7=E5=8F=B6?= <14103883+leaf-phos@user.noreply.gitee.com> Date: Fri, 16 May 2025 11:16:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BD=91=E5=85=B3=E5=AF=B9?= =?UTF-8?q?=E5=A4=96API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/common/constant/CacheConstants.java | 5 + .../com/ruoyi/common/constant/Constants.java | 10 ++ .../ruoyi/common/core/domain/entity/User.java | 4 + .../com/ruoyi/common/enums/UserStatus.java | 4 + .../filter/JwtAuthenticationTokenFilter.java | 95 +++++++++++++++---- .../ruoyi/system/user/mapper/UserMapper.xml | 7 ++ .../user/service/impl/UserConverterImpl.java | 2 + .../user/service/impl/UserServiceImpl.java | 28 ++++-- .../ruoyi/web/system/SysUserController.java | 1 + .../src/main/resources/application-env.yml | 3 +- ruoyi-web/src/main/resources/application.yml | 4 +- 11 files changed, 138 insertions(+), 25 deletions(-) diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java index ebccc06..f9d669c 100644 --- a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -117,4 +117,9 @@ public class CacheConstants * 所有硬件版本名称 */ public static final String ALL_HARDWARE_VERSION_NAME = "all_hardware_version_name"; + + /** + * API用户ID前缀 + */ + public static final String API_USER_ID_PREFIX = "api_user_id:"; } diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index 4892307..3e41389 100644 --- a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -184,4 +184,14 @@ public class Constants * 账号密码登录 */ public static final String LOGIN_TYPE_PASSWORD = "LOGIN_TYPE_PASSWORD"; + + /** + * 用户ID + */ + public static final String HEADER_USER_ID = "CT-USER-ID"; + + /** + * API秘钥 + */ + public static final String HEADER_API_SECRET = "CT-API-SECRET"; } diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/User.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/User.java index 2a3a3d8..00810dc 100644 --- a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/User.java +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/User.java @@ -130,6 +130,10 @@ public class User extends BaseEntity @ApiModelProperty("分成延迟到账时间(小时)") private Integer bonusDelay; + @ApiModelProperty("api访问秘钥") + @Sensitive(desensitizedType = DesensitizedType.PASSWORD) + private String apiSecret; + public User(Long userId) { this.userId = userId; } diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java index d7ff44a..7d36f29 100644 --- a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -27,4 +27,8 @@ public enum UserStatus { return info; } + + public static boolean isEnabled(String status) { + return OK.getCode().equals(status); + } } diff --git a/common-ruoyi/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/common-ruoyi/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java index 188e24f..0ccc6b6 100644 --- a/common-ruoyi/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java +++ b/common-ruoyi/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -1,9 +1,13 @@ package com.ruoyi.framework.security.filter; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.TokenService; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -11,11 +15,17 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.domain.vo.UserVO; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.user.service.UserService; /** * token过滤器 验证token有效性 @@ -28,18 +38,71 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter @Autowired private TokenService tokenService; + @Autowired + private RedisCache redisCache; + + @Autowired + private UserService userService; + + @Autowired + private SysPermissionService permissionService; + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { - LoginUser loginUser = tokenService.getLoginUser(request); - if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) - { - tokenService.verifyToken(loginUser); - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authenticationToken); + if (StringUtils.isNull(SecurityUtils.getAuthentication())) { + + // 优先尝试使用token来获取用户信息 + LoginUser loginUser = tokenService.getLoginUser(request); + if (loginUser != null) { + tokenService.verifyToken(loginUser); + } + // 若token未获取到信息,则使用accessKey来获取用户信息 + else { + loginUser = this.getLoginUserByAccessKey(request); + } + + if (StringUtils.isNotNull(loginUser)) { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } } chain.doFilter(request, response); } + + + private LoginUser getLoginUserByAccessKey(HttpServletRequest request) { + String userId = request.getHeader(Constants.HEADER_USER_ID); + String apiSecret = request.getHeader(Constants.HEADER_API_SECRET); + if (StringUtils.isBlank(userId) || StringUtils.isBlank(apiSecret)) { + return null; + } + // 从缓存中获取数据 + String redisKey = CacheConstants.API_USER_ID_PREFIX + userId; + LoginUser loginUser = redisCache.getCacheObject(redisKey); + + // 缓存未命中,查询获取数据 + if (loginUser == null) { + + // 获取用户信息 + UserVO user = userService.selectUserById(Long.parseLong(userId)); + if (user == null || !UserStatus.isEnabled(user.getStatus())) { + return null; + } + loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + + // 放入缓存中 + redisCache.setCacheObject(redisKey, loginUser, 30, TimeUnit.MINUTES); + } + + // 校验秘钥 + if (!SecurityUtils.matchesPassword(apiSecret, loginUser.getUser().getApiSecret())) { + redisCache.deleteObject(redisKey); + return null; + } + + return loginUser; + } } diff --git a/ruoyi-service/src/main/java/com/ruoyi/system/user/mapper/UserMapper.xml b/ruoyi-service/src/main/java/com/ruoyi/system/user/mapper/UserMapper.xml index 0dded07..688c4b5 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/system/user/mapper/UserMapper.xml +++ b/ruoyi-service/src/main/java/com/ruoyi/system/user/mapper/UserMapper.xml @@ -67,6 +67,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" u.withdraw_service_value, u.area_id, u.bonus_delay, + u.api_secret, d.dept_id, d.parent_id, d.ancestors, @@ -190,6 +191,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND date(u.create_time) >= #{createDateRange[0]} AND date(u.create_time) <= #{createDateRange[1]} + + AND u.api_secret like concat('%', #{apiSecret}, '%') + ${params.dataScope} @@ -290,6 +294,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" withdraw_service_value, area_id, bonus_delay, + api_secret, create_time )values( #{userId}, @@ -315,6 +320,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{withdrawServiceValue}, #{areaId}, #{bonusDelay}, + #{apiSecret}, sysdate() ) @@ -346,6 +352,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" withdraw_service_value = #{withdrawServiceValue}, area_id = #{areaId}, bonus_delay = #{bonusDelay}, + api_secret = #{apiSecret}, update_time = sysdate() where user_id = #{userId} diff --git a/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserConverterImpl.java b/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserConverterImpl.java index feb6b6a..070483a 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserConverterImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserConverterImpl.java @@ -27,6 +27,7 @@ public class UserConverterImpl implements UserConverter { vo.setSex(data.getSex()); vo.setAvatar(data.getAvatar()); vo.setStatus(data.getStatus()); + vo.setApiSecret(data.getApiSecret()); return vo; } @@ -49,6 +50,7 @@ public class UserConverterImpl implements UserConverter { vo.setSex(data.getSex()); vo.setAvatar(data.getAvatar()); vo.setStatus(data.getStatus()); + vo.setApiSecret(data.getApiSecret()); return vo; } diff --git a/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserServiceImpl.java index 97a965d..664e853 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserServiceImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/system/user/service/impl/UserServiceImpl.java @@ -278,6 +278,9 @@ public class UserServiceImpl implements UserService if (user.getPassword() != null) { user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); } + if (user.getApiSecret() != null) { + user.setApiSecret(SecurityUtils.encryptPassword(user.getApiSecret())); + } // 设置默认信息 this.setDefaultInfo(user); @@ -294,7 +297,7 @@ public class UserServiceImpl implements UserService // 后校验 this.afterCheck(user.getUserId()); - this.clearCache(); + this.clearCache(user.getUserId()); } return rows; @@ -317,8 +320,15 @@ public class UserServiceImpl implements UserService } } - void clearCache() { + private void clearCache() { + clearCache(null); + } + + private void clearCache(Long userId) { redisCache.deleteObject(CacheConstants.USER_NAME_LIST); + if (userId != null) { + redisCache.deleteObject(CacheConstants.API_USER_ID_PREFIX + userId); + } } @@ -357,6 +367,10 @@ public class UserServiceImpl implements UserService "修改用户'" + user.getUserName() + "'失败,手机号码已存在"); ServiceUtil.assertion(StringUtils.isNotEmpty(user.getEmail()) && !this.checkEmailUnique(user), "修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + + if (user.getApiSecret() != null) { + user.setApiSecret(SecurityUtils.encryptPassword(user.getApiSecret())); + } Integer result = transactionTemplate.execute(status -> { // 删除用户与角色关联 @@ -373,7 +387,7 @@ public class UserServiceImpl implements UserService // 后校验 this.afterCheck(user.getUserId()); - this.clearCache(); + this.clearCache(user.getUserId()); } return rows; @@ -413,7 +427,7 @@ public class UserServiceImpl implements UserService int rows = userMapper.updateUser(user); if (rows > 0) { - clearCache(); + clearCache(user.getUserId()); } return rows; @@ -562,7 +576,7 @@ public class UserServiceImpl implements UserService int rows = userMapper.deleteUserById(userId); if (rows > 0) { - clearCache(); + clearCache(userId); } return rows; @@ -590,7 +604,9 @@ public class UserServiceImpl implements UserService int rows = userMapper.deleteUserByIds(userIds); if (rows > 0) { - clearCache(); + for (Long userId : userIds) { + clearCache(userId); + } } return rows; diff --git a/ruoyi-web/src/main/java/com/ruoyi/web/system/SysUserController.java b/ruoyi-web/src/main/java/com/ruoyi/web/system/SysUserController.java index c3fc523..f4b48ab 100644 --- a/ruoyi-web/src/main/java/com/ruoyi/web/system/SysUserController.java +++ b/ruoyi-web/src/main/java/com/ruoyi/web/system/SysUserController.java @@ -134,6 +134,7 @@ public class SysUserController extends BaseController if (StringUtils.isNotNull(userId)) { UserVO user = userService.selectUserById(userId); + user.setApiSecret(null); ajax.put(AjaxResult.DATA_TAG, user); ajax.put("postIds", postService.selectPostListByUserId(userId)); ajax.put("roleIds", user.getRoles().stream().map(Role::getRoleId).collect(Collectors.toList())); diff --git a/ruoyi-web/src/main/resources/application-env.yml b/ruoyi-web/src/main/resources/application-env.yml index 3a8096b..875ffe1 100644 --- a/ruoyi-web/src/main/resources/application-env.yml +++ b/ruoyi-web/src/main/resources/application-env.yml @@ -6,7 +6,7 @@ spring: # 端口,默认为6379 port: 6379 # 数据库索引 - database: 8 + database: 9 # 密码 password: # 连接超时时间 @@ -45,3 +45,4 @@ pay: # 活体检测 liveness: returnUrl: http://192.168.2.99:4100/liveness + diff --git a/ruoyi-web/src/main/resources/application.yml b/ruoyi-web/src/main/resources/application.yml index bd0d716..028b621 100644 --- a/ruoyi-web/src/main/resources/application.yml +++ b/ruoyi-web/src/main/resources/application.yml @@ -51,7 +51,7 @@ spring: devtools: restart: # 热部署开关 - enabled: true + enabled: false # token配置 token: @@ -132,7 +132,7 @@ iot: # token过期时间 daysToExpire: 100 # 推送消息token - token: tVpNdGKrAFHfKZNgpIWQfZukrcYHNfFM + token: tVpNdGKrAFHfKZNgpIWQfZukrcYHNfFM # 产品ID productId: Kef0Ssgz72