【问题标题】:Passing username and password from non-web clients to RestController Service and Retrieving it in Service将非 Web 客户端的用户名和密码传递给 RestController Service 并在 Service 中检索
【发布时间】:2019-06-03 08:46:20
【问题描述】:

部署在 Internet 上的遗留 Java 应用程序正在尝试与基于 Spring Security 的应用程序和 Intranet 上的 AuthenticationService 通信。 AuthenticationService 在发送用户名和密码时对任何用户进行身份验证。客户端代码是:

    public static void ApacheHttpClient(String userID, String userPWD){
            CredentialsProvider provider = new BasicCredentialsProvider();
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userID, userPWD);
            provider.setCredentials(AuthScope.ANY, credentials);

            HttpClient client = HttpClientBuilder.create()
              .setDefaultCredentialsProvider(provider)
              .build();
            String URL_SECURED_BY_BASIC_AUTHENTICATION = "http://localhost:8080/abc/login";
            try {

                HttpResponse response = client.execute(
                  new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION));
                int statusCode = response.getStatusLine()
                  .getStatusCode();
              System.out.println("Response Status Code : "+statusCode);
              HttpEntity entity = response.getEntity();
} catch (Exception e) {
          e.printStackTrace();
        } finally {
            client.getConnectionManager().shutdown();
        }

用于接受用户名和密码并进行身份验证的 AuthenticationService 代码。这里, String auth = request.getHeader("Authorization");总是空的

@RestController
@RequestMapping("/abc")
public class Authenticate {

     @Autowired
        private LoginService loginService;

    @RequestMapping("/login")
    public void login(HttpServletRequest request, HttpServletResponse response) {
        try {
            String auth = request.getHeader("Authorization");
            if(auth != null && auth.length() > 6){
                String userpassEncoded = auth.substring(6);  
                // Decode it, using any base 64 decoder  
                sun.misc.BASE64Decoder dec = new sun.misc.BASE64Decoder();  
                String userpassDecoded= new String(dec.decodeBuffer(userpassEncoded));
                System.out.println(" userpassDecoded = "+userpassDecoded);
             }
           } catch (Exception e) {
            e.printStackTrace();
           }

           }

通常建议使用 HTTP Basic 或 Digest Authentication。我想了解从非基于 Web 的应用程序向服务发送和检索用户名/密码的方法。就像登录应该是一个 POST 方法,用户名和密码作为查询参数或路径参数等传递

【问题讨论】:

    标签: java rest spring-security apache-httpclient-4.x


    【解决方案1】:

    好的,这里发生了几件事。

    1. 如果您的 AuthenticationService 设置正确,您的 ApacheHttpClient 将会工作。 所以让我们假设您相信您的 AuthenticationService 使用 Spring Security 工作。 请做:

    curl -i http://localhost:8080/abc/login

    您是否看到类似这样的响应?

    HTTP/1.1 401 Unauthorized WWW-Authenticate: Basic realm="Spring Security Application"

    如果您没有看到 WWW-Authenticate 标头。然后您的 AuthorizationService 未设置为处理基本身份验证。您没有配置 Spring Security 为您进行基本身份验证。

    您会看到,为基本身份验证设置的应用程序应自动使用 401 响应和标头质询客户端(如浏览器或您的 HttpClient)。

    1. 假设您不希望 Spring Security 保护您的端点。

    在这种情况下,您可以将 HttpClient 配置为始终根据请求发送 Authorization 标头(而不是像 1 中描述的那样等待质询。)

    代码如下所示:

    public static String ApacheHttpClient(String userID,
                                        String userPWD,
                                        String url,
                                        boolean preempt) throws Exception {
        CredentialsProvider provider = new BasicCredentialsProvider();
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userID, userPWD);
        provider.setCredentials(AuthScope.ANY, credentials);
        HttpClientContext context = HttpClientContext.create();
    
        HttpClientBuilder builder = HttpClientBuilder.create();
        if (preempt) {
            AuthCache authCache = new BasicAuthCache();
            authCache.put(HttpHost.create(url), new BasicScheme());
            context.setCredentialsProvider(provider);
            context.setAuthCache(authCache);
        } else {
            builder.setDefaultCredentialsProvider(provider);
        }
        HttpClient client = builder.build();
        try {
            HttpResponse response = client.execute(new HttpGet(url), context);
            int statusCode = response.getStatusLine().getStatusCode();
            System.out.println("Response Status Code : " + statusCode);
            return EntityUtils.toString(response.getEntity());
        } finally {
            client.getConnectionManager().shutdown();
        }
    }
    

    我已经创建了一些示例供您使用

    git clone https://github.com/fhanik/spring-security-community.git
    cd spring-security-community
    ./gradlew :spring-security-community-samples-basic-authentication-client:bootRun
    

    首先访问非安全页面:http://localhost:8080/non-secure

    然后访问安全页面:http://localhost:8080/secure

    输入 user/password 作为您的凭据并点击刷新几次

    我还添加了您的client 的代码,以及如何test it

    【讨论】:

    • 嗨@Filip AuthenticationService 工作得非常好,因为如果我对我的用户名和密码进行硬编码,该服务会对我进行身份验证并返回我的 ldap 详细信息。另外,如果我通过硬编码用户名和密码来调用服务,我会在客户端收到 200 OK 响应。我想要一些技巧来从客户端传递凭据以及如何在服务中以更好的方式处理它们。
    • 嗨@Pan - 看看我为你准备的示例项目。它应该解释一切,但你必须真正看一看。如果您进行硬编码,您将获得 200OK,因为您还没有真正设置支持客户期望的 401 响应挑战的基本身份验证。您还没有说我尝试了您发布的ApacheHttpClient 代码,并且我设置了preempt=true 并且它再次起作用。在编写使用 Spring Security 的应用程序时,您不必自己进行任何基本的身份验证。这一切都为你完成了。例如,如果您更改:
    • 看看我的sample controller。我不必解析基本身份验证标头。 Spring Security 为我做这件事。
    • httpBasic() 已配置,使用 curl -i localhost:8080/abc/login 我没有收到任何错误 401 响应。但是, java.lang.IllegalArgumentException: Invalid HTTP host: localhost:8080/abc/secure 错误在 authCache.put(HttpHost.create(url), new BasicScheme());在 org.apache.http.HttpHost.create(HttpHost.java:122) 当我设置 url="localhost:8080/abc/secure"
    • 嗨潘,首先将 http:// 添加到您的 URL,如果这仍然不起作用,可能是 HttpClient 库的不同版本?请查看此test(也许自己运行)
    猜你喜欢
    • 2012-12-31
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多