mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 09:40:58 +00:00
[improve] improve the hierarchical alignment of token issuance (#4076)
This commit is contained in:
+40
-14
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+90
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user