Skip to content

Commit

Permalink
Support basic auth (#14644)
Browse files Browse the repository at this point in the history
* Support basic auth

* Fix sd

* Use trie

* Fix ut

* Fix ut

* Fix ut
  • Loading branch information
AlbumenJ authored Sep 7, 2024
1 parent 0129990 commit a25b21f
Show file tree
Hide file tree
Showing 23 changed files with 510 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,21 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
*/
private Boolean auth;

/**
* Authenticator for authentication
*/
private String authenticator;

/**
* Username for basic authenticator
*/
private String username;

/**
* Password for basic authenticator
*/
private String password;

/**
* Use separate instances for services with the same serviceKey (applies when using ReferenceConfig and SimpleReferenceCache together).
* Directly calling ReferenceConfig.get() will not check this attribute.
Expand Down Expand Up @@ -892,6 +907,33 @@ public void setAuth(Boolean auth) {
this.auth = auth;
}

public String getAuthenticator() {
return authenticator;
}

public AbstractInterfaceConfig setAuthenticator(String authenticator) {
this.authenticator = authenticator;
return this;
}

public String getUsername() {
return username;
}

public AbstractInterfaceConfig setUsername(String username) {
this.username = username;
return this;
}

public String getPassword() {
return password;
}

public AbstractInterfaceConfig setPassword(String password) {
this.password = password;
return this;
}

public SslConfig getSslConfig() {
return getConfigManager().getSsl().orElse(null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.support.RpcUtils;

public class AccessKeyAuthenticator implements Authenticator {
private final ApplicationModel applicationModel;
private final FrameworkModel frameworkModel;

public AccessKeyAuthenticator(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
public AccessKeyAuthenticator(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}

@Override
Expand Down Expand Up @@ -73,7 +73,7 @@ public void authenticate(Invocation invocation, URL url) throws RpcAuthenticatio
}

AccessKeyPair getAccessKeyPair(Invocation invocation, URL url) {
AccessKeyStorage accessKeyStorage = applicationModel
AccessKeyStorage accessKeyStorage = frameworkModel
.getExtensionLoader(AccessKeyStorage.class)
.getExtension(url.getParameter(Constants.ACCESS_KEY_STORAGE_KEY, Constants.DEFAULT_ACCESS_KEY_STORAGE));

Expand All @@ -97,10 +97,6 @@ String getSignature(URL url, Invocation invocation, String secretKey, String tim
RpcUtils.getMethodName(invocation),
secretKey,
time);
boolean parameterEncrypt = url.getParameter(Constants.PARAMETER_SIGNATURE_ENABLE_KEY, false);
if (parameterEncrypt) {
return SignatureUtils.sign(invocation.getArguments(), requestString, secretKey);
}
return SignatureUtils.sign(requestString, secretKey);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.auth;

import org.apache.dubbo.auth.exception.RpcAuthenticationException;
import org.apache.dubbo.auth.spi.Authenticator;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;

import java.util.Base64;
import java.util.Objects;

public class BasicAuthenticator implements Authenticator {

@Override
public void sign(Invocation invocation, URL url) {
String username = url.getParameter(Constants.USERNAME_KEY);
String password = url.getParameter(Constants.PASSWORD_KEY);
String auth = username + ":" + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
String authHeaderValue = "Basic " + encodedAuth;

invocation.setAttachment(Constants.AUTHORIZATION_HEADER_LOWER, authHeaderValue);
}

@Override
public void authenticate(Invocation invocation, URL url) throws RpcAuthenticationException {
String username = url.getParameter(Constants.USERNAME_KEY);
String password = url.getParameter(Constants.PASSWORD_KEY);
String auth = username + ":" + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
String authHeaderValue = "Basic " + encodedAuth;

if (!Objects.equals(authHeaderValue, invocation.getAttachment(Constants.AUTHORIZATION_HEADER))
&& !Objects.equals(authHeaderValue, invocation.getAttachment(Constants.AUTHORIZATION_HEADER_LOWER))) {
throw new RpcAuthenticationException("Failed to authenticate, maybe consumer side did not enable the auth");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@

public interface Constants {

String SERVICE_AUTH = "auth";
String AUTH_KEY = "auth";

String AUTHENTICATOR = "authenticator";
String AUTHENTICATOR_KEY = "authenticator";

String DEFAULT_AUTHENTICATOR = "accesskey";
String USERNAME_KEY = "username";

String PASSWORD_KEY = "password";

String DEFAULT_AUTHENTICATOR = "basic";

String DEFAULT_ACCESS_KEY_STORAGE = "urlstorage";

Expand All @@ -41,4 +45,11 @@ public interface Constants {
String SIGNATURE_STRING_FORMAT = "%s#%s#%s#%s";

String PARAMETER_SIGNATURE_ENABLE_KEY = "param.sign";

String AUTH_SUCCESS = "auth.success";

String AUTHORIZATION_HEADER_LOWER = "authorization";

String AUTHORIZATION_HEADER = "Authorization";
String REMOTE_ADDRESS_KEY = "tri.remote.address";
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,29 @@
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;

/**
* The ConsumerSignFilter
*
* @see org.apache.dubbo.rpc.Filter
*/
@Activate(group = CommonConstants.CONSUMER, value = Constants.SERVICE_AUTH, order = -10000)
@Activate(group = CommonConstants.CONSUMER, value = Constants.AUTH_KEY, order = -10000)
public class ConsumerSignFilter implements Filter {
private final ApplicationModel applicationModel;
private final FrameworkModel frameworkModel;

public ConsumerSignFilter(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
public ConsumerSignFilter(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}

@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
boolean shouldAuth = url.getParameter(Constants.SERVICE_AUTH, false);
boolean shouldAuth = url.getParameter(Constants.AUTH_KEY, false);
if (shouldAuth) {
Authenticator authenticator = applicationModel
Authenticator authenticator = frameworkModel
.getExtensionLoader(Authenticator.class)
.getExtension(url.getParameter(Constants.AUTHENTICATOR, Constants.DEFAULT_AUTHENTICATOR));
.getExtension(url.getParameter(Constants.AUTHENTICATOR_KEY, Constants.DEFAULT_AUTHENTICATOR));
authenticator.sign(invocation, url);
}
return invoker.invoke(invocation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,27 @@
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;

@Activate(group = CommonConstants.PROVIDER, value = Constants.SERVICE_AUTH, order = -10000)
@Activate(group = CommonConstants.PROVIDER, value = Constants.AUTH_KEY, order = -10000)
public class ProviderAuthFilter implements Filter {
private final ApplicationModel applicationModel;
private final FrameworkModel frameworkModel;

public ProviderAuthFilter(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
public ProviderAuthFilter(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}

@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
boolean shouldAuth = url.getParameter(Constants.SERVICE_AUTH, false);
boolean shouldAuth = url.getParameter(Constants.AUTH_KEY, false);
if (shouldAuth) {
Authenticator authenticator = applicationModel
if (Boolean.TRUE.equals(invocation.getAttributes().get(Constants.AUTH_SUCCESS))) {
return invoker.invoke(invocation);
}
Authenticator authenticator = frameworkModel
.getExtensionLoader(Authenticator.class)
.getExtension(url.getParameter(Constants.AUTHENTICATOR, Constants.DEFAULT_AUTHENTICATOR));
.getExtension(url.getParameter(Constants.AUTHENTICATOR_KEY, Constants.DEFAULT_AUTHENTICATOR));
try {
authenticator.authenticate(invocation, url);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.auth.filter;

import org.apache.dubbo.auth.Constants;
import org.apache.dubbo.auth.spi.Authenticator;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.HeaderFilter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.support.RpcUtils;

import static org.apache.dubbo.rpc.RpcException.AUTHORIZATION_EXCEPTION;

@Activate(value = Constants.AUTH_KEY, order = -20000)
public class ProviderAuthHeaderFilter implements HeaderFilter {
private final FrameworkModel frameworkModel;

public ProviderAuthHeaderFilter(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}

@Override
public RpcInvocation invoke(Invoker<?> invoker, RpcInvocation invocation) throws RpcException {
URL url = invoker.getUrl();
boolean shouldAuth = url.getParameter(Constants.AUTH_KEY, false);
if (shouldAuth) {
Authenticator authenticator = frameworkModel
.getExtensionLoader(Authenticator.class)
.getExtension(url.getParameter(Constants.AUTHENTICATOR_KEY, Constants.DEFAULT_AUTHENTICATOR));
try {
authenticator.authenticate(invocation, url);
} catch (Exception e) {
Class<?> serviceType = invoker.getInterface();
throw new RpcException(
AUTHORIZATION_EXCEPTION,
"Forbid invoke remote service " + serviceType + " method " + RpcUtils.getMethodName(invocation)
+ "() from consumer "
+ invocation.getAttributes().get(Constants.REMOTE_ADDRESS_KEY) + " to provider "
+ RpcContext.getServiceContext().getLocalHost());
}
invocation.getAttributes().put(Constants.AUTH_SUCCESS, Boolean.TRUE);
}
return invocation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@

import org.apache.dubbo.auth.model.AccessKeyPair;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.rpc.Invocation;

/**
* This SPI Extension support us to store our {@link AccessKeyPair} or load {@link AccessKeyPair} from other
* storage, such as filesystem.
*/
@SPI
@SPI(scope = ExtensionScope.FRAMEWORK)
public interface AccessKeyStorage {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@

import org.apache.dubbo.auth.exception.RpcAuthenticationException;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.rpc.Invocation;

@SPI("accessKey")
@SPI(scope = ExtensionScope.FRAMEWORK, value = "basic")
public interface Authenticator {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
accesskey=org.apache.dubbo.auth.AccessKeyAuthenticator
accesskey=org.apache.dubbo.auth.AccessKeyAuthenticator
basic=org.apache.dubbo.auth.BasicAuthenticator
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
auth=org.apache.dubbo.auth.filter.ProviderAuthHeaderFilter
Loading

0 comments on commit a25b21f

Please sign in to comment.