This is an automated email from the ASF dual-hosted git repository.
zhaoqingran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/master by this push:
new d1e28e7621 [improve] improve the hierarchical alignment of token
issuance (#4076)
d1e28e7621 is described below
commit d1e28e76219c061c55c3af7ab2da9e9ce902571a
Author: Duansg <[email protected]>
AuthorDate: Wed Mar 18 09:06:07 2026 +0800
[improve] improve the hierarchical alignment of token issuance (#4076)
---
.../manager/service/impl/AccountServiceImpl.java | 54 +++++++++----
.../manager/service/AccountServiceTest.java | 93 +++++++++++++++++++++-
2 files changed, 130 insertions(+), 17 deletions(-)
diff --git
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AccountServiceImpl.java
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AccountServiceImpl.java
index aa6a11284b..3b74469764 100644
---
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AccountServiceImpl.java
+++
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AccountServiceImpl.java
@@ -25,10 +25,12 @@ import com.usthe.sureness.util.JsonWebTokenUtil;
import com.usthe.sureness.util.Md5Util;
import com.usthe.sureness.util.SurenessContextHolder;
import io.jsonwebtoken.Claims;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.naming.AuthenticationException;
+
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.util.JsonUtil;
@@ -46,14 +48,26 @@ import org.springframework.stereotype.Service;
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class AccountServiceImpl implements AccountService {
+
+ private static final String REFRESH_CLAIM = "refresh";
+
/**
* Token validity time in seconds
*/
private static final long PERIOD_TIME = 3600L;
+
/**
* account data provider
*/
- private final SurenessAccountProvider accountProvider = new
DocumentAccountProvider();
+ private final SurenessAccountProvider accountProvider;
+
+ public AccountServiceImpl() {
+ this(new DocumentAccountProvider());
+ }
+
+ public AccountServiceImpl(SurenessAccountProvider accountProvider) {
+ this.accountProvider = accountProvider;
+ }
@Override
public Map<String, String> authGetToken(LoginDto loginDto) throws
AuthenticationException {
@@ -75,10 +89,8 @@ public class AccountServiceImpl implements AccountService {
// Get the roles the user has - rbac
List<String> roles = account.getOwnRoles();
// Issue TOKEN
- String issueToken =
JsonWebTokenUtil.issueJwt(loginDto.getIdentifier(), PERIOD_TIME, roles);
- Map<String, Object> customClaimMap = new HashMap<>(1);
- customClaimMap.put("refresh", true);
- String issueRefresh =
JsonWebTokenUtil.issueJwt(loginDto.getIdentifier(), PERIOD_TIME << 5,
customClaimMap);
+ String issueToken = issueAccessToken(loginDto.getIdentifier(), roles,
PERIOD_TIME);
+ String issueRefresh = issueRefreshToken(loginDto.getIdentifier(),
PERIOD_TIME << 5);
Map<String, String> resp = new HashMap<>(2);
resp.put("token", issueToken);
resp.put("refreshToken", issueRefresh);
@@ -90,18 +102,21 @@ public class AccountServiceImpl implements AccountService {
@Override
public RefreshTokenResponse refreshToken(String refreshToken) throws
Exception {
Claims claims = JsonWebTokenUtil.parseJwt(refreshToken);
- String userId = String.valueOf(claims.getSubject());
- boolean isRefresh = claims.get("refresh", Boolean.class);
- if (StringUtils.isBlank(userId) || !isRefresh) {
+ String userId = claims.getSubject();
+ Boolean isRefresh = claims.get(REFRESH_CLAIM, Boolean.class);
+ if (StringUtils.isBlank(userId) || !Boolean.TRUE.equals(isRefresh)) {
throw new AuthenticationException("Illegal Refresh Token");
}
SurenessAccount account = accountProvider.loadAccount(userId);
if (account == null) {
throw new AuthenticationException("Not Exists This Token Mapping
Account");
}
+ if (account.isDisabledAccount() || account.isExcessiveAttempts()) {
+ throw new AuthenticationException("Expired or Illegal Account");
+ }
List<String> roles = account.getOwnRoles();
- String issueToken = issueToken(userId, roles, PERIOD_TIME);
- String issueRefresh = issueToken(userId, roles, PERIOD_TIME << 5);
+ String issueToken = issueAccessToken(userId, roles, PERIOD_TIME);
+ String issueRefresh = issueRefreshToken(userId, PERIOD_TIME << 5);
return new RefreshTokenResponse(issueToken, issueRefresh);
}
@@ -113,13 +128,24 @@ public class AccountServiceImpl implements AccountService
{
if (account == null) {
throw new AuthenticationException("Not Exists This Token Mapping
Account");
}
+ if (account.isDisabledAccount() || account.isExcessiveAttempts()) {
+ throw new AuthenticationException("Expired or Illegal Account");
+ }
List<String> roles = account.getOwnRoles();
- return issueToken(userId, roles, null);
+ return issueApiToken(userId, roles);
}
- private String issueToken(String userId, List<String> roles, Long
expirationMillis) {
+ private String issueAccessToken(String userId, List<String> roles, Long
expirationMillis) {
+ return JsonWebTokenUtil.issueJwt(userId, expirationMillis, roles, new
HashMap<>(0));
+ }
+
+ private String issueRefreshToken(String userId, Long expirationMillis) {
Map<String, Object> customClaimMap = new HashMap<>(1);
- customClaimMap.put("refresh", true);
- return JsonWebTokenUtil.issueJwt(userId, expirationMillis, roles,
customClaimMap);
+ customClaimMap.put(REFRESH_CLAIM, true);
+ return JsonWebTokenUtil.issueJwt(userId, expirationMillis,
customClaimMap);
+ }
+
+ private String issueApiToken(String userId, List<String> roles) {
+ return issueAccessToken(userId, roles, null);
}
}
diff --git
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AccountServiceTest.java
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AccountServiceTest.java
index c4804a116a..3848af58c4 100644
---
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AccountServiceTest.java
+++
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AccountServiceTest.java
@@ -19,14 +19,18 @@ package org.apache.hertzbeat.manager.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.usthe.sureness.provider.DefaultAccount;
import com.usthe.sureness.provider.SurenessAccount;
import com.usthe.sureness.provider.SurenessAccountProvider;
-import com.usthe.sureness.provider.ducument.DocumentAccountProvider;
+import com.usthe.sureness.subject.SubjectSum;
import com.usthe.sureness.util.JsonWebTokenUtil;
import com.usthe.sureness.util.Md5Util;
+import com.usthe.sureness.util.SurenessContextHolder;
+import io.jsonwebtoken.Claims;
import io.jsonwebtoken.MalformedJwtException;
import java.util.Collections;
import java.util.List;
@@ -65,8 +69,8 @@ class AccountServiceTest {
@BeforeEach
void setUp() {
- accountProvider = mock(DocumentAccountProvider.class);
- accountService = new AccountServiceImpl();
+ accountProvider = mock(SurenessAccountProvider.class);
+ accountService = new AccountServiceImpl(accountProvider);
JsonWebTokenUtil.setDefaultSecretKey(jwt);
}
@@ -136,6 +140,89 @@ class AccountServiceTest {
assertNotNull(response);
assertNotNull(response.getToken());
assertNotNull(response.getRefreshToken());
+ Claims accessClaims = JsonWebTokenUtil.parseJwt(response.getToken());
+ Claims refreshClaims =
JsonWebTokenUtil.parseJwt(response.getRefreshToken());
+ assertNull(accessClaims.get("refresh", Boolean.class));
+ assertEquals(Boolean.TRUE, refreshClaims.get("refresh",
Boolean.class));
+ }
+
+ @Test
+ void testRefreshTokenRejectsAccessToken() {
+ String userId = "admin";
+ String accessToken = JsonWebTokenUtil.issueJwt(userId, 3600L, roles);
+
+ Assertions.assertThrows(
+ AuthenticationException.class,
+ () -> accountService.refreshToken(accessToken)
+ );
+ }
+
+ @Test
+ void testRefreshTokenRejectsDisabledAccount() {
+ String userId = "admin";
+ String refreshToken = JsonWebTokenUtil.issueJwt(userId, 3600L,
Collections.singletonMap("refresh", true));
+ SurenessAccount account = DefaultAccount.builder("app1")
+ .setPassword(Md5Util.md5(password + salt))
+ .setSalt(salt)
+ .setOwnRoles(roles)
+ .setDisabledAccount(Boolean.TRUE)
+ .setExcessiveAttempts(Boolean.FALSE)
+ .build();
+ when(accountProvider.loadAccount(userId)).thenReturn(account);
+
+ Assertions.assertThrows(
+ AuthenticationException.class,
+ () -> accountService.refreshToken(refreshToken)
+ );
+ }
+
+ @Test
+ void testGenerateTokenCannotRefresh() throws Exception {
+ SurenessAccount account = DefaultAccount.builder("app1")
+ .setPassword(Md5Util.md5(password + salt))
+ .setSalt(salt)
+ .setOwnRoles(roles)
+ .setDisabledAccount(Boolean.FALSE)
+ .setExcessiveAttempts(Boolean.FALSE)
+ .build();
+ when(accountProvider.loadAccount(identifier)).thenReturn(account);
+ SubjectSum subjectSum = mock(SubjectSum.class);
+ when(subjectSum.getPrincipal()).thenReturn(identifier);
+
+ try (var mockedStatic = mockStatic(SurenessContextHolder.class)) {
+
mockedStatic.when(SurenessContextHolder::getBindSubject).thenReturn(subjectSum);
+
+ String token = accountService.generateToken();
+
+ assertNull(JsonWebTokenUtil.parseJwt(token).get("refresh",
Boolean.class));
+ Assertions.assertThrows(
+ AuthenticationException.class,
+ () -> accountService.refreshToken(token)
+ );
+ }
+ }
+
+ @Test
+ void testGenerateTokenRejectsDisabledAccount() {
+ SurenessAccount account = DefaultAccount.builder("app1")
+ .setPassword(Md5Util.md5(password + salt))
+ .setSalt(salt)
+ .setOwnRoles(roles)
+ .setDisabledAccount(Boolean.TRUE)
+ .setExcessiveAttempts(Boolean.FALSE)
+ .build();
+ when(accountProvider.loadAccount(identifier)).thenReturn(account);
+ SubjectSum subjectSum = mock(SubjectSum.class);
+ when(subjectSum.getPrincipal()).thenReturn(identifier);
+
+ try (var mockedStatic = mockStatic(SurenessContextHolder.class)) {
+
mockedStatic.when(SurenessContextHolder::getBindSubject).thenReturn(subjectSum);
+
+ Assertions.assertThrows(
+ AuthenticationException.class,
+ () -> accountService.generateToken()
+ );
+ }
}
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]