Skip to content

Commit

Permalink
optimize token
Browse files Browse the repository at this point in the history
  • Loading branch information
ponfee committed Jan 23, 2024
1 parent 58b1b0c commit 6305d7f
Show file tree
Hide file tree
Showing 19 changed files with 101 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
import cn.ponfee.disjob.admin.util.PageUtils;
import cn.ponfee.disjob.common.util.Strings;
import cn.ponfee.disjob.common.util.UuidUtils;
import cn.ponfee.disjob.core.model.TokenType;
import cn.ponfee.disjob.supervisor.application.SchedGroupService;
import cn.ponfee.disjob.supervisor.application.ServerMetricsService;
import cn.ponfee.disjob.supervisor.application.request.SchedGroupAddRequest;
import cn.ponfee.disjob.supervisor.application.request.SchedGroupPageRequest;
import cn.ponfee.disjob.supervisor.application.value.TokenName;
import com.google.common.collect.ImmutableMap;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
Expand Down Expand Up @@ -142,12 +142,12 @@ public String token(@RequestParam("group") String group, ModelMap mmap) {
@PostMapping("/token")
@ResponseBody
public AjaxResult token(@RequestParam("group") String group,
@RequestParam("name") TokenName name,
@RequestParam("type") TokenType type,
@RequestParam("operation") TokenOperation operation,
@RequestParam("currentValue") String currentValue) {
String newToken = TokenOperation.clear == operation ? "" : UuidUtils.uuid32();
String oldToken = TokenOperation.set == operation ? "" : currentValue;
if (schedGroupService.updateToken(group, name, newToken, getLoginName(), oldToken)) {
if (schedGroupService.updateToken(group, type, newToken, getLoginName(), oldToken)) {
return AjaxResult.success("操作成功", newToken);
} else {
return AjaxResult.error("操作失败");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,20 @@
<script th:inline="javascript">
const prefix = ctx + "disjob/mggroup";

function updateToken(name, operation) {
const input = $("#" + name + "Token");
function updateToken(type, operation) {
const input = $("#" + type + "Token");
const params = {
"group": '[(${data.group})]',
"name": name,
"type": type,
"operation": operation,
"currentValue": input.val()
};
const ops = (operation === 'change') ? "更换" : (operation === 'set' ? "设置" : "清除");
$.modal.confirm("确认要" + ops + "'" + name + "'令牌吗?", function () {
$.modal.confirm("确认要" + ops + "'" + type + "'令牌吗?", function () {
$.operate.post(prefix + "/token", params, function (result) {
if (result.code === 0) {
input.val(result.data);
setTokenButton(name);
setTokenButton(type);
}
}, false);
});
Expand Down Expand Up @@ -107,20 +107,20 @@
return false;
}

function setTokenButton(name) {
const input = $("#" + name + "Token");
function setTokenButton(type) {
const input = $("#" + type + "Token");
if (input.val()) {
$("#" + name + "1").html('<button type="button" class="btn btn-warning" onclick="updateToken(\'' + name + '\', \'change\')">更换</button>')
$("#" + name + "2").html('<button type="button" class="btn btn-danger" onclick="updateToken(\'' + name + '\', \'clear\')">清除</button>');
$("#" + type + "1").html('<button type="button" class="btn btn-warning" onclick="updateToken(\'' + type + '\', \'change\')">更换</button>')
$("#" + type + "2").html('<button type="button" class="btn btn-danger" onclick="updateToken(\'' + type + '\', \'clear\')">清除</button>');
} else {
$("#" + name + "1").html('<button type="button" class="btn btn-primary" onclick="updateToken(\'' + name + '\', \'set\')">设置</button>')
$("#" + name + "2").html('<button type="button" class="btn btn-default disabled" disabled>清除</button>');
$("#" + type + "1").html('<button type="button" class="btn btn-primary" onclick="updateToken(\'' + type + '\', \'set\')">设置</button>')
$("#" + type + "2").html('<button type="button" class="btn btn-default disabled" disabled>清除</button>');
}
}

$(function () {
const names = ['supervisor', 'worker', 'user'];
names.forEach(function (item) {
const types = ['supervisor', 'worker', 'user'];
types.forEach(function (item) {
setTokenButton(item);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,10 @@ public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityMan
filterChainDefinitionMap.put("/ruoyi/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");

// disjob服务间的PRC调用,后续给服务间颁发证书或密钥来解决安全问题
// disjob框架的 rpc / openapi 接口
filterChainDefinitionMap.put("/supervisor/rpc/**", "anon");
filterChainDefinitionMap.put("/worker/rpc/**", "anon");
filterChainDefinitionMap.put("/supervisor/openapi/**", "anon");

// 退出 logout地址,shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");
Expand Down
58 changes: 30 additions & 28 deletions disjob-core/src/main/java/cn/ponfee/disjob/core/base/Tokens.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package cn.ponfee.disjob.core.base;

import cn.ponfee.disjob.core.model.TokenType;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -27,41 +28,33 @@ public class Tokens {

private static final long EXPIRATION_MILLISECONDS = 60_000L;

public enum Type {
/**
* Supervisor token
*/
SUPERVISOR,
/**
* Worker token
*/
WORKER,
/**
* User token
*/
USER,
public static String createAuthentication(String tokenPlain, TokenType type, String group) {
return create(tokenPlain, type, Mode.authentication, group);
}

public enum Mode {
/**
* For authenticate
*/
AUTHENTICATE,
/**
* For signature
*/
SIGNATURE,
public static String createSignature(String tokenPlain, TokenType type, String group) {
return create(tokenPlain, type, Mode.signature, group);
}

public static boolean verifyAuthentication(String tokenSecret, String tokenPlain, TokenType type, String group) {
return verify(tokenSecret, tokenPlain, type, Mode.authentication, group);
}

public static String create(String tokenPlain, Type type, Mode mode, String group) {
public static boolean verifySignature(String tokenSecret, String tokenPlain, TokenType type, String group) {
return verify(tokenSecret, tokenPlain, type, Mode.signature, group);
}

// -----------------------------------------------------------------private methods

private static String create(String tokenPlain, TokenType type, Mode mode, String group) {
if (StringUtils.isEmpty(tokenPlain)) {
return null;
}
String expiration = Long.toString(System.currentTimeMillis() + EXPIRATION_MILLISECONDS);
return secret(tokenPlain, type, mode, expiration, group) + DOT + expiration;
}

public static boolean verify(String tokenSecret, String tokenPlain, Type type, Mode mode, String group) {
private static boolean verify(String tokenSecret, String tokenPlain, TokenType type, Mode mode, String group) {
if (StringUtils.isEmpty(tokenPlain)) {
return true;
}
Expand All @@ -80,17 +73,26 @@ public static boolean verify(String tokenSecret, String tokenPlain, Type type, M
return actual.equals(expect);
}

// -----------------------------------------------------------------private methods

private static String secret(String tokenPlain, Type type, Mode mode, String expiration, String group) {
private static String secret(String tokenPlain, TokenType type, Mode mode, String expiration, String group) {
Assert.notNull(type, "Type cannot be null.");
Assert.notNull(mode, "Mode cannot be null.");
Assert.hasText(group, "Group cannot be empty.");
String payload = type + DOT + mode + DOT + expiration + DOT + group;
String payload = type.name() + DOT + mode.name() + DOT + expiration + DOT + group;

HmacUtils hmac = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, tokenPlain.getBytes(UTF_8));
byte[] digest = hmac.hmac(payload.getBytes(UTF_8));
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
}

private enum Mode {
/**
* For authentication
*/
authentication,
/**
* For signature
*/
signature,
}

}
33 changes: 16 additions & 17 deletions disjob-core/src/main/java/cn/ponfee/disjob/core/base/Worker.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
import cn.ponfee.disjob.common.base.SingletonClassConstraint;
import cn.ponfee.disjob.common.util.Numbers;
import cn.ponfee.disjob.common.util.Strings;
import cn.ponfee.disjob.core.base.Tokens.Mode;
import cn.ponfee.disjob.core.base.Tokens.Type;
import cn.ponfee.disjob.core.exception.AuthenticationException;
import cn.ponfee.disjob.core.model.SchedJob;
import cn.ponfee.disjob.core.model.TokenType;
import cn.ponfee.disjob.core.param.worker.AuthenticationParam;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
Expand Down Expand Up @@ -217,21 +216,21 @@ public LocalDateTime getStartupAt() {
*
* @return map of authentication http headers
*/
public abstract Map<String, String> authenticationHeaders();
public abstract Map<String, String> createWorkerAuthenticationHeaders();

/**
* 认证Supervisor远程调用Worker RPC接口的授权信息
* Worker signature
*
* @param param authentication param
* @return signature string
*/
public abstract void authenticate(AuthenticationParam param);
public abstract String createWorkerSignatureToken();

/**
* Worker signature
* 认证Supervisor远程调用Worker RPC接口的授权信息
*
* @return signature string
* @param param authentication param
*/
public abstract String signature();
public abstract void verifySupervisorAuthenticationToken(AuthenticationParam param);

@Override
public boolean equals(Object o) {
Expand Down Expand Up @@ -259,25 +258,25 @@ private static synchronized Current create(final String group, final String work
private final String supervisorToken = StringUtils.trim(sToken);

@Override
public Map<String, String> authenticationHeaders() {
public Map<String, String> createWorkerAuthenticationHeaders() {
if (workerToken == null) {
return Collections.singletonMap(AUTHENTICATE_HEADER_GROUP, group);
}

String tokenSecret = Objects.requireNonNull(Tokens.create(workerToken, Type.WORKER, Mode.AUTHENTICATE, group));
String tokenSecret = Objects.requireNonNull(Tokens.createAuthentication(workerToken, TokenType.worker, group));
return ImmutableMap.of(AUTHENTICATE_HEADER_GROUP, group, AUTHENTICATE_HEADER_TOKEN, tokenSecret);
}

@Override
public void authenticate(AuthenticationParam param) {
if (!Tokens.verify(param.getSupervisorToken(), supervisorToken, Type.SUPERVISOR, Mode.AUTHENTICATE, group)) {
throw new AuthenticationException("Authenticate failed.");
}
public String createWorkerSignatureToken() {
return Tokens.createSignature(workerToken, TokenType.worker, group);
}

@Override
public String signature() {
return Tokens.create(workerToken, Type.WORKER, Mode.SIGNATURE, group);
public void verifySupervisorAuthenticationToken(AuthenticationParam param) {
if (!Tokens.verifyAuthentication(param.getSupervisorToken(), supervisorToken, TokenType.supervisor, group)) {
throw new AuthenticationException("Authenticate failed.");
}
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
** \/ \/ \/ **
\* */

package cn.ponfee.disjob.supervisor.application.value;
package cn.ponfee.disjob.core.model;

/**
* Token name
* Token type
*
* @author Ponfee
*/
public enum TokenName {
public enum TokenType {

/**
* supervisor_token
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package cn.ponfee.disjob.core.base;

import cn.ponfee.disjob.core.model.TokenType;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -21,11 +22,11 @@ public class TokensTest {

@Test
public void test() {
String tokenPlain = "tokenPlain";
String tokenPlain = "1878f0158782423f9306e7d4c70c999c";
String group = "app-test";
String tokenSecret = Tokens.create(tokenPlain, Tokens.Type.WORKER, Tokens.Mode.AUTHENTICATE, group);
String tokenSecret = Tokens.createAuthentication(tokenPlain, TokenType.user, group);
System.out.println(tokenSecret);
boolean state = Tokens.verify(tokenSecret, tokenPlain, Tokens.Type.WORKER, Tokens.Mode.AUTHENTICATE, group);
boolean state = Tokens.verifyAuthentication(tokenSecret, tokenPlain, TokenType.user, group);
assertThat(state).isTrue();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public boolean receive(ExecuteTaskParam param) {
return false;
}

currentWorker.authenticate(param);
currentWorker.verifySupervisorAuthenticationToken(param);

Worker assignedWorker = param.getWorker();
if (!currentWorker.sameWorker(assignedWorker)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public <T> T execute(String group, String path, HttpMethod httpMethod, Type retu
int serverNumber = servers.size();
Map<String, String> authenticationHeaders = null;
if (discoveryServerRole == ServerRole.SUPERVISOR) {
authenticationHeaders = Worker.current().authenticationHeaders();
authenticationHeaders = Worker.current().createWorkerAuthenticationHeaders();
}
int start = ThreadLocalRandom.current().nextInt(serverNumber);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,18 @@
import cn.ponfee.disjob.common.model.PageResponse;
import cn.ponfee.disjob.common.util.Functions;
import cn.ponfee.disjob.core.base.Tokens;
import cn.ponfee.disjob.core.base.Tokens.Mode;
import cn.ponfee.disjob.core.base.Tokens.Type;
import cn.ponfee.disjob.core.base.Worker;
import cn.ponfee.disjob.core.exception.GroupNotFoundException;
import cn.ponfee.disjob.core.exception.KeyExistsException;
import cn.ponfee.disjob.core.model.SchedGroup;
import cn.ponfee.disjob.core.model.TokenType;
import cn.ponfee.disjob.registry.SupervisorRegistry;
import cn.ponfee.disjob.supervisor.application.converter.SchedGroupConverter;
import cn.ponfee.disjob.supervisor.application.request.SchedGroupAddRequest;
import cn.ponfee.disjob.supervisor.application.request.SchedGroupPageRequest;
import cn.ponfee.disjob.supervisor.application.request.SchedGroupUpdateRequest;
import cn.ponfee.disjob.supervisor.application.response.SchedGroupResponse;
import cn.ponfee.disjob.supervisor.application.value.DisjobGroup;
import cn.ponfee.disjob.supervisor.application.value.TokenName;
import cn.ponfee.disjob.supervisor.configuration.SupervisorProperties;
import cn.ponfee.disjob.supervisor.dao.mapper.SchedGroupMapper;
import com.google.common.collect.ImmutableSet;
Expand Down Expand Up @@ -113,9 +111,9 @@ public SchedGroupResponse get(String group) {
return SchedGroupConverter.INSTANCE.convert(schedGroup);
}

public boolean updateToken(String group, TokenName name, String newToken, String updatedBy, String oldToken) {
public boolean updateToken(String group, TokenType type, String newToken, String updatedBy, String oldToken) {
return Functions.doIfTrue(
isOneAffectedRow(schedGroupMapper.updateToken(group, name, newToken, updatedBy, oldToken)),
isOneAffectedRow(schedGroupMapper.updateToken(group, type, newToken, updatedBy, oldToken)),
this::refresh
);
}
Expand Down Expand Up @@ -154,24 +152,24 @@ public static boolean isDeveloper(String group, String user) {
return getDisjobGroup(group).isDeveloper(user);
}

public static String createSupervisorAuthenticateToken(String group) {
public static String createSupervisorAuthenticationToken(String group) {
String supervisorToken = getDisjobGroup(group).getSupervisorToken();
return Tokens.create(supervisorToken, Type.SUPERVISOR, Mode.AUTHENTICATE, group);
return Tokens.createAuthentication(supervisorToken, TokenType.supervisor, group);
}

public static boolean verifyWorkerAuthenticateToken(String tokenSecret, String group) {
public static boolean verifyWorkerAuthenticationToken(String tokenSecret, String group) {
String workerToken = getDisjobGroup(group).getWorkerToken();
return Tokens.verify(tokenSecret, workerToken, Type.WORKER, Mode.AUTHENTICATE, group);
return Tokens.verifyAuthentication(tokenSecret, workerToken, TokenType.worker, group);
}

public static boolean verifyUserAuthenticateToken(String tokenSecret, String group) {
public static boolean verifyUserAuthenticationToken(String tokenSecret, String group) {
String userToken = getDisjobGroup(group).getUserToken();
return Tokens.verify(tokenSecret, userToken, Type.USER, Mode.AUTHENTICATE, group);
return Tokens.verifyAuthentication(tokenSecret, userToken, TokenType.user, group);
}

public static boolean verifyWorkerSignatureToken(String tokenSecret, String group) {
String workerToken = getDisjobGroup(group).getWorkerToken();
return Tokens.verify(tokenSecret, workerToken, Type.WORKER, Mode.SIGNATURE, group);
return Tokens.verifySignature(tokenSecret, workerToken, TokenType.worker, group);
}

// ------------------------------------------------------------private methods
Expand Down
Loading

0 comments on commit 6305d7f

Please sign in to comment.