rocketmq的AclClientRPCHook權(quán)限控制使用技巧示例詳解
序
本文主要研究一下rocketmq的AclClientRPCHook
RPCHook
rocketmq-remoting-4.5.2-sources.jar!/org/apache/rocketmq/remoting/RPCHook.java
public interface RPCHook { void doBeforeRequest(final String remoteAddr, final RemotingCommand request); void doAfterResponse(final String remoteAddr, final RemotingCommand request, final RemotingCommand response); }
- RPCHook定義了doBeforeRequest、doAfterResponse方法
AclClientRPCHook
rocketmq-acl-4.5.2-sources.jar!/org/apache/rocketmq/acl/common/AclClientRPCHook.java
public class AclClientRPCHook implements RPCHook { private final SessionCredentials sessionCredentials; protected ConcurrentHashMap<Class<? extends CommandCustomHeader>, Field[]> fieldCache = new ConcurrentHashMap<Class<? extends CommandCustomHeader>, Field[]>(); public AclClientRPCHook(SessionCredentials sessionCredentials) { this.sessionCredentials = sessionCredentials; } @Override public void doBeforeRequest(String remoteAddr, RemotingCommand request) { byte[] total = AclUtils.combineRequestContent(request, parseRequestContent(request, sessionCredentials.getAccessKey(), sessionCredentials.getSecurityToken())); String signature = AclUtils.calSignature(total, sessionCredentials.getSecretKey()); request.addExtField(SIGNATURE, signature); request.addExtField(ACCESS_KEY, sessionCredentials.getAccessKey()); // The SecurityToken value is unneccessary,user can choose this one. if (sessionCredentials.getSecurityToken() != null) { request.addExtField(SECURITY_TOKEN, sessionCredentials.getSecurityToken()); } } @Override public void doAfterResponse(String remoteAddr, RemotingCommand request, RemotingCommand response) { } protected SortedMap<String, String> parseRequestContent(RemotingCommand request, String ak, String securityToken) { CommandCustomHeader header = request.readCustomHeader(); // Sort property SortedMap<String, String> map = new TreeMap<String, String>(); map.put(ACCESS_KEY, ak); if (securityToken != null) { map.put(SECURITY_TOKEN, securityToken); } try { // Add header properties if (null != header) { Field[] fields = fieldCache.get(header.getClass()); if (null == fields) { fields = header.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); } Field[] tmp = fieldCache.putIfAbsent(header.getClass(), fields); if (null != tmp) { fields = tmp; } } for (Field field : fields) { Object value = field.get(header); if (null != value && !field.isSynthetic()) { map.put(field.getName(), value.toString()); } } } return map; } catch (Exception e) { throw new RuntimeException("incompatible exception.", e); } } public SessionCredentials getSessionCredentials() { return sessionCredentials; } }
- AclClientRPCHook實(shí)現(xiàn)了RPCHook接口,其構(gòu)造器接收SessionCredentials參數(shù);其doBeforeRequest首先通過parseRequestContent從request讀取CommandCustomHeader,將其field連同accessKey、securityToken放到一個(gè)SortedMap,再通過AclUtils.combineRequestContent計(jì)算要發(fā)送的請(qǐng)求內(nèi)容;然后通過AclUtils.calSignature計(jì)算出signature,最后往request的extFields添加SIGNATURE、ACCESS_KEY;若設(shè)置securityToken,則會(huì)往request的extFields添加SECURITY_TOKEN
SessionCredentials
rocketmq-acl-4.5.2-sources.jar!/org/apache/rocketmq/acl/common/SessionCredentials.java
public class SessionCredentials { public static final Charset CHARSET = Charset.forName("UTF-8"); public static final String ACCESS_KEY = "AccessKey"; public static final String SECRET_KEY = "SecretKey"; public static final String SIGNATURE = "Signature"; public static final String SECURITY_TOKEN = "SecurityToken"; public static final String KEY_FILE = System.getProperty("rocketmq.client.keyFile", System.getProperty("user.home") + File.separator + "key"); private String accessKey; private String secretKey; private String securityToken; private String signature; public SessionCredentials() { String keyContent = null; try { keyContent = MixAll.file2String(KEY_FILE); } catch (IOException ignore) { } if (keyContent != null) { Properties prop = MixAll.string2Properties(keyContent); if (prop != null) { this.updateContent(prop); } } } public SessionCredentials(String accessKey, String secretKey) { this.accessKey = accessKey; this.secretKey = secretKey; } public SessionCredentials(String accessKey, String secretKey, String securityToken) { this(accessKey, secretKey); this.securityToken = securityToken; } public void updateContent(Properties prop) { { String value = prop.getProperty(ACCESS_KEY); if (value != null) { this.accessKey = value.trim(); } } { String value = prop.getProperty(SECRET_KEY); if (value != null) { this.secretKey = value.trim(); } } { String value = prop.getProperty(SECURITY_TOKEN); if (value != null) { this.securityToken = value.trim(); } } } //...... }
- SessionCredentials提供了三個(gè)構(gòu)造器,一個(gè)無參構(gòu)造器從KEY_FILE加載keyContent然后解析為Properties再通過updateContent方法給accessKey、secretKey、securityToken賦值;一個(gè)是accessKey, secretKey的構(gòu)造器;還有一個(gè)是accessKey, secretKey, securityToken的構(gòu)造器
AclUtils
rocketmq-acl-4.5.2-sources.jar!/org/apache/rocketmq/acl/common/AclUtils.java
public class AclUtils { private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.COMMON_LOGGER_NAME); public static byte[] combineRequestContent(RemotingCommand request, SortedMap<String, String> fieldsMap) { try { StringBuilder sb = new StringBuilder(""); for (Map.Entry<String, String> entry : fieldsMap.entrySet()) { if (!SessionCredentials.SIGNATURE.equals(entry.getKey())) { sb.append(entry.getValue()); } } return AclUtils.combineBytes(sb.toString().getBytes(CHARSET), request.getBody()); } catch (Exception e) { throw new RuntimeException("Incompatible exception.", e); } } public static byte[] combineBytes(byte[] b1, byte[] b2) { int size = (null != b1 ? b1.length : 0) + (null != b2 ? b2.length : 0); byte[] total = new byte[size]; if (null != b1) System.arraycopy(b1, 0, total, 0, b1.length); if (null != b2) System.arraycopy(b2, 0, total, b1.length, b2.length); return total; } public static String calSignature(byte[] data, String secretKey) { String signature = AclSigner.calSignature(data, secretKey); return signature; } //...... }
- combineRequestContent首先將fieldsMap拼接為字符串,然后通過AclUtils.combineBytes將其與request.getBody()結(jié)合在一起;calSignature方法內(nèi)部是委托給AclSigner.calSignature(data, secretKey)來實(shí)現(xiàn)
AclSigner
rocketmq-acl-4.5.2-sources.jar!/org/apache/rocketmq/acl/common/AclSigner.java
public class AclSigner { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); public static final SigningAlgorithm DEFAULT_ALGORITHM = SigningAlgorithm.HmacSHA1; private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_AUTHORIZE_LOGGER_NAME); private static final int CAL_SIGNATURE_FAILED = 10015; private static final String CAL_SIGNATURE_FAILED_MSG = "[%s:signature-failed] unable to calculate a request signature. error=%s"; public static String calSignature(String data, String key) throws AclException { return calSignature(data, key, DEFAULT_ALGORITHM, DEFAULT_CHARSET); } public static String calSignature(String data, String key, SigningAlgorithm algorithm, Charset charset) throws AclException { return signAndBase64Encode(data, key, algorithm, charset); } private static String signAndBase64Encode(String data, String key, SigningAlgorithm algorithm, Charset charset) throws AclException { try { byte[] signature = sign(data.getBytes(charset), key.getBytes(charset), algorithm); return new String(Base64.encodeBase64(signature), DEFAULT_CHARSET); } catch (Exception e) { String message = String.format(CAL_SIGNATURE_FAILED_MSG, CAL_SIGNATURE_FAILED, e.getMessage()); log.error(message, e); throw new AclException("CAL_SIGNATURE_FAILED", CAL_SIGNATURE_FAILED, message, e); } } private static byte[] sign(byte[] data, byte[] key, SigningAlgorithm algorithm) throws AclException { try { Mac mac = Mac.getInstance(algorithm.toString()); mac.init(new SecretKeySpec(key, algorithm.toString())); return mac.doFinal(data); } catch (Exception e) { String message = String.format(CAL_SIGNATURE_FAILED_MSG, CAL_SIGNATURE_FAILED, e.getMessage()); log.error(message, e); throw new AclException("CAL_SIGNATURE_FAILED", CAL_SIGNATURE_FAILED, message, e); } } public static String calSignature(byte[] data, String key) throws AclException { return calSignature(data, key, DEFAULT_ALGORITHM, DEFAULT_CHARSET); } public static String calSignature(byte[] data, String key, SigningAlgorithm algorithm, Charset charset) throws AclException { return signAndBase64Encode(data, key, algorithm, charset); } private static String signAndBase64Encode(byte[] data, String key, SigningAlgorithm algorithm, Charset charset) throws AclException { try { byte[] signature = sign(data, key.getBytes(charset), algorithm); return new String(Base64.encodeBase64(signature), DEFAULT_CHARSET); } catch (Exception e) { String message = String.format(CAL_SIGNATURE_FAILED_MSG, CAL_SIGNATURE_FAILED, e.getMessage()); log.error(message, e); throw new AclException("CAL_SIGNATURE_FAILED", CAL_SIGNATURE_FAILED, message, e); } } }
- calSignature默認(rèn)使用的是SigningAlgorithm.HmacSHA1及Charset.forName("UTF-8")字符集來簽名;signAndBase64Encode方法首先通過sign方法簽名,然后將其轉(zhuǎn)為Base64的字符串
小結(jié)
AclClientRPCHook實(shí)現(xiàn)了RPCHook接口,其構(gòu)造器接收SessionCredentials參數(shù);其doBeforeRequest首先通過parseRequestContent從request讀取CommandCustomHeader,將其field連同accessKey、securityToken放到一個(gè)SortedMap,再通過AclUtils.combineRequestContent計(jì)算要發(fā)送的請(qǐng)求內(nèi)容;然后通過AclUtils.calSignature計(jì)算出signature,最后往request的extFields添加SIGNATURE、ACCESS_KEY;若設(shè)置securityToken,則會(huì)往request的extFields添加SECURITY_TOKEN
doc AclClientRPCHook
以上就是rocketmq的AclClientRPCHook使用技巧示例詳解的詳細(xì)內(nèi)容,更多關(guān)于rocketmq AclClientRPCHook的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot解決數(shù)據(jù)庫時(shí)間和返回時(shí)間格式不一致的問題
這篇文章主要介紹了SpringBoot解決數(shù)據(jù)庫時(shí)間和返回時(shí)間格式不一致的問題,文章通過代碼示例和圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)和工作有一定的幫助,需要的朋友可以參考下2024-03-03使用JPA單項(xiàng)一對(duì)多外鍵關(guān)聯(lián)
這篇文章主要介紹了使用JPA單項(xiàng)一對(duì)多外鍵關(guān)聯(lián),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06Spring 實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離的示例
現(xiàn)在大型的電子商務(wù)系統(tǒng),在數(shù)據(jù)庫層面大都采用讀寫分離技術(shù),我們通常的做法就是把查詢從主庫中抽取出來,采用多個(gè)從庫,使用負(fù)載均衡,減輕每個(gè)從庫的查詢壓力。2017-01-01java中的阻塞隊(duì)列應(yīng)用場(chǎng)景及代碼實(shí)例
這篇文章主要介紹了java中的阻塞隊(duì)列應(yīng)用場(chǎng)景及代碼實(shí)例阻塞隊(duì)列是一種特殊的隊(duì)列,它提供了線程安全的操作,并在隊(duì)列為空或滿時(shí)提供了阻塞的功能,阻塞隊(duì)列通常用于多線程場(chǎng)景,其中生產(chǎn)者線程向隊(duì)列中添加元素,而消費(fèi)者線程從隊(duì)列中獲取元素,需要的朋友可以參考下2024-01-01