【问题标题】:Basic Authentication with Resteasy client使用 Resteasy 客户端进行基本身份验证
【发布时间】:2014-04-01 11:06:22
【问题描述】:

我正在尝试使用 REST 对在我的 jboss 上运行的登录模块执行基本身份验证。我已经找到了一个 StackOverflow 主题,它解释了如何使用凭据进行身份验证。

RESTEasy client framework authentication credentials

这不起作用。分析与 Wireshark 建立的连接我无法看到带有 Authorization: Basic 的 HTTP 包。经过更多研究,我发现这篇文章 http://docs.jboss.org/resteasy/docs/2.3.3.Final/userguide/html/RESTEasy_Client_Framework.html 描述了如何从 resteasy 将基本身份验证附加到 ApacheHttpClient4Executor

// Configure HttpClient to authenticate preemptively
// by prepopulating the authentication data cache.

// 1. Create AuthCache instance
AuthCache authCache = new BasicAuthCache();

// 2. Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put("com.bluemonkeydiamond.sippycups", basicAuth);

// 3. Add AuthCache to the execution context
BasicHttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);

// 4. Create client executor and proxy
httpClient = new DefaultHttpClient();
ApacheHttpClient4Executor executor = new ApacheHttpClient4Executor(httpClient, localContext);
client = ProxyFactory.create(BookStoreService.class, url, executor);

但这也不起作用。没有描述如何将基本身份验证的用户名和密码附加到构造中。为什么该信息与来自httpcomponent 的任何类都没有关联?

【问题讨论】:

    标签: basic-authentication resteasy


    【解决方案1】:

    考虑一下 Adam Bien 的 solution

    您可以将ClientRequestFilter 附加到RESTEasy Client,这会将Authorization 标头添加到请求中:

    public class Authenticator implements ClientRequestFilter {
    
        private final String user;
        private final String password;
    
        public Authenticator(String user, String password) {
            this.user = user;
            this.password = password;
        }
    
        public void filter(ClientRequestContext requestContext) throws IOException {
            MultivaluedMap<String, Object> headers = requestContext.getHeaders();
            final String basicAuthentication = getBasicAuthentication();
            headers.add("Authorization", basicAuthentication);
    
        }
    
        private String getBasicAuthentication() {
            String token = this.user + ":" + this.password;
            try {
                return "Basic " +
                     DatatypeConverter.printBase64Binary(token.getBytes("UTF-8"));
            } catch (UnsupportedEncodingException ex) {
                throw new IllegalStateException("Cannot encode with UTF-8", ex);
            }
        }
    }
    
    Client client = ClientBuilder.newClient()
                         .register(new Authenticator(user, password));
    

    【讨论】:

    • 感谢您的回答!请注意,Adam Bien 的解决方案在标题中写入 BASIC,而此答案使用 Basic 正确
    • 参考Are HTTP headers case-sensitive? 的讨论:它们不是——所以无论你写BASIC 还是basicBasic 都只是风格问题
    • 或者使用 class org.jboss.resteasy.client.jaxrs.BasicAuthentication 来做到这一点。
    【解决方案2】:

    可以使用 org.jboss.resteasy.client.jaxrs.BasicAuthentication,它与 resteasy-client 3.x 一起打包,专门用于基本身份验证。

    Client client = ClientBuilder.newClient();    
    ResteasyWebTarget resteasyWebTarget = (ResteasyWebTarget)client.target("http://mywebservice/rest/api");
    resteasyWebTarget.register(new BasicAuthentication("username", "passwd"));
    

    【讨论】:

    • 这是一个比公认的解决方案更好的解决方案,因为它不需要您通过直接提供 BASIC 标头来破解。
    【解决方案3】:

    您可以通过在客户端配置中调用 .header(HttpHeaders.AUTHORIZATION, authHeader) 来将原始授权标头添加到您的 REST 客户端。 凭据必须以“user:pass”格式打包在授权头中,编码为base64字节数组,然后附加到标识基本身份验证的字符串“Basic”。

    这是整个 sn-p(灵感来自 this post on baeldung

        String auth = userName + ":" + password;
        byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("ISO-8859-1")));
        String authHeader = "Basic " + new String(encodedAuth);
    
        authToken = restClient.target(restApiUrl + loginPath)
                .request()
                .accept(MediaType.TEXT_PLAIN)
                .header(HttpHeaders.AUTHORIZATION, authHeader)
                .get(String.class);
    

    这在 Resteasy 客户端中对我有用。有关信息,在使用 wget 进行测试时,我必须使用 --auth-no-challenge 标志。

    【讨论】:

    • 这是一个有价值的答案,因为它允许用户完全依赖 JAX-RS 库而不是特定的实现(如 Resteasy)。
    【解决方案4】:

    我最近升级到 resteasy-client:4.0.0.Final 以处理一些 Jackson 升级问题,我注意到设置标头的工作方式似乎有所不同(我得到 401:以前工作的每个经过身份验证的请求的授权错误) .我也找不到太多文档,(如果我的经验能代表更广泛的案例,那么 4.0.0.Final 版本只有一个月大,并且存在一些依赖性问题)。

    之前将标头注入 ClientRequestContext 的代码:

    public AddAuthHeadersRequestFilter(String username, String password) {
            this.username = username;
            this.password = password;
        }
    
        @Override
        public void filter(ClientRequestContext requestContext) throws IOException {
            String token = username + ":" + password;
            String base64Token = Base64.encodeString(token);
            requestContext.getHeaders().add("Authorization", "Basic " + base64Token);
        }
    }
    

    然后我们像这样在 ResteasyClient 上设置过滤器:

    ResteasyClient client = new ResteasyClientBuilder()
                .sslContext(buildSSLContext())
                .hostnameVerifier(buildHostVerifier())
                .build();
    
    client.register(new AddAuthHeadersRequestFilter(user, pass));
    

    但是,这似乎没有设置 HeaderDelegate,这是在 4.x(?) 和可能的早期版本中检索标头的位置。

    诀窍是在 ResteasyWebTarget 而不是 4.0.0.Final 版本中的 client 上注册该过滤器(您可能会注意到 clientBuilder 现在的工作方式略有不同也)。

        ResteasyClient client = (ResteasyClient)ResteasyClientBuilder.newBuilder()
                .sslContext(buildSSLContext())
                .hostnameVerifier(buildHostVerifier())
                .build();
    
        ResteasyWebTarget target = client.target(url);
    
        target.register(new AddAuthHeadersRequestFilter(user, pass));
    

    【讨论】:

      猜你喜欢
      • 2010-12-04
      • 1970-01-01
      • 1970-01-01
      • 2012-01-10
      • 1970-01-01
      • 2022-10-30
      • 2015-10-29
      • 2018-07-05
      • 2019-01-14
      相关资源
      最近更新 更多