Skip to content

Commit

Permalink
[ISSUE #12719] refresh the client's access token
Browse files Browse the repository at this point in the history
[ISSUE #12719] refresh the client's access token

[ISSUE #12719] refresh the client's access token
  • Loading branch information
lucky8987 committed Oct 18, 2024
1 parent 2b178be commit 96d5095
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class NacosAuthLoginConstant {
public static final String COLON = ":";

public static final String SERVER = "server";



public static final int RELOGIN_CODE = 403;

public static final String NEXTREFRESHTIME = "nextRefreshTime";
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.auth.impl.process.HttpLoginProcessor;
import com.alibaba.nacos.common.utils.NumberUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
import com.alibaba.nacos.plugin.auth.api.RequestResource;
Expand Down Expand Up @@ -58,8 +59,8 @@ public class NacosClientAuthServiceImpl extends AbstractClientAuthService {
* A context to take with when sending request to Nacos server.
*/
private volatile LoginIdentityContext loginIdentityContext = new LoginIdentityContext();


/**
* Login to servers.
*
Expand All @@ -69,16 +70,19 @@ public class NacosClientAuthServiceImpl extends AbstractClientAuthService {
@Override
public Boolean login(Properties properties) {
try {
if ((System.currentTimeMillis() - lastRefreshTime) < TimeUnit.SECONDS
.toMillis(tokenTtl - tokenRefreshWindow)) {

String nextRefreshTimeStr = loginIdentityContext.getParameter(NacosAuthLoginConstant.NEXTREFRESHTIME);
long nextRefreshTime = NumberUtils.toLong(nextRefreshTimeStr, 0);

if (System.currentTimeMillis() < nextRefreshTime) {
return true;
}

if (StringUtils.isBlank(properties.getProperty(PropertyKeyConst.USERNAME))) {
lastRefreshTime = System.currentTimeMillis();
loginIdentityContext.setParameter(NacosAuthLoginConstant.NEXTREFRESHTIME, "0");
return true;
}

for (String server : this.serverList) {
HttpLoginProcessor httpLoginProcessor = new HttpLoginProcessor(nacosRestTemplate);
properties.setProperty(NacosAuthLoginConstant.SERVER, server);
Expand All @@ -87,11 +91,11 @@ public Boolean login(Properties properties) {
if (identityContext.getAllKey().contains(NacosAuthLoginConstant.ACCESSTOKEN)) {
tokenTtl = Long.parseLong(identityContext.getParameter(NacosAuthLoginConstant.TOKENTTL));
tokenRefreshWindow = tokenTtl / 10;
lastRefreshTime = System.currentTimeMillis();

nextRefreshTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(tokenTtl - tokenRefreshWindow);
LoginIdentityContext newCtx = new LoginIdentityContext();
newCtx.setParameter(NacosAuthLoginConstant.ACCESSTOKEN,
identityContext.getParameter(NacosAuthLoginConstant.ACCESSTOKEN));
newCtx.setParameter(NacosAuthLoginConstant.NEXTREFRESHTIME, String.valueOf(nextRefreshTime));
this.loginIdentityContext = newCtx;
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ protected Map<String, String> getAppHeaders() {
result.put(APP_FILED, AppNameUtils.getAppName());
return result;
}

protected void reLogin() {
securityProxy.reLogin();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.alibaba.nacos.api.remote.response.ResponseCode;
import com.alibaba.nacos.api.selector.AbstractSelector;
import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant;
import com.alibaba.nacos.client.env.NacosClientProperties;
import com.alibaba.nacos.client.monitor.MetricsMonitor;
import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder;
Expand Down Expand Up @@ -96,6 +97,7 @@ public class NamingGrpcClientProxy extends AbstractNamingClientProxy {
public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory,
NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder) throws NacosException {
super(securityProxy);
securityProxy.login(properties.asProperties());
this.namespaceId = namespaceId;
this.uuid = UUID.randomUUID().toString();
this.requestTimeout = Long.parseLong(properties.getProperty(CommonParams.NAMING_REQUEST_TIMEOUT, "-1"));
Expand Down Expand Up @@ -446,6 +448,10 @@ private <T extends Response> T requestToServer(AbstractNamingRequest request, Cl
getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName()));
response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout);
if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {
// If the 403 login operation is triggered, refresh the accessToken of the client
if (NacosAuthLoginConstant.RELOGIN_CODE == response.getErrorCode()) {
reLogin();
}
throw new NacosException(response.getErrorCode(), response.getMessage());
}
if (responseClass.isAssignableFrom(response.getClass())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.alibaba.nacos.api.selector.ExpressionSelector;
import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.client.address.ServerListChangeEvent;
import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant;
import com.alibaba.nacos.client.env.NacosClientProperties;
import com.alibaba.nacos.client.monitor.MetricsMonitor;
import com.alibaba.nacos.client.naming.core.NamingServerListManager;
Expand Down Expand Up @@ -73,7 +74,7 @@ public class NamingHttpClientProxy extends AbstractNamingClientProxy {
private static final int DEFAULT_SERVER_PORT = 8848;

private static final String MODULE_NAME = "Naming";

private static final String IP_PARAM = "ip";

private static final String PORT_PARAM = "port";
Expand Down Expand Up @@ -440,6 +441,12 @@ public String callServer(String api, Map<String, String> params, Map<String, Str
if (HttpStatus.SC_NOT_MODIFIED == restResult.getCode()) {
return StringUtils.EMPTY;
}

// If the 403 login operation is triggered, refresh the accessToken of the client
if (NacosAuthLoginConstant.RELOGIN_CODE == restResult.getCode()) {
reLogin();
}

throw new NacosException(restResult.getCode(), restResult.getMessage());
} catch (NacosException e) {
NAMING_LOGGER.error("[NA] failed to request", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.alibaba.nacos.client.security;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant;
import com.alibaba.nacos.plugin.auth.spi.client.ClientAuthPluginManager;
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
import com.alibaba.nacos.plugin.auth.spi.client.ClientAuthService;
Expand All @@ -38,6 +39,8 @@
public class SecurityProxy implements Closeable {

private ClientAuthPluginManager clientAuthPluginManager;

private Properties properties;

/**
* Construct from serverList, nacosRestTemplate, init client auth plugin.
Expand All @@ -57,6 +60,7 @@ public SecurityProxy(List<String> serverList, NacosRestTemplate nacosRestTemplat
* @param properties login identity information.
*/
public void login(Properties properties) {
this.properties = properties;
if (clientAuthPluginManager.getAuthServiceSpiImplSet().isEmpty()) {
return;
}
Expand Down Expand Up @@ -85,4 +89,19 @@ public Map<String, String> getIdentityContext(RequestResource resource) {
public void shutdown() throws NacosException {
clientAuthPluginManager.shutdown();
}

/**
* Login again to refresh the accessToken.
*/
public void reLogin() {
if (clientAuthPluginManager.getAuthServiceSpiImplSet().isEmpty()) {
return;
}
for (ClientAuthService clientAuthService : clientAuthPluginManager.getAuthServiceSpiImplSet()) {
LoginIdentityContext loginIdentityContext = clientAuthService.getLoginIdentityContext(null);
if (loginIdentityContext != null) {
loginIdentityContext.setParameter(NacosAuthLoginConstant.NEXTREFRESHTIME, "0");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.alibaba.nacos.common.http.HttpRestResult;
import com.alibaba.nacos.common.http.client.NacosRestTemplate;
import com.alibaba.nacos.common.http.param.Header;
import com.alibaba.nacos.common.utils.NumberUtils;
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
Expand Down Expand Up @@ -235,4 +237,28 @@ void testGetAccessTokenWithInvalidTtl() throws Exception {
//when
assertFalse(nacosClientAuthService.login(properties));
}

@Test
void testNextRefreshTime() throws Exception {
NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class);
HttpRestResult<Object> result = new HttpRestResult<>();
result.setData("{\"accessToken\":\"abc\",\"tokenTtl\":\"60\"}");
result.setCode(200);
when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result);
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.USERNAME, "aaa");
properties.setProperty(PropertyKeyConst.PASSWORD, "123456");

List<String> serverList = new ArrayList<>();
serverList.add("localhost");

NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl();
nacosClientAuthService.setServerList(serverList);
nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate);
//when
nacosClientAuthService.login(properties);
LoginIdentityContext loginIdentityContext = nacosClientAuthService.getLoginIdentityContext(null);
String nextRefreshTime = loginIdentityContext.getParameter(NacosAuthLoginConstant.NEXTREFRESHTIME);
assertTrue(System.currentTimeMillis() < NumberUtils.toLong(nextRefreshTime, 0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -709,4 +709,21 @@ void testConfigAppNameLabels() throws Exception {
String appName = config.labels().get(Constants.APPNAME);
assertNotNull(appName);
}

@Test
void testResponseCode403Exception() throws NacosException {
Throwable exception = assertThrows(NacosException.class, () -> {

when(this.rpcClient.request(Mockito.any())).thenReturn(ErrorResponse.build(403, "Invalid signature"));

try {
client.registerService(SERVICE_NAME, GROUP_NAME, instance);
} catch (NacosException ex) {
assertNull(ex.getCause());

throw ex;
}
});
assertTrue(exception.getMessage().contains("Invalid signature"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -645,4 +645,29 @@ void testRegApiForDomain() throws NacosException {
});

}

@Test
void testCallServerFail403() throws Exception {
//given
NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class);

when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> {
//return url
HttpRestResult<Object> res = new HttpRestResult<Object>();
res.setMessage("Invalid signature");
res.setCode(403);
return res;
});

final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate");
nacosRestTemplateField.setAccessible(true);
nacosRestTemplateField.set(clientProxy, nacosRestTemplate);
String api = "/api";
Map<String, String> params = new HashMap<>();
Map<String, String> body = new HashMap<>();
String method = HttpMethod.GET;
String curServer = "127.0.0.1";
//then
assertThrows(NacosException.class, () -> clientProxy.callServer(api, params, body, curServer, method));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@

import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant;
import com.alibaba.nacos.client.auth.impl.NacosClientAuthServiceImpl;
import com.alibaba.nacos.common.http.HttpRestResult;
import com.alibaba.nacos.common.http.client.NacosRestTemplate;
import com.alibaba.nacos.common.http.param.Header;
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
import com.alibaba.nacos.plugin.auth.api.RequestResource;
import com.alibaba.nacos.plugin.auth.spi.client.ClientAuthPluginManager;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -100,4 +102,18 @@ void testLoginWithoutAnyPlugin() throws NoSuchFieldException, IllegalAccessExcep
Map<String, String> header = securityProxy.getIdentityContext(new RequestResource());
assertTrue(header.isEmpty());
}

@Test
void testReLogin() throws NoSuchFieldException, IllegalAccessException {
NacosClientAuthServiceImpl authService = new NacosClientAuthServiceImpl();
ClientAuthPluginManager clientAuthPluginManager = mock(ClientAuthPluginManager.class);
Field clientAuthPluginManagerField = SecurityProxy.class.getDeclaredField("clientAuthPluginManager");
clientAuthPluginManagerField.setAccessible(true);
clientAuthPluginManagerField.set(securityProxy, clientAuthPluginManager);
when(clientAuthPluginManager.getAuthServiceSpiImplSet()).thenReturn(Collections.singleton(authService));
securityProxy.reLogin();
LoginIdentityContext loginIdentityContext = authService.getLoginIdentityContext(null);
String nextRefreshTime = loginIdentityContext.getParameter(NacosAuthLoginConstant.NEXTREFRESHTIME);
assertEquals(nextRefreshTime, "0");
}
}

0 comments on commit 96d5095

Please sign in to comment.