自动自定义验证

时间:2017-05-17 20:30:36

标签: java authentication apache-httpclient-4.x

我想使用Apache HttpClient 4+将经过身份验证的请求发送到HTTP服务器(实际上,我需要将其用于不同的服务器实现)并且仅在需要时自动进行身份验证(或重新身份验证),身份验证令牌不存在或已死亡

为了进行身份验证,我需要使用包含用户凭据的JSON发送POST请求。

如果cookie中没有提供身份验证令牌,则一个服务器返回状态码401,另一个500响应正文中带有AUTH_REQUIRED文本。

我通过将file_put_contents($filename, pack("C*", ...$my_PNG)); 设置为正确的HttpClient,尝试实施自己的CredentialsProvider并注册并取消注册其余标准版本,在不同的Credentials版本中玩了很多。

我还尝试设置自己的AuthScheme。当AuthenticationHandler被调用时,我分析isAuthenticationRequested作为方法参数传递,并通过分析状态代码和响应体来决定返回什么。我希望这个(HttpResponse)强制客户端通过调用isAuthenticationRequested() == trueAuthScheme.authenticate返回的AuthScheme实现)进行身份验证,而不是{{1调用我可以看到AuthenticationHandler.selectScheme。我真的不知道应该通过这种方法返回什么,因此我只是回归AuthScheme.authenticate

这是我在结果中的调试输出

AuthenticationHandler.getChallenges

接下来我该怎么办?我正朝着正确的方向前进吗?

更新

我几乎达到了我所需要的水平。遗憾的是,我无法提供完整的项目资源,因为我无法提供对服务器的公共访问权限。这是我的简化代码示例:

new HashMap<>()

DEBUG org.apache.http.impl.client.DefaultHttpClient - Authentication required
DEBUG org.apache.http.impl.client.DefaultHttpClient - example.com requested authentication
DEBUG com.test.httpclient.MyAuthenticationHandler - MyAuthenticationHandler.getChallenges()
DEBUG org.apache.http.impl.client.DefaultHttpClient - Response contains no authentication challenges

MyAuthScheme.java

public class MyAuthScheme implements ContextAwareAuthScheme {

    public static final String NAME = "myscheme";

    @Override
    public Header authenticate(Credentials credentials, 
                           HttpRequest request, 
                           HttpContext context) throws AuthenticationException {

        HttpClientContext clientContext = ((HttpClientContext) context);        
        String name = clientContext.getTargetAuthState().getState().name();

        // Hack #1: 
        // I've come to this check. I don't like it, but it allows to authenticate
        // first request and don't repeat authentication procedure for further 
        // requests
        if(name.equals("CHALLENGED") && clientContext.getResponse() == null) {

            //
            // auth procedure must be here but is omitted in current example
            //

            // Hack #2: first request won't be present with auth token cookie set via cookie store 
            request.setHeader(new BasicHeader("Cookie", "MYAUTHTOKEN=bru99rshi7r5ucstkj1wei4fshsd"));

            // this works for second and subsequent requests
            BasicClientCookie authTokenCookie = new BasicClientCookie("MYAUTHTOKEN", "bru99rshi7r5ucstkj1wei4fshsd");
            authTokenCookie.setDomain("example.com");
            authTokenCookie.setPath("/");

            BasicCookieStore cookieStore = (BasicCookieStore) clientContext.getCookieStore();
            cookieStore.addCookie(authTokenCookie);
        }

        // I can't return cookie header here, otherwise it will clear 
        // other cookies, right?
        return null;
    }

    @Override
    public void processChallenge(Header header) throws MalformedChallengeException {

    }

    @Override
    public String getSchemeName() {
        return NAME;
    }

    @Override
    public String getParameter(String name) {
        return null;
    }

    @Override
    public String getRealm() {
        return null;
    }

    @Override
    public boolean isConnectionBased() {
        return false;
    }

    @Override
    public boolean isComplete() {
        return true;
    }

    @Override
    public Header authenticate(Credentials credentials, 
                           HttpRequest request) throws AuthenticationException {        
        return null;
    }

}

MyAuthStrategy.java

public class MyAuthStrategy implements AuthenticationStrategy {

    @Override
    public boolean isAuthenticationRequested(HttpHost authhost, 
                                         HttpResponse response, 
                                         HttpContext context) {

        return response.getStatusLine().getStatusCode() == 401;
    }

    @Override
    public Map<String, Header> getChallenges(HttpHost authhost, 
                                         HttpResponse response, 
                                         HttpContext context) throws MalformedChallengeException {

        Map<String, Header> challenges = new HashMap<>();
        challenges.put(MyAuthScheme.NAME, new BasicHeader(
                "WWW-Authentication", 
                "Myscheme realm=\"My SOAP authentication\""));

        return challenges;
    }

    @Override
    public Queue<AuthOption> select(Map<String, Header> challenges, 
                                HttpHost authhost, 
                                HttpResponse response, 
                                HttpContext context) throws MalformedChallengeException {

        Credentials credentials = ((HttpClientContext) context)
                .getCredentialsProvider()
                .getCredentials(new AuthScope(authhost));

        Queue<AuthOption> authOptions = new LinkedList<>();
        authOptions.add(new AuthOption(new MyAuthScheme(), credentials));

        return authOptions;
    }

    @Override
    public void authSucceeded(HttpHost authhost, AuthScheme authScheme, HttpContext context) {}

    @Override
    public void authFailed(HttpHost authhost, AuthScheme authScheme, HttpContext context) {}

}

版本

httpcore:4.4.6
httpclient:4.5.3

可能这不是最好的代码,但至少它可行。

请查看我在MyApp.java方法中的评论。

1 个答案:

答案 0 :(得分:1)

这对Apache HttpClient 4.2

的预期效果

注意。虽然它是使用httpclient 4.5编译和执行的,但它的执行会永远循环。

MyAuthScheme.java

public class MyAuthScheme implements ContextAwareAuthScheme {

    public static final String NAME = "myscheme";
    private static final String REQUEST_BODY = "{\"login\":\"%s\",\"password\":\"%s\"}";

    private final URI loginUri;

    public MyAuthScheme(URI uri) {
        loginUri = uri;
    }

    @Override
    public Header authenticate(Credentials credentials, 
                           HttpRequest request, 
                           HttpContext context) throws AuthenticationException {

        BasicCookieStore cookieStore = (BasicCookieStore) context.getAttribute(ClientContext.COOKIE_STORE);

        DefaultHttpClient client = new DefaultHttpClient();

        // authentication cookie is set automatically when 
        // login response arrived
        client.setCookieStore(cookieStore);

        HttpPost loginRequest = new HttpPost(loginUri);
        String requestBody = String.format(
                REQUEST_BODY, 
                credentials.getUserPrincipal().getName(), 
                credentials.getPassword());
        loginRequest.setHeader("Content-Type", "application/json");

        try {
            loginRequest.setEntity(new StringEntity(requestBody));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        try {
            HttpResponse response = client.execute(loginRequest);
            int code = response.getStatusLine().getStatusCode();
            EntityUtils.consume(response.getEntity());
            if(code != 200) {
                throw new IllegalStateException("Authentication problem");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            loginRequest.reset();            
        }

        return null;
    }

    @Override
    public void processChallenge(Header header) throws MalformedChallengeException {}

    @Override
    public String getSchemeName() {
        return NAME;
    }

    @Override
    public String getParameter(String name) {
        return null;
    }

    @Override
    public String getRealm() {
        return null;
    }

    @Override
    public boolean isConnectionBased() {
        return false;
    }

    @Override
    public boolean isComplete() {
        return false;
    }

    @Override
    public Header authenticate(Credentials credentials, 
                           HttpRequest request) throws AuthenticationException {
        // not implemented
        return null;
    }

}

MyAuthSchemeFactory.java

public class MyAuthSchemeFactory implements AuthSchemeFactory {

    private final URI loginUri;

    public MyAuthSchemeFactory(URI uri) {
        this.loginUri = uri;
    }

    @Override
    public AuthScheme newInstance(HttpParams params) {
        return new MyAuthScheme(loginUri);
    }

}

MyAuthStrategy.java

public class MyAuthStrategy implements AuthenticationStrategy {

    @Override
    public boolean isAuthenticationRequested(HttpHost authhost, 
                                             HttpResponse response, 
                                             HttpContext context) {

        return response.getStatusLine().getStatusCode() == 401;
    }

    @Override
    public Map<String, Header> getChallenges(HttpHost authhost, 
                                             HttpResponse response, 
                                             HttpContext context) throws MalformedChallengeException {

        Map<String, Header> challenges = new HashMap<>();
        challenges.put("myscheme", new BasicHeader("WWW-Authenticate", "myscheme"));

        return challenges;
    }

    @Override
    public Queue<AuthOption> select(Map<String, Header> challenges, 
                                    HttpHost authhost, 
                                    HttpResponse response, 
                                    HttpContext context) throws MalformedChallengeException {

        AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute(ClientContext.AUTHSCHEME_REGISTRY);
        AuthScheme authScheme = registry.getAuthScheme(MyAuthScheme.NAME, new BasicHttpParams());
        CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
        Credentials credentials = credsProvider.getCredentials(new AuthScope(authhost));

        Queue<AuthOption> options = new LinkedList<>();
        options.add(new AuthOption(authScheme, credentials));

        return options;
    }

    @Override
    public void authSucceeded(HttpHost authhost, AuthScheme authScheme, HttpContext context) {}

    @Override
    public void authFailed(HttpHost authhost, AuthScheme authScheme, HttpContext context) {}

}

App.java

public class App {

    public static void main(String[] args) throws IOException, URISyntaxException {

        URI loginUri = new URI("https://example.com/api/v3/users/login");

        AuthSchemeRegistry schemeRegistry = new AuthSchemeRegistry();
        schemeRegistry.register(MyAuthScheme.NAME, new MyAuthSchemeFactory(loginUri));

        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
                new AuthScope("example.com", 8065), 
                new UsernamePasswordCredentials("user1@example.com", "secret"));

        DefaultHttpClient client = new DefaultHttpClient();
        client.setCredentialsProvider(credentialsProvider);
        client.setTargetAuthenticationStrategy(new MyAuthStrategy());
        client.setAuthSchemes(schemeRegistry);
        client.setCookieStore(new BasicCookieStore());


        String getResourcesUrl = "https://example.com:8065/api/v3/myresources/";

        HttpGet getResourcesRequest = new HttpGet(getResourcesUrl);
        getResourcesRequest.setHeader("x-requested-with", "XMLHttpRequest");

        try {
            HttpResponse response = client.execute(getResourcesRequest);
            // consume response
        } finally {
            getResourcesRequest.reset();
        }   

        // further requests won't call MyAuthScheme.authenticate()

        HttpGet getResourcesRequest2 = new HttpGet(getResourcesUrl);
        getResourcesRequest2.setHeader("x-requested-with", "XMLHttpRequest");

        try {
            HttpResponse response2 = client.execute(getResourcesRequest);
            // consume response
        } finally {
            getResourcesRequest2.reset();
        }   

        HttpGet getResourcesRequest3 = new HttpGet(getResourcesUrl);
        getResourcesRequest3.setHeader("x-requested-with", "XMLHttpRequest");

        try {
            HttpResponse response3 = client.execute(getResourcesRequest);
            // consume response
        } finally {
            getResourcesRequest3.reset();
        }   

    }

}
相关问题