我有2个Spring Web应用程序,它们提供2套独立的服务。Web App 1具有使用基于用户的身份验证实现的Spring Security。
现在,Web App 2需要访问Web App 1的服务。通常,我们将使用RestTemplate类向其他Web服务发出请求。
我们如何将Web App 2的请求中的身份验证凭据传递给Web App 1
我当时的情况也一样。这是我的解决方案。
服务器-Spring Security配置
<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>
客户端RestTemplate配置
<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>
自定义HttpState实现
/** * 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依赖
<dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.1</version> </dependency>
这是一个与Spring 3.1和Apache HttpComponents 4.1配合使用的很好的解决方案,我是根据该站点上的各种答案创建的,并阅读了Spring RestTempalte源代码。我希望能够节省其他人的时间,我认为spring应该只内置一些像这样的代码,但事实并非如此。
RestClient client = new RestClient(); client.setApplicationPath("someApp"); String url = client.login("theuser", "123456"); UserPortfolio portfolio = client.template().getForObject(client.apiUrl("portfolio"), UserPortfolio.class);
下面是Factory类,该类将HttpComponents上下文设置为与使用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; } }
以下是Statefull Rest模板,你可以使用它来记住cookie,一旦登录,它就会记住JSESSIONID并在后续请求中发送。
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; } }
这是一个代表其他客户端的类,因此你可以调用受spring安全保护的应用程序。
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(); } }