Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
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;
Expand All @@ -46,14 +48,26 @@
@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 {
Expand All @@ -75,10 +89,8 @@ public Map<String, String> authGetToken(LoginDto loginDto) throws Authentication
// 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);
Expand All @@ -90,18 +102,21 @@ public Map<String, String> authGetToken(LoginDto loginDto) throws Authentication
@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);
}

Expand All @@ -113,13 +128,24 @@ public String generateToken() throws AuthenticationException {
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@

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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -136,6 +140,89 @@ void testRefreshTokenWithValidToken() throws Exception {
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
Expand Down
Loading