From 8d5d2eb522fa3ede76246af2cea36e58b52e04f7 Mon Sep 17 00:00:00 2001 From: hengyuss <1183660933@qq.com> Date: Sat, 27 Jun 2026 19:14:09 +0800 Subject: [PATCH] fix: Decouple JWT signing key from user password hash --- .../config/properties/JwtProperties.java | 28 +++++++++++++++++++ .../impl/DashboardUserServiceImpl.java | 2 +- .../shenyu/admin/shiro/config/ShiroRealm.java | 8 ++++-- .../src/main/resources/application.yml | 1 + .../admin/shiro/config/ShiroRealmTest.java | 12 +++++--- .../common/constant/AdminConstants.java | 2 ++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/JwtProperties.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/JwtProperties.java index 3eabd2ae18c7..22a3c9fc2f0a 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/JwtProperties.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/properties/JwtProperties.java @@ -17,10 +17,16 @@ package org.apache.shenyu.admin.config.properties; +import jakarta.annotation.PostConstruct; import org.apache.shenyu.common.constant.AdminConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import java.security.SecureRandom; +import java.util.Base64; + /** * Jwt Properties. */ @@ -28,8 +34,22 @@ @ConfigurationProperties(prefix = "shenyu.jwt") public class JwtProperties { + private static final Logger LOG = LoggerFactory.getLogger(JwtProperties.class); + private Long expiredSeconds = AdminConstants.THE_ONE_DAY_MILLIS_TIME; + private String secretKey = AdminConstants.JWT_DEFAULT_SECRET_KEY; + + @PostConstruct + private void init() { + if (AdminConstants.JWT_DEFAULT_SECRET_KEY.equals(this.secretKey)) { + LOG.warn("jwt secretKey is using the default value, which is not secure. Please configure 'shenyu.jwt.secretKey' in your configuration."); + byte[] key = new byte[32]; + new SecureRandom().nextBytes(key); + this.secretKey = Base64.getEncoder().encodeToString(key); + } + } + /** * Gets the value of expiredSeconds. * @@ -47,4 +67,12 @@ public Long getExpiredSeconds() { public void setExpiredSeconds(final Long expiredSeconds) { this.expiredSeconds = expiredSeconds; } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(final String secretKey) { + this.secretKey = secretKey; + } } diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java index 87ea9546c2da..c8c3879797f4 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DashboardUserServiceImpl.java @@ -311,7 +311,7 @@ public LoginDashboardUserVO login(final String userName, final String password, userDO.setClientId(clientId); dashboardUserMapper.updateSelective(userDO); } - return loginUser.setToken(JwtUtils.generateToken(finalDashboardUserVO.getUserName(), finalDashboardUserVO.getPassword(), + return loginUser.setToken(JwtUtils.generateToken(finalDashboardUserVO.getUserName(), jwtProperties.getSecretKey(), clientId, jwtProperties.getExpiredSeconds())).setExpiredTime(jwtProperties.getExpiredSeconds()); }) .orElse(null); diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java index 2cd6ac14283e..886841358e9d 100644 --- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java +++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java @@ -19,6 +19,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.shenyu.admin.config.properties.JwtProperties; import org.apache.shenyu.admin.model.custom.UserInfo; import org.apache.shenyu.admin.model.vo.DashboardUserVO; import org.apache.shenyu.admin.service.DashboardUserService; @@ -54,9 +55,12 @@ public class ShiroRealm extends AuthorizingRealm { private final DashboardUserService dashboardUserService; - public ShiroRealm(final PermissionService permissionService, final DashboardUserService dashboardUserService) { + private final JwtProperties jwtProperties; + + public ShiroRealm(final PermissionService permissionService, final DashboardUserService dashboardUserService, final JwtProperties jwtProperties) { this.permissionService = permissionService; this.dashboardUserService = dashboardUserService; + this.jwtProperties = jwtProperties; } @Override @@ -110,7 +114,7 @@ protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken a throw new AuthenticationException("clientId is invalid or does not match"); } - if (!JwtUtils.verifyToken(token, dashboardUserVO.getPassword())) { + if (!JwtUtils.verifyToken(token, jwtProperties.getSecretKey())) { throw new AuthenticationException("token is error."); } diff --git a/shenyu-admin/src/main/resources/application.yml b/shenyu-admin/src/main/resources/application.yml index 6d2c3af3bfe2..b5fc4c3e415f 100755 --- a/shenyu-admin/src/main/resources/application.yml +++ b/shenyu-admin/src/main/resources/application.yml @@ -114,6 +114,7 @@ shenyu: object-class: person login-field: cn jwt: + secret-key: "defaultSecretKey" expired-seconds: 86400000 cluster: enabled: false diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/shiro/config/ShiroRealmTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/shiro/config/ShiroRealmTest.java index 845bfe2aab6e..189d131de335 100644 --- a/shenyu-admin/src/test/java/org/apache/shenyu/admin/shiro/config/ShiroRealmTest.java +++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/shiro/config/ShiroRealmTest.java @@ -17,6 +17,7 @@ package org.apache.shenyu.admin.shiro.config; +import org.apache.shenyu.admin.config.properties.JwtProperties; import org.apache.shenyu.admin.model.custom.UserInfo; import org.apache.shenyu.admin.model.vo.DashboardUserVO; import org.apache.shenyu.admin.service.DashboardUserService; @@ -54,8 +55,8 @@ @MockitoSettings(strictness = Strictness.LENIENT) public final class ShiroRealmTest { - private static final String PASSWORD = "123456"; - + private static final String SECRETKEY = "123456"; + @InjectMocks private ShiroRealm shiroRealm; @@ -65,6 +66,9 @@ public final class ShiroRealmTest { @Mock private DashboardUserService dashboardUserService; + @Mock + private JwtProperties jwtProperties; + @Test public void testSupports() { BearerToken token = mock(BearerToken.class); @@ -115,7 +119,7 @@ public void testDoGetAuthenticationInfo() { AuthenticationException exception3 = assertThrows(AuthenticationException.class, () -> { when(dashboardUserService.findByUserName(any())).thenReturn(dashboardUserVO); - when(dashboardUserVO.getPassword()).thenReturn(PASSWORD); + when(jwtProperties.getSecretKey()).thenReturn(SECRETKEY); when(token.getCredentials()).thenReturn("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" + ".eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlck5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0" + ".vZiLpzbncmNC5KL1idgfapCOTsRC0h_5XnqxaGfcLlM"); @@ -124,7 +128,7 @@ public void testDoGetAuthenticationInfo() { assertNotNull(exception3); when(dashboardUserService.findByUserName(any())).thenReturn(dashboardUserVO); - when(dashboardUserVO.getPassword()).thenReturn(PASSWORD); + when(jwtProperties.getSecretKey()).thenReturn(SECRETKEY); when(token.getCredentials()).thenReturn("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" + ".eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlck5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0" + ".Qlpf6FdKAffgceukbi2BQYdPVf71d4Nwy0YQlkiTQFc"); diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java index c73b7404549e..a160077bd561 100644 --- a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java +++ b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java @@ -306,5 +306,7 @@ public final class AdminConstants { public static final long FIVE_SECONDS_MILLIS_TIME = 5 * 1000L; public static final long TEN_SECONDS_MILLIS_TIME = 10 * 1000L; + + public static final String JWT_DEFAULT_SECRET_KEY = "defaultSecretKey"; }