Skip to content

Commit

Permalink
feat: authentication support (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
monkeyWie authored Jan 17, 2021
1 parent c4d339f commit 94162a2
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 16 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Proxyee is a JAVA written HTTP proxy server library that supports HTTP, HTTPS, W
<dependency>
<groupId>com.github.monkeywie</groupId>
<artifactId>proxyee</artifactId>
<version>1.3.2</version>
<version>1.3.3</version>
</dependency>
```

Expand Down Expand Up @@ -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.

Expand Down
27 changes: 25 additions & 2 deletions README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Proxyee 是一个 JAVA 编写的 HTTP 代理服务器类库,支持 HTTP、HTTP
<dependency>
<groupId>com.github.monkeywie</groupId>
<artifactId>proxyee</artifactId>
<version>1.3.2</version>
<version>1.3.3</version>
</dependency>
```

Expand Down Expand Up @@ -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 协议

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.github.monkeywie</groupId>
<artifactId>proxyee</artifactId>
<version>1.3.2</version>
<version>1.3.3</version>
<build>
<plugins>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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);
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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进来,不转发
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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));
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 94162a2

Please sign in to comment.