1

RESTful 認証サービスを構築していますが、接続に問題があります。応答を取得しようとすると、恐ろしい「サーバーから返された HTTP 応答コード: 400」が表示されます。これは非常に奇妙に思えます。リクエストを送信するときにこのエラーが発生すると思います。

このサービスを Spring-Security AuthenticationProvider の一部として使用しています。現在、テスト用の実際のサービスではなく、シミュレーターを使用しています。シミュレーターにもサービスにも接続しません。

呼び出し方法は次のとおりです。

public <T> T invoke(String service, Object request, Class<T> responseType) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    URL url = new URL("http://localhost:8888/simulator/rest" + service);
    HttpURLConnection uc = (HttpURLConnection) url.openConnection();

    uc.setRequestMethod("POST");
    uc.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
    uc.setDoOutput(true);
    uc.setDoInput(true);
    uc.connect();

    OutputStream out = uc.getOutputStream();
    mapper.writeValue(out, request);
    out.close();

    return mapper.readValue(uc.getInputStream(), responseType);
}

このメソッドを呼び出すコードは次のとおりです。

UsernamePasswordAuthenticationToken token = 
    new UsernamePasswordAuthenticationToken("thomas", "thomas");
UsernamePasswordAuthenticationToken response = 
    invoke("/authenticate", token, UsernamePasswordAuthenticationToken.class);

呼び出されるシミュレーター メソッドは次のとおりです。

@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
@ResponseBody
public UsernamePasswordAuthenticationToken authenticate(
    @RequestBody UsernamePasswordAuthenticationToken userNameAndPassword) {

    String userName = (String) userNameAndPassword.getPrincipal();
    String password = (String) userNameAndPassword.getCredentials();
    if (userName.equalsIgnoreCase("thomas")) {
        if (userName.equals(password)) {
            UsernamePasswordAuthenticationToken response = 
                new UsernamePasswordAuthenticationToken(
                    userName,
                        password, 
                        new ArrayList<GrantedAuthority>());
                return response;
            }
        }
        return new UsernamePasswordAuthenticationToken(userName, password);
    }

エラーの原因となる行は次のとおりです。

mapper.readValue(uc.getInputStream(), responseType);

このコードに問題が見られない場合。ずっと眺めていたに違いない。問題に新しい目が必要です。

ところで、この REST サービスとシミュレーターは、他の操作で正常に使用されています。

追加情報:

エラーは uc.getInputStream() 呼び出しで発生します。HttpURLConnection.inputStream = null。

また、リクエストのヘッダーは次のとおりです。

これが役立つ場合、このリクエストのヘッダーは次のとおりです。

[WARN] 400 - POST /simulator/rest/authenticate (127.0.0.1) 1417 bytes
   Request headers
      Content-Type: application/json;charset=UTF-8
      X-Tenant: 1
      Authorization: 0000013770b132a1dfcbfe0a694542b244534e0e406cfa857660c904daa89af91d0ac769
      Cache-Control: no-cache
      Pragma: no-cache
      User-Agent: Java/1.6.0_26
      Host: localhost:8888
      Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
      Connection: keep-alive
      Content-Length: 112
   Response headers
      Set-Cookie: JSESSIONID=1r02p7yvm8mzs;Path=/
      X-UA-Compatible: IE=9
      Content-Type: text/html; charset=iso-8859-1
      Content-Length: 1417

これが私のトークンコードです:

import java.util.List;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

public class SerializedAuthenticationToken extends UsernamePasswordAuthenticationToken {
    private static final long serialVersionUID = 2783395505630241326L;

    private Object principal;
    private Object credentials;

    /**
     * no-arg constructor to satisfy Serializable.
     */
    public SerializedAuthenticationToken() {
        super(null, null);
    }

    /**
     * constructor.
     */
    public SerializedAuthenticationToken(Object principal, Object credentials) {
        super(null, null);
        setPrincipal(principal);
        setCredentials(credentials);
    }

    /**
     * constructor with List<GrantedAuthorities>.
     */
    public SerializedAuthenticationToken(Object principal, Object credentials, List<GrantedAuthority> authorities) {
        super(null, null, authorities);
        setPrincipal(principal);
        setCredentials(credentials);
    }

    public Object getPrincipal() {
        return principal;
    }

    public void setPrincipal(Object principal) {
        this.principal = principal;
    }

    public Object getCredentials() {
        return credentials;
    }

    public void setCredentials(Object credentials) {
        this.credentials = credentials;
    }

    public void setName(Object name) {

    }
}

また、新しいスタック トレースを取得しています。

org.codehaus.jackson.map.JsonMappingException: Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead (through reference chain: com.mckesson.shared.util.SerializedAuthenticationToken["authenticated"])
4

2 に答える 2

1

これを行うには、ダミー トークンを作成する必要があります。これを確認するための私のテストは次のとおりです。

public class JacksonTest {

@Test
public void test() throws Exception {

    ObjectMapper mapper = new ObjectMapper();

    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("thomas", "thomas");
    String tokenStr = mapper.writeValueAsString(token);
    /* WON'T WORK
     UsernamePasswordAuthenticationToken auth = mapper.readValue(tokenStr, UsernamePasswordAuthenticationToken.class);
     */
    MyToken auth = mapper.readValue(tokenStr, MyToken.class);
    String authStr = mapper.writeValueAsString(auth);

    Assert.assertThat(tokenStr.equals(authStr), is(true));
}


private static class MyToken extends UsernamePasswordAuthenticationToken {
    private Object principal;
    private Object credentials;

    private static final long serialVersionUID = -5045038656629236029L;

    public MyToken() {

        super(null, null);
    }

    public MyToken(Object principal, Object credentials) {

        super(null, null);
        this.principal = principal;
        this.credentials = credentials;
    }

    /**
     * @return the principal
     */
    public Object getPrincipal() {
        return principal;
    }

    /**
     * @param principal the principal to set
     */
    public void setPrincipal(Object principal) {
        this.principal = principal;
    }

    /**
     * @return the credentials
     */
    public Object getCredentials() {
        return credentials;
    }

    /**
     * @param credentials the credentials to set
     */
    public void setCredentials(Object credentials) {
        this.credentials = credentials;
    }


    public void setName(Object name) {

    }

}

}

于 2012-05-20T22:12:50.307 に答える
0

正確な問題を特定することはできませんが、問題の原因は、非常に合理的な抽象化が存在する何かを達成するために、このような低レベルの API に不必要にドロップダウンしているという事実だと思います。Spring の RestTemplate を見て、より洗練された手段や、RESTful サービスに対するクライアント コードの記述を確認してください。あなたが間違っていることが何であれ、RestTemplate がそれを正しくする可能性があります。

于 2012-05-18T18:42:42.863 に答える