Learn to add basic authentication to http requests invoked by Spring RestTemplate while accessing rest apis over the network.
1. Maven dependency
To work with Spring RestTemplate and HttpClient API, we must include spring-boot-starter-web and httpclient dependencies in
pom.xml
file.
In this RestTemplate basic authentication tutorial, we are using dependencies.
< parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >2.0.5.RELEASE</ version > < relativePath /> </ parent > < dependencies > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-security</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > < dependency > < groupId >org.apache.httpcomponents</ groupId > < artifactId >httpclient</ artifactId > < version >4.5.3</ version > </ dependency > </ dependencies > |
2. Enable BasicAuth in RestTemplate
To enable basic authentication in RestTemplate for outgoing rest requests, we shall configure CredentialsProvider into HttpClient API. This HttpClient will be used by RestTemplate to send HTTP requests to backend rest apis.
In this example, we are creating a Junit test which invokes a basic auth secured rest api.
package com.howtodoinjava.rest; import java.net.URI; import java.net.URISyntaxException; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthCache; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.context.annotation.Bean; import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestTemplate; @RunWith (SpringRunner. class ) @SpringBootTest (webEnvironment=WebEnvironment.RANDOM_PORT) public class SpringBootDemoApplicationTests { @LocalServerPort int randomServerPort; //Timeout value in milliseconds int timeout = 10_000; public RestTemplate restTemplate; @Before public void setUp() { restTemplate = new RestTemplate(getClientHttpRequestFactory()); } private HttpComponentsClientHttpRequestFactory getClientHttpRequestFactory() { HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(); clientHttpRequestFactory.setHttpClient(httpClient()); return clientHttpRequestFactory; } private HttpClient httpClient() { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials( "admin" , "password" )); HttpClient client = HttpClientBuilder .create() .setDefaultCredentialsProvider(credentialsProvider) .build(); return client; } @Test public void testGetEmployeeList_success() throws URISyntaxException { URI uri = new URI(baseUrl); ResponseEntity<String> result = restTemplate.getForEntity(uri, String. class ); //Verify request succeed Assert.assertEquals( 200 , result.getStatusCodeValue()); Assert.assertEquals( true , result.getBody().contains( "employeeList" )); } } |
3. RestTemplate basic authentication Demo
For demo purpose, we can write a simple REST API given below.
3.1. REST API
@RestController @RequestMapping (path = "/employees" ) public class EmployeeController { @Autowired private EmployeeDAO employeeDao; @GetMapping (path= "/" , produces = "application/json" ) public Employees getEmployees() { return employeeDao.getAllEmployees(); } } |
3.2. REST Security Configuration
And the security is configured globally.
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests().anyRequest().authenticated() .and() .httpBasic(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser( "admin" ) .password( "{noop}password" ) .roles( "USER" ); } } |
3.3. Access rest api by executing junit test
When we execute the junit test, it starts the application and deploys the rest api on it’s endpoint. It then invokes the rest api, and perform basic authentication upon getting 401 error.
We can verify the whole authentication process in application logs by setting
'logging.level.org.apache.http=DEBUG'
in application.properties
file.o.a.http.impl.auth.HttpAuthenticator : Authentication required o.a.http.impl.auth.HttpAuthenticator : localhost: 54770 requested authentication o.a.h.i.c.TargetAuthenticationStrategy : Authentication schemes in the order of preference: [Negotiate, Kerberos, NTLM, Digest, Basic] o.a.h.i.c.TargetAuthenticationStrategy : Challenge for Negotiate authentication scheme not available o.a.h.i.c.TargetAuthenticationStrategy : Challenge for Kerberos authentication scheme not available o.a.h.i.c.TargetAuthenticationStrategy : Challenge for NTLM authentication scheme not available o.a.h.i.c.TargetAuthenticationStrategy : Challenge for Digest authentication scheme not available o.a.http.impl.auth.HttpAuthenticator : Selected authentication options: [BASIC [complete= true ]] org.apache.http.wire : http-outgoing- 0 << "0[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "[\r][\n]" o.a.http.impl.execchain.MainClientExec : Executing request GET /employees/ HTTP/ 1.1 o.a.http.impl.execchain.MainClientExec : Target auth state: CHALLENGED o.a.http.impl.auth.HttpAuthenticator : Generating response to an authentication challenge using basic scheme o.a.http.impl.execchain.MainClientExec : Proxy auth state: UNCHALLENGED org.apache.http.headers : http-outgoing- 0 >> GET /employees/ HTTP/ 1.1 org.apache.http.headers : http-outgoing- 0 >> Accept: text/plain, application/json, application /*+json, */ * org.apache.http.headers : http-outgoing- 0 >> Host: localhost: 54770 org.apache.http.headers : http-outgoing- 0 >> Connection: Keep-Alive org.apache.http.headers : http-outgoing- 0 >> User-Agent: Apache-HttpClient/ 4.5 . 3 (Java/ 1.8 .0_171) org.apache.http.headers : http-outgoing- 0 >> Accept-Encoding: gzip,deflate org.apache.http.headers : http-outgoing- 0 >> Authorization: Basic YWRtaW46cGFzc3dvcmQ= org.apache.http.wire : http-outgoing- 0 >> "GET /employees/ HTTP/1.1[\r][\n]" org.apache.http.wire : http-outgoing- 0 >> "Accept: text/plain, application/json, application/*+json, */*[\r][\n]" org.apache.http.wire : http-outgoing- 0 >> "Host: localhost:54770[\r][\n]" org.apache.http.wire : http-outgoing- 0 >> "Connection: Keep-Alive[\r][\n]" org.apache.http.wire : http-outgoing- 0 >> "User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_171)[\r][\n]" org.apache.http.wire : http-outgoing- 0 >> "Accept-Encoding: gzip,deflate[\r][\n]" org.apache.http.wire : http-outgoing- 0 >> "Authorization: Basic YWRtaW46cGFzc3dvcmQ=[\r][\n]" org.apache.http.wire : http-outgoing- 0 >> "[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "HTTP/1.1 200 [\r][\n]" org.apache.http.wire : http-outgoing- 0 << "Set-Cookie: JSESSIONID=A54891F86FEF06160BDAFC78CE631C4E; Path=/; HttpOnly[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "X-Content-Type-Options: nosniff[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "X-XSS-Protection: 1; mode=block[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "Cache-Control: no-cache, no-store, max-age=0, must-revalidate[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "Pragma: no-cache[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "Expires: 0[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "X-Frame-Options: DENY[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "Content-Type: application/json;charset=UTF-8[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "Transfer-Encoding: chunked[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "Date: Thu, 18 Oct 2018 17:54:26 GMT[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "101[\r][\n]" org.apache.http.wire : http-outgoing- 0 << "{" employeeList ":[{" id ":1," firstName ":" Lokesh "," lastName ":" Gupta "," email ":" howtodoinjava @gmail .com "},{" id ":2," firstName ":" Alex "," lastName ":" Kolenchiskey "," email ":" abc @gmail .com "},{" id ":3," firstName ":" David "," lastName ":" Kameron "," email ":" titanic @gmail .com "}]}[\r][\n]" org.apache.http.headers : http-outgoing- 0 << HTTP/ 1.1 200 |
4. Conclusion
In this spring resttemplate example, we learned to pass basic authentication via “Authorization” header while accessing rest api. It is done in two steps. First step is to include required dependencies e.g.
spring-boot-starter-web
and httpclient
. Second step is to configure RestTemplate
and add auth details.
Share if you face any problem in resttemplate set authorization header example.
No comments:
Post a Comment