diff --git a/README.md b/README.md
index a3bc951..2a5bbd4 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ Proxyee is a JAVA written HTTP proxy server library that supports HTTP, HTTPS, W
com.github.monkeywie
proxyee
- 1.3.2
+ 1.3.3
```
@@ -115,8 +115,31 @@ openssl req -sha256 -new -x509 -days 365 -key ca.key -out ca.crt \
Copy `ca.crt` and `ca_private.der` to the project src/resources/ after generation, or implement the HttpProxyCACertFactory interface to custom load the root certificate and private key.
-## Pre-proxy support
+## Authentication
+
+Currently only basic authentication are supported.
+
+- Basic
+```java
+// curl -i -x 127.0.0.1:9999 -U admin:123456 https://www.baidu.com
+HttpProxyServerConfig config = new HttpProxyServerConfig();
+ config.setAuthenticationProvider(new BasicHttpProxyAuthenticationProvider() {
+ @Override
+ protected boolean authenticate(String usr, String pwd) {
+ return "admin".equals(usr) && "123456".equals(pwd);
+ }
+ });
+new HttpProxyServer()
+ .serverConfig(config)
+ .start(9999);
+```
+
+- Custom
+
+Customize authentication by implementing the `HttpProxyAuthenticationProvider` interface.
+
+## Pre-proxy support
Pre-proxy can be set,support http,socks4,socks5 protocol.
diff --git a/README_zh-CN.md b/README_zh-CN.md
index 5459bb8..cc33fdb 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -25,7 +25,7 @@ Proxyee 是一个 JAVA 编写的 HTTP 代理服务器类库,支持 HTTP、HTTP
com.github.monkeywie
proxyee
- 1.3.2
+ 1.3.3
```
@@ -120,8 +120,31 @@ openssl req -sha256 -new -x509 -days 365 -key ca.key -out ca.crt \
生成完之后把`ca.crt`和`ca_private.der`复制到项目的 src/resources/中,或者实现 HttpProxyCACertFactory 接口来自定义加载根证书和私钥
-## 前置代理支持
+## 身份验证
+
+目前只支持基本身份验证方案。
+
+- 基本验证
+```java
+// curl -i -x 127.0.0.1:9999 -U admin:123456 https://www.baidu.com
+HttpProxyServerConfig config = new HttpProxyServerConfig();
+ config.setAuthenticationProvider(new BasicHttpProxyAuthenticationProvider() {
+ @Override
+ protected boolean authenticate(String usr, String pwd) {
+ return "admin".equals(usr) && "123456".equals(pwd);
+ }
+ });
+new HttpProxyServer()
+ .serverConfig(config)
+ .start(9999);
+```
+
+- 自定义验证
+
+通过实现`HttpProxyAuthenticationProvider`接口来自定义验证。
+
+## 前置代理支持
可设置前置代理,支持 http,socks4,socks5 协议
diff --git a/pom.xml b/pom.xml
index 0bd587b..c3f0345 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.github.monkeywie
proxyee
- 1.3.2
+ 1.3.3
diff --git a/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyClientHandle.java b/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyClientHandler.java
similarity index 89%
rename from src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyClientHandle.java
rename to src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyClientHandler.java
index 5fb8525..72313f9 100644
--- a/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyClientHandle.java
+++ b/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyClientHandler.java
@@ -9,11 +9,11 @@
import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.ReferenceCountUtil;
-public class HttpProxyClientHandle extends ChannelInboundHandlerAdapter {
+public class HttpProxyClientHandler extends ChannelInboundHandlerAdapter {
private Channel clientChannel;
- public HttpProxyClientHandle(Channel clientChannel) {
+ public HttpProxyClientHandler(Channel clientChannel) {
this.clientChannel = clientChannel;
}
@@ -24,7 +24,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
ReferenceCountUtil.release(msg);
return;
}
- HttpProxyInterceptPipeline interceptPipeline = ((HttpProxyServerHandle) clientChannel.pipeline()
+ HttpProxyInterceptPipeline interceptPipeline = ((HttpProxyServerHandler) clientChannel.pipeline()
.get("serverHandle")).getInterceptPipeline();
if (msg instanceof HttpResponse) {
interceptPipeline.afterResponse(clientChannel, ctx.channel(), (HttpResponse) msg);
@@ -45,7 +45,7 @@ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.channel().close();
clientChannel.close();
- HttpProxyExceptionHandle exceptionHandle = ((HttpProxyServerHandle) clientChannel.pipeline()
+ HttpProxyExceptionHandle exceptionHandle = ((HttpProxyServerHandler) clientChannel.pipeline()
.get("serverHandle")).getExceptionHandle();
exceptionHandle.afterCatch(clientChannel, ctx.channel(), cause);
}
diff --git a/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyInitializer.java b/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyInitializer.java
index 06db5a9..0bc65ca 100644
--- a/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyInitializer.java
+++ b/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyInitializer.java
@@ -29,11 +29,11 @@ protected void initChannel(Channel ch) throws Exception {
}
if (requestProto.getSsl()) {
ch.pipeline().addLast(
- ((HttpProxyServerHandle) clientChannel.pipeline().get("serverHandle")).getServerConfig()
+ ((HttpProxyServerHandler) clientChannel.pipeline().get("serverHandle")).getServerConfig()
.getClientSslCtx()
.newHandler(ch.alloc(), requestProto.getHost(), requestProto.getPort()));
}
ch.pipeline().addLast("httpCodec", new HttpClientCodec());
- ch.pipeline().addLast("proxyClientHandle", new HttpProxyClientHandle(clientChannel));
+ ch.pipeline().addLast("proxyClientHandle", new HttpProxyClientHandler(clientChannel));
}
}
diff --git a/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyServerHandle.java b/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyServerHandler.java
similarity index 89%
rename from src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyServerHandle.java
rename to src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyServerHandler.java
index bfa8e7e..efe9ba7 100644
--- a/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyServerHandle.java
+++ b/src/main/java/com/github/monkeywie/proxyee/handler/HttpProxyServerHandler.java
@@ -10,6 +10,7 @@
import com.github.monkeywie.proxyee.proxy.ProxyHandleFactory;
import com.github.monkeywie.proxyee.server.HttpProxyServer;
import com.github.monkeywie.proxyee.server.HttpProxyServerConfig;
+import com.github.monkeywie.proxyee.server.auth.HttpProxyAuthenticationProvider;
import com.github.monkeywie.proxyee.util.ProtoUtil;
import com.github.monkeywie.proxyee.util.ProtoUtil.RequestProto;
import io.netty.bootstrap.Bootstrap;
@@ -28,7 +29,7 @@
import java.util.LinkedList;
import java.util.List;
-public class HttpProxyServerHandle extends ChannelInboundHandlerAdapter {
+public class HttpProxyServerHandler extends ChannelInboundHandlerAdapter {
private ChannelFuture cf;
private String host;
@@ -56,7 +57,7 @@ public HttpProxyExceptionHandle getExceptionHandle() {
return exceptionHandle;
}
- public HttpProxyServerHandle(HttpProxyServerConfig serverConfig, HttpProxyInterceptInitializer interceptInitializer, HttpTunnelIntercept tunnelIntercept, ProxyConfig proxyConfig, HttpProxyExceptionHandle exceptionHandle) {
+ public HttpProxyServerHandler(HttpProxyServerConfig serverConfig, HttpProxyInterceptInitializer interceptInitializer, HttpTunnelIntercept tunnelIntercept, ProxyConfig proxyConfig, HttpProxyExceptionHandle exceptionHandle) {
this.serverConfig = serverConfig;
this.proxyConfig = proxyConfig;
this.interceptInitializer = interceptInitializer;
@@ -75,6 +76,11 @@ public void channelRead(final ChannelHandlerContext ctx, final Object msg) throw
ctx.channel().close();
return;
}
+ if (!authenticate(ctx, request)) {
+ status = 2;
+ ctx.channel().close();
+ return;
+ }
status = 1;
this.host = requestProto.getHost();
this.port = requestProto.getPort();
@@ -139,6 +145,19 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E
exceptionHandle.beforeCatch(ctx.channel(), cause);
}
+ private boolean authenticate(ChannelHandlerContext ctx, HttpRequest request) {
+ if (serverConfig.getAuthenticationProvider() != null) {
+ HttpProxyAuthenticationProvider authProvider = serverConfig.getAuthenticationProvider();
+ if (!authProvider.authenticate(request.headers().get(HttpHeaderNames.PROXY_AUTHORIZATION))) {
+ HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpProxyServer.UNAUTHORIZED);
+ response.headers().set(HttpHeaderNames.PROXY_AUTHENTICATE, authProvider.authType() + " realm=\"" + authProvider.authRealm() + "\"");
+ ctx.writeAndFlush(response);
+ return false;
+ }
+ }
+ return true;
+ }
+
private void handleProxyData(Channel channel, Object msg, boolean isHttp) throws Exception {
if (cf == null) {
// connection异常 还有HttpContent进来,不转发
diff --git a/src/main/java/com/github/monkeywie/proxyee/handler/TunnelProxyInitializer.java b/src/main/java/com/github/monkeywie/proxyee/handler/TunnelProxyInitializer.java
index 6344b7f..cf7ddfd 100644
--- a/src/main/java/com/github/monkeywie/proxyee/handler/TunnelProxyInitializer.java
+++ b/src/main/java/com/github/monkeywie/proxyee/handler/TunnelProxyInitializer.java
@@ -42,7 +42,7 @@ public void channelUnregistered(ChannelHandlerContext ctx0) throws Exception {
public void exceptionCaught(ChannelHandlerContext ctx0, Throwable cause) throws Exception {
ctx0.channel().close();
clientChannel.close();
- HttpProxyExceptionHandle exceptionHandle = ((HttpProxyServerHandle) clientChannel.pipeline()
+ HttpProxyExceptionHandle exceptionHandle = ((HttpProxyServerHandler) clientChannel.pipeline()
.get("serverHandle")).getExceptionHandle();
exceptionHandle.afterCatch(clientChannel, ctx0.channel(), cause);
}
diff --git a/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServer.java b/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServer.java
index 287534d..4424ed0 100644
--- a/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServer.java
+++ b/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServer.java
@@ -3,7 +3,7 @@
import com.github.monkeywie.proxyee.crt.CertPool;
import com.github.monkeywie.proxyee.crt.CertUtil;
import com.github.monkeywie.proxyee.exception.HttpProxyExceptionHandle;
-import com.github.monkeywie.proxyee.handler.HttpProxyServerHandle;
+import com.github.monkeywie.proxyee.handler.HttpProxyServerHandler;
import com.github.monkeywie.proxyee.intercept.HttpProxyInterceptInitializer;
import com.github.monkeywie.proxyee.intercept.HttpTunnelIntercept;
import com.github.monkeywie.proxyee.proxy.ProxyConfig;
@@ -30,6 +30,8 @@ public class HttpProxyServer {
//http代理隧道握手成功
public final static HttpResponseStatus SUCCESS = new HttpResponseStatus(200,
"Connection established");
+ public final static HttpResponseStatus UNAUTHORIZED = new HttpResponseStatus(407,
+ "Unauthorized");
private HttpProxyCACertFactory caCertFactory;
private HttpProxyServerConfig serverConfig;
@@ -137,7 +139,7 @@ public void start(String ip, int port) {
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast("httpCodec", new HttpServerCodec());
ch.pipeline().addLast("serverHandle",
- new HttpProxyServerHandle(serverConfig, proxyInterceptInitializer, tunnelIntercept, proxyConfig,
+ new HttpProxyServerHandler(serverConfig, proxyInterceptInitializer, tunnelIntercept, proxyConfig,
httpProxyExceptionHandle));
}
});
diff --git a/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServerConfig.java b/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServerConfig.java
index 8f10905..abc50f9 100644
--- a/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServerConfig.java
+++ b/src/main/java/com/github/monkeywie/proxyee/server/HttpProxyServerConfig.java
@@ -1,5 +1,6 @@
package com.github.monkeywie.proxyee.server;
+import com.github.monkeywie.proxyee.server.auth.HttpProxyAuthenticationProvider;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.ssl.SslContext;
import io.netty.resolver.AddressResolverGroup;
@@ -23,6 +24,7 @@ public class HttpProxyServerConfig {
private int workerGroupThreads;
private int proxyGroupThreads;
private boolean handleSsl;
+ private HttpProxyAuthenticationProvider authenticationProvider;
private final AddressResolverGroup extends SocketAddress> resolver;
public HttpProxyServerConfig() {
@@ -129,6 +131,14 @@ public void setProxyGroupThreads(int proxyGroupThreads) {
this.proxyGroupThreads = proxyGroupThreads;
}
+ public HttpProxyAuthenticationProvider getAuthenticationProvider() {
+ return authenticationProvider;
+ }
+
+ public void setAuthenticationProvider(final HttpProxyAuthenticationProvider authenticationProvider) {
+ this.authenticationProvider = authenticationProvider;
+ }
+
public AddressResolverGroup> resolver() {
return resolver;
}
diff --git a/src/main/java/com/github/monkeywie/proxyee/server/auth/BasicHttpProxyAuthenticationProvider.java b/src/main/java/com/github/monkeywie/proxyee/server/auth/BasicHttpProxyAuthenticationProvider.java
new file mode 100644
index 0000000..39fbea8
--- /dev/null
+++ b/src/main/java/com/github/monkeywie/proxyee/server/auth/BasicHttpProxyAuthenticationProvider.java
@@ -0,0 +1,40 @@
+package com.github.monkeywie.proxyee.server.auth;
+
+import java.util.Base64;
+
+/**
+ * @Author LiWei
+ * @Description
+ * @Date 2021/1/15 14:12
+ */
+public abstract class BasicHttpProxyAuthenticationProvider implements HttpProxyAuthenticationProvider {
+
+ public static final String AUTH_TYPE_BASIC = "Basic";
+ public static final String AUTH_REALM_BASIC = "Access to the staging site";
+
+ public String authType() {
+ return AUTH_TYPE_BASIC;
+ }
+
+ public String authRealm() {
+ return AUTH_REALM_BASIC;
+ }
+
+ protected abstract boolean authenticate(String usr, String pwd);
+
+ public boolean authenticate(String authorization) {
+ String usr = "";
+ String pwd = "";
+ if (authorization != null && authorization.length() > 0) {
+ String token = authorization.substring(AUTH_TYPE_BASIC.length() + 1);
+ String decode = new String(Base64.getDecoder().decode(token));
+ String[] arr = decode.split(":");
+ usr = arr[0];
+ if (arr.length >= 2) {
+ pwd = arr[1];
+ }
+ }
+ return authenticate(usr, pwd);
+ }
+
+}
diff --git a/src/main/java/com/github/monkeywie/proxyee/server/auth/HttpProxyAuthenticationProvider.java b/src/main/java/com/github/monkeywie/proxyee/server/auth/HttpProxyAuthenticationProvider.java
new file mode 100644
index 0000000..fe335f2
--- /dev/null
+++ b/src/main/java/com/github/monkeywie/proxyee/server/auth/HttpProxyAuthenticationProvider.java
@@ -0,0 +1,14 @@
+package com.github.monkeywie.proxyee.server.auth;
+
+/**
+ * @Author LiWei
+ * @Description
+ * @Date 2021/1/15 14:12
+ */
+public interface HttpProxyAuthenticationProvider {
+ String authType();
+
+ String authRealm();
+
+ boolean authenticate(String authorization);
+}
diff --git a/src/test/java/com/github/monkeywie/proxyee/AuthHttpProxyServer.java b/src/test/java/com/github/monkeywie/proxyee/AuthHttpProxyServer.java
new file mode 100644
index 0000000..28b85a4
--- /dev/null
+++ b/src/test/java/com/github/monkeywie/proxyee/AuthHttpProxyServer.java
@@ -0,0 +1,22 @@
+package com.github.monkeywie.proxyee;
+
+import com.github.monkeywie.proxyee.server.HttpProxyServer;
+import com.github.monkeywie.proxyee.server.HttpProxyServerConfig;
+import com.github.monkeywie.proxyee.server.auth.BasicHttpProxyAuthenticationProvider;
+
+public class AuthHttpProxyServer {
+
+ // curl -i -x 127.0.0.1:9999 -U admin:123456 https://www.baidu.com
+ public static void main(String[] args) throws Exception {
+ HttpProxyServerConfig config = new HttpProxyServerConfig();
+ config.setAuthenticationProvider(new BasicHttpProxyAuthenticationProvider() {
+ @Override
+ protected boolean authenticate(String usr, String pwd) {
+ return "admin".equals(usr) && "123456".equals(pwd);
+ }
+ });
+ new HttpProxyServer()
+ .serverConfig(config)
+ .start(9999);
+ }
+}