Posted In: Spring, Spring Boot, Spring Security

Example – Spring Boot – Security – Default Basic Authentication – SSL/HTTPS enabled

 

1. Create Spring boot project. Refer create-eclipse-spring-boot-application-step-by-step

 
 

2. Choose security checkbox or add following maven entry

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

 
 

3. Set up Spring Boot embedded Tomcat to use HTTPS port 8443

 

3a. Create certificate using following command. Use the same folder as your project

E:\workspace_neon>cd Example201721>keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650

 

3b. Add SSL entries to application.properties

logging.level.org.springframework.web=DEBUG
server.port: 8443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: changeit
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat

 
 

4. Start spring boot application

Find following log. It is printed at log level info and above.

Using default security password: 264a55ac-a392-408f-b8a1-f1c5e519f493

.......

org.springframework.security.web.header.HeaderWriterFilter@3569edd5, 
org.springframework.security.web.authentication.logout.LogoutFilter@4b61d0c6,
org.springframework.security.web.authentication.www.BasicAuthenticationFilter@1f14f20c, 
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@42d236fb,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@19f9d595, 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5d28bcd5,
org.springframework.security.web.session.SessionManagementFilter@430fa4ef, 
org.springframework.security.web.access.ExceptionTranslationFilter@cd7f1ae,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@1bd81830]

If you do not see the log change the log level using following entry in application.properties

logging.level.org.springframework.web=DEBUG

 
 

5. Create simple rest service

@RestController
public class ExampleService {
	@RequestMapping(value = "/employee/list",
	        consumes = "application/json",
	        method = { RequestMethod.POST })
	public @ResponseBody Object getEmployeeList(
	        ServletRequest req, ServletResponse res,
	        @RequestBody String requestJson) throws Exception {
		System.out.println("getEmployeeList() called");
		return "{'response' : 'success'}";
	}
...............

 
 

6. Now try the service in your browser https://localhost:8443/employee/list. You should get unauthorized error

You may get certificate error. Click on Advance, add to exception and enter user name and password.
 

 

 

{
  "timestamp": 1487267495668,
  "status": 401,
  "error": "Unauthorized",
  "message": "Bad credentials",
  "path": "/employee/list"
}

 
 

7. Add basic authentication information. Use same password found in step 3

Using default security password: 264a55ac-a392-408f-b8a1-f1c5e519f493

 

7a. Using POSTMAN browser plugin

Make sure you are able to access the url in Chrome before trying this as it will use certificate from Chrome to proceed
 

 

 

7b. Using curl

Convert username:password to BASE64 using online tools like base64encode
user:264a55ac-a392-408f-b8a1-f1c5e519f493
Pass it to curl in header –header “Authorization:Basic dXNlcjoyNjRhNTVhYy1hMzkyLTQwOGYtYjhhMS1mMWM1ZTUxOWY0OTM=”
use –insecure (-k) in case you want to tell curl to ignore that the server can’t be verified.

curl -X POST https://localhost:8443/employee/list -k --header "Content-Type:application/json" --header "Authorization:Basic dXNlcjoyNjRhNTVhYy1hMzkyLTQwOGYtYjhhMS1mMWM1ZTUxOWY0OTM=" -d "{}"

 

7c. Using MockMvc

Note – This test will always fail as Spring Boot will regenerate password each time you restart the server. Set your own password to run this test.
In case you encounter following error import cert in your JRE using keytool command. Follow steps explained in section 7d
Error – sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class Example201720ApplicationTests {

	@Autowired
	private MockMvc mvc;

	@Test
	public void contextLoads() {
	}

	@Test
	public void testBasicAuth() throws Exception {
		mvc.perform(MockMvcRequestBuilders.post("/employee/list")
		.header("Authorization", "Basic dXNlcjoyNjRhNTVhYy1hMzkyLTQwOGYtYjhhMS1mMWM1ZTUxOWY0OTM=")
		.accept(MediaType.TEXT_HTML)
		.contentType(MediaType.APPLICATION_JSON)
		.content("{}")).andExpect(status().isOk())
		.andExpect(content().string(
		equalTo("{'response' : 'success'}")));
	}
}

 

7d. Using RestTemplate

In case you encounter following error import cert in your JRE using keytool command
Error – sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Solution
Download certificate from browser

 
 

 

keytool -import -alias tomcat -keystore  E:/programs/jre118_112/lib/security/cacerts -file C:/Users/trupti/Downloads/localhosttomcat.der

 

	@Test
	public void testBasicAuth2() throws Exception {
		try {
			String url = "https://localhost:8443/employee/list";
			CloseableHttpClient httpClient
		      = HttpClients.custom()
		        .setSSLHostnameVerifier(new NoopHostnameVerifier())
		        .build();
		    HttpComponentsClientHttpRequestFactory requestFactory 
		      = new HttpComponentsClientHttpRequestFactory();
		    requestFactory.setHttpClient(httpClient);
		    RestTemplate rest = new RestTemplate(requestFactory);
			// rest.setErrorHandler(new RestErrorHandler());
			rest.getMessageConverters().add(
			        new MappingJackson2HttpMessageConverter());
			rest.getMessageConverters()
			        .add(new StringHttpMessageConverter());
			HttpHeaders headers = new HttpHeaders();
			headers.add("Authorization",
			        "Basic dXNlcjoyNjRhNTVhYy1hMzkyLTQwOGYtYjhhMS1mMWM1ZTUxOWY0OTM=");
			headers.setContentType(MediaType.APPLICATION_JSON);
			HttpEntity<String> entity = new HttpEntity<String>(
			        "{}", headers);
			ResponseEntity<String> response = rest.exchange(url,
			        HttpMethod.POST, entity, String.class);
			System.out.println(response.getBody());
		} catch (HttpStatusCodeException e) {
			e.printStackTrace();
			if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
				String responseString = e
				        .getResponseBodyAsString();
				System.out.println(responseString);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 
 

8. Now service should return valid 200 response

 
 

Tags: , , , , ,

by , on February 17th, 2017

  • Categories