【问题标题】:Spring Rest API Authentication through another REST APISpring Rest API 通过另一个 REST API 进行身份验证
【发布时间】:2015-04-01 23:17:30
【问题描述】:

我有一个 spring rest api,它通过数据库使用 base64 身份验证进行保护。然后是否有可能采用另一个 rest api 并以某种方式通过第一个 api 进行身份验证?

【问题讨论】:

  • 顺便说一句,后端使用 UserDetailsS​​ervice
  • 为什么不使用 OAuth?这是一种比 HTTP 基本身份验证更安全的方式。这是一篇可能会有所帮助的博客文章。 stormpath.com/blog/secure-your-rest-api-right-way

标签: security rest authentication spring-security


【解决方案1】:

您是否考虑过使用基于 OAuth 的身份验证和 API 密钥管理来保护您的 API。从security perspective 来看,HTTP 基本身份验证并不理想,用户名和密码还有另一组 security issues for APIs

无论哪种方式,您都可以考虑使用Stormpath 来让这对您来说真的很容易。看看this guide,它同时支持HTTP basic和OAuth。

此示例代码将让您清楚地了解这是多么容易。

假设您想要公开一个名为startEngines() 的操作并且您想要保护它。您还需要公开一个新操作以获取 访问令牌,在此示例中为 String getAccessToken(ApiKey)

您的用户将运行如下内容:

@Test
public void executeSomeOauth2AuthenticatedOperation() {

    String userApiKeyPath = System.getProperty("user.home") + "/.stormpath/apiKey_4Yrc0TJ5sBFldwtu6nfzf5.properties";
    ApiKey userApiKey = ApiKeys.builder().setFileLocation(userApiKeyPath).build();

    //Developer requests access token
    String accessToken = getAccessToken(userApiKey);

    //Developer executes an authenticated operation (e.g startEngines()) with the provided accessToken
    if (startEngines(accessToken)) {
        System.out.print("Client-side message: Execution allowed");
    } else {
        System.out.print("Client-side message: Execution denied");
    }
}

您的代码将如下所示:

String path = System.getProperty("user.home") + "/.stormpath/apiKey.properties";
String applicationUrl = "https://api.stormpath.com/v1/applications/2TqboZ1qo73eDM4gTo2H94";
Client client = Clients.builder().setApiKey(ApiKeys.builder().setFileLocation(path).build()).build();
Application application = client.getResource(applicationUrl, Application.class);

public String getAccessToken(ApiKey apiKey) {
    HttpRequest request = createOauthAuthenticationRequest(apiKey);
    AccessTokenResult accessTokenResult = (AccessTokenResult) application.authenticateApiRequest(request);
    System.out.println(accessTokenResult.getScope());
    return accessTokenResult.getTokenResponse().getAccessToken();
}

public boolean startEngines(String accessToken) {
    HttpRequest request = createRequestForOauth2AuthenticatedOperation(accessToken);
    try {
        OauthAuthenticationResult result = application.authenticateOauthRequest(request).execute();
        System.out.println(result.getAccount().getEmail() + " is about to start the engines!");

        doStartEngines(); //Here you will actually call your internal doStartEngines() operation
        return true;

    } catch (AccessTokenOauthException e) {

        //This accessToken is not allowed to start the engines
        System.out.print("AccessToken: " + accessToken + " just tried to start the engines. He is not allowed to do so.");
        return false;

    }
}

private HttpRequest createOauthAuthenticationRequest(ApiKey apiKey) {
    try {
        String credentials = apiKey.getId() + ":" + apiKey.getSecret();

        Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
        headers.put("Accept", new String[]{"application/json"});
        headers.put("Content-Type", new String[]{"application/x-www-form-urlencoded"});
        headers.put("Authorization", new String[]{"Basic " + Base64.encodeBase64String(credentials.getBytes("UTF-8"))});

        Map<String, String[]> parameters = new LinkedHashMap<String, String[]>();
        parameters.put("grant_type", new String[]{"client_credentials"});

        HttpRequest request = HttpRequests.method(HttpMethod.POST)
                .headers(headers)
                .parameters(parameters)
                .build();
        return request;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

private HttpRequest createRequestForOauth2AuthenticatedOperation(String token) {
    try {
        Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
        headers.put("Accept", new String[]{"application/json"});
        headers.put("Authorization", new String[]{"Bearer " + token});
        HttpRequest request = HttpRequests.method(HttpMethod.GET)
                .headers(headers)
                .build();
        return request;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

private void doStartEngines() {
    System.out.println("Server-side message: Engines started!!!");
}

为了简单起见,我让所有这些代码在同一台机器上运行(客户端和服务器端代码之间没有网络通信)。您实际上需要使用 Spring 通过 Rest API 公开 startEngines()String getAccessToken(ApiKey),并让最终用户通过网络访问它们。

试一试,它应该是一个非常简单快捷的解决方案。 :)

完全披露-我在Stormpath工作

【讨论】:

    【解决方案2】:

    Base64 是一种编码机制,而不是一种身份验证方案。我假设您的意思是您使用的是basic authentication,这是Authorization HTTP 标头中编码的用户名和密码base64。

    如果其他服务也使用基本身份验证并针对同一凭据数据库检查用户名和密码,那么您将能够使用相同的 base64 编码字符串针对其他 REST 服务进行身份验证。

    【讨论】:

      【解决方案3】:

      我所要做的就是让我的第二个 API 中捕获所有请求的类调用第一个 API,该 API 已经连接到我的数据库,发送基本身份验证标头以验证凭据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-16
        • 2017-02-04
        • 1970-01-01
        • 2013-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-29
        相关资源
        最近更新 更多