gum*_*ins 11 java authentication apache-httpclient-4.x
我想使用Apache HttpClient 4+将经过身份验证的请求发送到HTTP服务器(实际上,我需要针对不同的服务器实现)并且仅在需要时,当auth令牌不存在时自动进行身份验证(或重新身份验证)或它死了.
为了进行身份验证,我需要使用包含用户凭据的JSON发送POST请求.
如果cookie中没有提供认证令牌,则一个服务器返回状态代码401,另一个服务器返回响应主体中带有AUTH_REQUIRED文本的500.
我HttpClient通过CredentialsProvider适当设置Credentials,尝试实现自己AuthScheme并注册它并取消注册其余标准版本,玩了很多不同的版本.
我也试着自己设定AuthenticationHandler.当isAuthenticationRequested调用时,我正在分析HttpResponse哪个作为方法参数传递,并通过分析状态代码和响应体来决定返回什么.我希望this(isAuthenticationRequested() == true)强制客户端通过调用AuthScheme.authenticate(我的AuthScheme实现返回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
Run Code Online (Sandbox Code Playgroud)
接下来我该怎么办?我正朝着正确的方向前进吗?
UPDATE
我几乎达到了我所需要的水平.不幸的是,我无法提供完全正常工作的项目源,因为我无法提供对我的服务器的公共访问.这是我的简化代码示例:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
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) {}
}
Run Code Online (Sandbox Code Playgroud)
MyApp.java
public class MyApp {
public static void main(String[] args) throws IOException {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
Credentials credentials = new UsernamePasswordCredentials("user@example.com", "secret");
credsProvider.setCredentials(AuthScope.ANY, credentials);
HttpClientContext context = HttpClientContext.create();
context.setCookieStore(new BasicCookieStore());
context.setCredentialsProvider(credsProvider);
CloseableHttpClient client = HttpClientBuilder.create()
// my server requires this header otherwise it returns response with code 500
.setDefaultHeaders(Collections.singleton(new BasicHeader("x-requested-with", "XMLHttpRequest")))
.setTargetAuthenticationStrategy(new MyAuthStrategy())
.build();
String url = "https://example.com/some/resource";
String url2 = "https://example.com/another/resource";
// ======= REQUEST 1 =======
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request, context);
String responseText = EntityUtils.toString(response.getEntity());
request.reset();
// ======= REQUEST 2 =======
HttpGet request2 = new HttpGet(url);
HttpResponse response2 = client.execute(request2, context);
String responseText2 = EntityUtils.toString(response2.getEntity());
request2.reset();
// ======= REQUEST 3 =======
HttpGet request3 = new HttpGet(url2);
HttpResponse response3 = client.execute(request3, context);
String responseText3 = EntityUtils.toString(response3.getEntity());
request3.reset();
client.close();
}
}
Run Code Online (Sandbox Code Playgroud)
httpcore:4.4.6
httpclient:4.5.3
可能这不是最好的代码,但至少它是有效的.
请看MyAuthScheme.authenticate()方法中的评论.
这对我来说与 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;
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
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) {}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)