Tengo 2 primavera de aplicaciones web que proporciona 2 separe el conjunto de los servicios. La Aplicación Web de 1 ha de Primavera Seguridad implementado utilizando una autenticación basada en usuario.

Ahora, la Aplicación Web de 2 necesita para tener acceso al servicio de Web App 1. Normalmente, se utiliza el RestTemplate clase para hacer peticiones a otros servicios web.

¿Cómo podemos pasar las credenciales de autenticación en la solicitud de Aplicación de Web 2 Web de la Aplicación 1

InformationsquelleAutor Prabhu R | 2011-01-06

7 Comentarios

  1. 10

    Yo estaba en la misma situación. Aquí está mi solución.

    Servidor primavera seguridad config

    <sec:http>
        <sec:intercept-url pattern="/**" access="ROLE_USER" method="POST"/>
        <sec:intercept-url pattern="/**" filters="none" method="GET"/>
        <sec:http-basic />
    </sec:http>
    
    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="${rest.username}" password="${rest.password}" authorities="ROLE_USER"/>
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>

    Lado del cliente RestTemplate config

    <bean id="httpClient" class="org.apache.commons.httpclient.HttpClient">
        <constructor-arg ref="httpClientParams"/>
        <property name="state" ref="httpState"/>
    </bean>
    
    <bean id="httpState" class="CustomHttpState">
        <property name="credentials" ref="credentials"/>
    </bean>
    
    <bean id="credentials" class="org.apache.commons.httpclient.UsernamePasswordCredentials">
        <constructor-arg value="${rest.username}"/>
        <constructor-arg value="${rest.password}"/>
    </bean>
    
    <bean id="httpClientFactory" class="org.springframework.http.client.CommonsClientHttpRequestFactory">
        <constructor-arg ref="httpClient"/>
    </bean>
    
    
    <bean class="org.springframework.web.client.RestTemplate">
        <constructor-arg ref="httpClientFactory"/>                
    </bean>

    Personalizado HttpState aplicación

    /**
     * Custom implementation of {@link HttpState} with credentials property.
     *
     * @author banterCZ
     */
    public class CustomHttpState extends HttpState {
    
        /**
         * Set credentials property.
         *
         * @param credentials
         * @see #setCredentials(org.apache.commons.httpclient.auth.AuthScope, org.apache.commons.httpclient.Credentials)
         */
        public void setCredentials(final Credentials credentials) {
            super.setCredentials(AuthScope.ANY, credentials);
        }
    
    }

    Maven dependencia

    <dependency>
       <groupId>commons-httpclient</groupId>
       <artifactId>commons-httpclient</artifactId>
       <version>3.1</version>
    </dependency>
    • Puede usted explicar por qué la necesidad de un http personalizado del estado de la clase?
    • HttpState#setCredentials no es setter (también conocido como un descriptor de acceso), ya que requiere de dos parámetros. Así credenciales no es un POJO de campo y no se puede acceder en la Primavera de configuración xml.
    • Cuando ejecuto mi aplicación registra que: [org.springframework.beans.GenericTypeAwarePropertyDescriptor] - [Invalid JavaBean property 'credentials' being accessed! Ambiguous write methods found next to actually used [public void a.b.c.d.CustomHttpState.setCredentials(org.apache.commons.httpclient.Credentials)]: ...(error continues). Es habitual?
    • No, no es lo habitual. Hizo usted exactamente copiar y pegar la clase CustomHttpState?
    • Su realmente debe especificar requires-channel="https" en cada uno de sus intercept-url de las entradas. Nunca se debe enviar nombre de usuario/contraseña de información a través de onu-cifrada de http.
    • ¿Cómo podemos conseguir el resto.nombre de usuario y el resto.la contraseña?
    • Usted necesita configurar una primavera de la propiedad de marcador de posición, consulte stackoverflow.com/questions/7542506/…
    • Gran solución. Tuve que añadir también esta a la Primavera de configuración: <bean id=»httpClientParams» clase=»org.apache.commons.httpclient.params.HttpClientParams»> <propiedad nombre=»authenticationPreemptive» value=»true» /> <propiedad nombre=»connectionManagerClass» value=»org.apache.commons.httpclient.MultiThreadedHttpConnectionManager» /> </bean>

  2. 13

    Aquí es una solución que funciona muy bien con la Primavera 3.1 y Apache HttpComponents 4.1 he creado basado en varias respuestas en este sitio y leer la primavera RestTempalte código fuente. Estoy compartiendo con la esperanza de salvar a los demás del tiempo, creo que la primavera sólo debe tener algún código como este, construido en el pero no lo hace.

    RestClient client = new RestClient();
    client.setApplicationPath("someApp");
    String url = client.login("theuser", "123456");
    UserPortfolio portfolio = client.template().getForObject(client.apiUrl("portfolio"), 
                             UserPortfolio.class);

    A continuación se encuentra la Fábrica de la clase que las configuraciones de seguridad de la HttpComponents contexto a ser el mismo en cada solicitud con la RestTemplate.

    public class StatefullHttpComponentsClientHttpRequestFactory extends 
                       HttpComponentsClientHttpRequestFactory
    {
        private final HttpContext httpContext;
    
        public StatefullHttpComponentsClientHttpRequestFactory(HttpClient httpClient, HttpContext httpContext)
        {
            super(httpClient);
            this.httpContext = httpContext;
        }
    
        @Override
        protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri)
        {
            return this.httpContext;
        }
    }

    A continuación se Statefull Resto de la plantilla que puede utilizar para recordar las cookies, una vez que inicie la sesión se recordará el JSESSIONID y la envió en posteriores solicitudes.

    public class StatefullRestTemplate extends RestTemplate
    {
        private final HttpClient httpClient;
        private final CookieStore cookieStore;
        private final HttpContext httpContext;
        private final StatefullHttpComponentsClientHttpRequestFactory statefullHttpComponentsClientHttpRequestFactory;
    
        public StatefullRestTemplate()
        {
            super();
            httpClient = new DefaultHttpClient();
            cookieStore = new BasicCookieStore();
            httpContext = new BasicHttpContext();
            httpContext.setAttribute(ClientContext.COOKIE_STORE, getCookieStore());
            statefullHttpComponentsClientHttpRequestFactory = new StatefullHttpComponentsClientHttpRequestFactory(httpClient, httpContext);
            super.setRequestFactory(statefullHttpComponentsClientHttpRequestFactory);
        }
    
        public HttpClient getHttpClient()
        {
            return httpClient;
        }
    
        public CookieStore getCookieStore()
        {
            return cookieStore;
        }
    
        public HttpContext getHttpContext()
        {
            return httpContext;
        }
    
        public StatefullHttpComponentsClientHttpRequestFactory getStatefulHttpClientRequestFactory()
        {
            return statefullHttpComponentsClientHttpRequestFactory;
        }
    }

    Aquí es una clase para representar un resto de cliente de modo que usted puede llamar a una aplicación protegida con la primavera
    de seguridad.

    public class RestClient
    {
    private String host = "localhost";
    private String port = "8080";
    private String applicationPath;
    private String apiPath = "api";
    private String loginPath = "j_spring_security_check";
    private String logoutPath = "logout";
    private final String usernameInputFieldName = "j_username";
    private final String passwordInputFieldName = "j_password";
    private final StatefullRestTemplate template = new StatefullRestTemplate();
    /**
    * This method logs into a service by doing an standard http using the configuration in this class.
    * 
    * @param username
    *            the username to log into the application with
    * @param password
    *            the password to log into the application with
    * 
    * @return the url that the login redirects to
    */
    public String login(String username, String password)
    {
    MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
    form.add(usernameInputFieldName, username);
    form.add(passwordInputFieldName, password);
    URI location = this.template.postForLocation(loginUrl(), form);
    return location.toString();
    }
    /**
    * Logout by doing an http get on the logout url
    * 
    * @return result of the get as ResponseEntity
    */
    public ResponseEntity<String> logout()
    {
    return this.template.getForEntity(logoutUrl(), String.class);
    }
    public String applicationUrl(String relativePath)
    {
    return applicationUrl() + "/" + checkNotNull(relativePath);
    }
    public String apiUrl(String relativePath)
    {
    return applicationUrl(apiPath + "/" + checkNotNull(relativePath));
    }
    public StatefullRestTemplate template()
    {
    return template;
    }
    public String serverUrl()
    {
    return "http://" + host + ":" + port;
    }
    public String applicationUrl()
    {
    return serverUrl() + "/" + nullToEmpty(applicationPath);
    }
    public String loginUrl()
    {
    return applicationUrl(loginPath);
    }
    public String logoutUrl()
    {
    return applicationUrl(logoutPath);
    }
    public String apiUrl()
    {
    return applicationUrl(apiPath);
    }
    public void setLogoutPath(String logoutPath)
    {
    this.logoutPath = logoutPath;
    }
    public String getHost()
    {
    return host;
    }
    public void setHost(String host)
    {
    this.host = host;
    }
    public String getPort()
    {
    return port;
    }
    public void setPort(String port)
    {
    this.port = port;
    }
    public String getApplicationPath()
    {
    return applicationPath;
    }
    public void setApplicationPath(String contextPath)
    {
    this.applicationPath = contextPath;
    }
    public String getApiPath()
    {
    return apiPath;
    }
    public void setApiPath(String apiPath)
    {
    this.apiPath = apiPath;
    }
    public String getLoginPath()
    {
    return loginPath;
    }
    public void setLoginPath(String loginPath)
    {
    this.loginPath = loginPath;
    }
    public String getLogoutPath()
    {
    return logoutPath;
    }
    @Override
    public String toString()
    {
    StringBuilder builder = new StringBuilder();
    builder.append("RestClient [\n serverUrl()=");
    builder.append(serverUrl());
    builder.append(", \n applicationUrl()=");
    builder.append(applicationUrl());
    builder.append(", \n loginUrl()=");
    builder.append(loginUrl());
    builder.append(", \n logoutUrl()=");
    builder.append(logoutUrl());
    builder.append(", \n apiUrl()=");
    builder.append(apiUrl());
    builder.append("\n]");
    return builder.toString();
    }
    }
    • Para aquellos usuarios que quieren usar este código en Android SDK 23+ , por favor, agregue gradle dependencia «org.apache.httpcomponents:httpcore:4.3.2» en su módulo de gradle archivo.
  3. 3

    La RestTemplate es muy básica y limitada; no parece ser una manera fácil de hacer esto. La mejor manera es, probablemente, para implementar compendio de basic auth en la Web de App 1. A continuación, utilice Apache HttpClient directamente para acceder al resto de servicios de Aplicación Web 2.

    Que se dice, para la prueba de que fue capaz de evitar esto con un gran hack. Básicamente utiliza el RestTemplate para presentar el inicio de sesión (j_spring_security_check), analizar los jsessionid de los encabezados de solicitud, a continuación, envíe el resto de solicitud. Aquí está el código, pero dudo que sea la mejor solución para la producción de código.

    public final class RESTTest {
    public static void main(String[] args) {
    RestTemplate rest = new RestTemplate();
    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String s, SSLSession sslsession) {
    return true;
    }
    });
    //setting up a trust store with JCA is a whole other issue
    //this assumes you can only log in via SSL
    //you could turn that off, but not on a production site!
    System.setProperty("javax.net.ssl.trustStore", "/path/to/cacerts");
    System.setProperty("javax.net.ssl.trustStorePassword", "somepassword");
    String jsessionid = rest.execute("https://localhost:8443/j_spring_security_check", HttpMethod.POST,
    new RequestCallback() {
    @Override
    public void doWithRequest(ClientHttpRequest request) throws IOException {
    request.getBody().write("j_username=user&j_password=user".getBytes());
    }
    }, new ResponseExtractor<String>() {
    @Override
    public String extractData(ClientHttpResponse response) throws IOException {
    List<String> cookies = response.getHeaders().get("Cookie");
    //assuming only one cookie with jsessionid as the only value
    if (cookies == null) {
    cookies = response.getHeaders().get("Set-Cookie");
    }
    String cookie = cookies.get(cookies.size() - 1);
    int start = cookie.indexOf('=');
    int end = cookie.indexOf(';');
    return cookie.substring(start + 1, end);
    }
    });
    rest.put("http://localhost:8080/rest/program.json;jsessionid=" + jsessionid, new DAO("REST Test").asJSON());
    }

    }

    Nota para que esto funcione, usted necesita para crear un almacén de confianza en la JCA para la conexión SSL en realidad puede ser hecho. Supongo que usted no quiere tener la Primavera de la Seguridad de inicio de sesión sobre la llanura HTTP para un sitio de producción, ya que sería un enorme agujero de seguridad.

    • me doy cuenta de que respondió a esto hace más de un año, pero me gustaría llevar su oferta a «publicar el código» si todavía está disponible. 🙂 gracias
    • Código publicado, pero no lo he probado de nuevo con una versión reciente de la Primavera. Tiene la RestTemplate no se ha actualizado?
  4. 2

    Hay una manera simple de hacer esto en caso de que usted es alguien que está buscando un simple llamada y no una API consumidor.

    HttpClient client = new HttpClient();
    client.getParams().setAuthenticationPreemptive(true);
    Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setRequestFactory(new CommonsClientHttpRequestFactory(client));
    client.getState().setCredentials(AuthScope.ANY, defaultcreds);
    • Esta parece ser la configuración de la RestTemplate RequestFactory, ¿qué sucede cuando se quiere acceder a múltiples url donde todos ellos requieren diferentes digerir auth. Usted puede agregar por host auth / o debe utilizar una nueva restTemplate ? ¿Podría dar un ejemplo
    • Supongo que tendrías que crear un restTemplate para cada tipo de digerir, o se podría extender el CommonsClientHttpRequestFactory y dependiendo de la solicitud a determinar el cual el cliente para utilizar
    • muchas gracias, este código ayudó a resolver mi problema
  5. 2

    La siguiente autenticar y retorno de la cookie de sesión:

    String sessionCookie= restTemplate.execute(uri, HttpMethod.POST, request -> {
    request.getBody().write(("j_username=USER_NAME&j_password=PASSWORD").getBytes());
    }, response -> {
    AbstractClientHttpResponse r = (AbstractClientHttpResponse) response;
    HttpHeaders headers = r.getHeaders();
    return headers.get("Set-Cookie").get(0);
    });
  6. 1

    El usuario actualmente autenticado credenciales deben estar disponibles en la Web de la Aplicación 1 en Authentication objeto, que es accesible a través de SecurityContext (por ejemplo, usted puede obtenerlo llamando SecurityContextHolder.getContext().getAuthentication()).

    Después de recuperar las credenciales, puede usarlos para acceder a la Aplicación Web de 2.

    Puede pasar «Authentiation» encabezado con RestTemplate ya sea extendiendo con un decorador (como se describe aquí) o el uso de RestTemplate.exchange() método, como se describe en este post del foro.

    • Supongo que la Aplicación Web 2 no puede ver la sesión de HTTP para la Aplicación Web de 1, así que esto probablemente no va a funcionar.
    • Lo siento, entendido mal la dirección deseada: de Web App2 a Web App 1. Cambiar mi respuesta.
  7. 0

    Esto es muy similar a la ams de la aproximación, excepto que he completamente encapsulada, la preocupación de mantener la cookie de sesión en la StatefulClientHttpRequestFactory. También por la decoración existente ClientHttpRequestFactory con este comportamiento, que puede ser utilizado con cualquier subyacente ClientHttpRequestFactory y no está atado a una aplicación específica.

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.client.ClientHttpRequest;
    import org.springframework.http.client.ClientHttpRequestFactory;
    import org.springframework.http.client.ClientHttpResponse;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.URI;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    import static java.lang.String.format;
    /**
    * Decorates a ClientHttpRequestFactory to maintain sessions (cookies)
    * to web servers.
    */
    public class StatefulClientHttpRequestFactory implements ClientHttpRequestFactory {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private final ClientHttpRequestFactory requestFactory;
    private final Map<String, String> hostToCookie = new HashMap<>();
    public StatefulClientHttpRequestFactory(ClientHttpRequestFactory requestFactory){
    this.requestFactory = requestFactory;
    }
    @Override
    public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
    final String host = request.getURI().getHost();
    String cookie = getCookie(host);
    if(cookie != null){
    logger.debug(format("Setting request Cookie header to [%s]", cookie));
    request.getHeaders().set("Cookie", cookie);
    }
    //decorate the request with a callback to process 'Set-Cookie' when executed
    return new CallbackClientHttpRequest(request, response -> {
    List<String> responseCookie = response.getHeaders().get("Set-Cookie");
    if(responseCookie != null){
    setCookie(host, responseCookie.stream().collect(Collectors.joining("; ")));
    }
    return response;
    });
    }
    private synchronized String getCookie(String host){
    String cookie = hostToCookie.get(host);
    return cookie;
    }
    private synchronized void setCookie(String host, String cookie){
    hostToCookie.put(host, cookie);
    }
    private static class CallbackClientHttpRequest implements ClientHttpRequest{
    private final ClientHttpRequest request;
    private final Function<ClientHttpResponse, ClientHttpResponse> filter;
    public CallbackClientHttpRequest(ClientHttpRequest request, Function<ClientHttpResponse, ClientHttpResponse> filter){
    this.request = request;
    this.filter = filter;
    }
    @Override
    public ClientHttpResponse execute() throws IOException {
    ClientHttpResponse response = request.execute();
    return filter.apply(response);
    }
    @Override
    public OutputStream getBody() throws IOException {
    return request.getBody();
    }
    @Override
    public HttpMethod getMethod() {
    return request.getMethod();
    }
    @Override
    public URI getURI() {
    return request.getURI();
    }
    @Override
    public HttpHeaders getHeaders() {
    return request.getHeaders();
    }
    }
    }

Dejar respuesta

Please enter your comment!
Please enter your name here