I have a controller protected with HTTP Basic authentication.
I setup the app to use session cookies and it works.
However when I test the controller using MockMvc, a successful authentication does not give me any cookie.
Web configuration:
package world.pyb.spring.cookiesdemo;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("argentina").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http.httpBasic()
.and().authorizeRequests().antMatchers(HttpMethod.GET, "/hello").authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
//#formatter:on
}
}
Simple controller:
package world.pyb.spring.cookiesdemo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HelloController {
#RequestMapping("/hello")
public String index() {
return "Greetings from Spring Boot!";
}
}
Simple controller test that doesn't give me the session cookie:
package world.pyb.spring.cookiesdemo;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class HelloControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/hello")
.with(SecurityMockMvcRequestPostProcessors.httpBasic("admin", "argentina"))
)
// prints "Cookies = []"
.andDo(MockMvcResultHandlers.print())
.andExpect(cookie().exists("JSESSIONID"))
.andExpect(status().is2xxSuccessful())
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
}
}
Related questions:
Why does Spring MockMvc result not contain a cookie?
Unit Testing /login in Spring MVC using MockMvc
Spring MVC testing (security Integration test), JSESSIONID is not present
Some answers suggest not to use MockMvc but I'd like to keep using it if possible.
As shown in the question's code, MockMvcResultMatcher includes support for cookies.
This will work fine, as long as the controller under test itself delivers the cookie. The problem here is that the cookie is delivered by Spring Security, which is a wrapper around your controller. MockMvc is testing your controller directly, and not testing your controller running in its real HTTP server, as would be required to test the security-layer cookies.
That's why TestRestTemplate, which invokes your controller in its full server context, delivers a more thorough test environment.
Notice however that as of Spring 5, the newer approach to running-server API testing is based on WebTestClient.
Related
My issue is that I get always Request method 'POST' not supported !
I'm trying to test my rest web webservice. Here is the WS :
#RequestMapping(value = "/addGouvernorate", method = RequestMethod.POST, produces = { "application/json" })
public ResponseEntity<Gouvernorate> addGouvernorateee(#RequestBody Gouvernorate gouvernorate) throws IOException {
gouvernorateService.addGouvernorat(gouvernorate);
return new ResponseEntity<Gouvernorate>(gouvernorate, HttpStatus.OK);
}
i'm trying to call it with jsoup as follows :
package test.sofiane.rqs;
import java.io.IOException;
import org.jsoup.Connection.Method;
import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;
import org.springframework.security.crypto.codec.Base64;
public class Test {
public static void main(String[] args) throws IOException {
String base64login = new String(Base64.encode("sof:1".getBytes()));
Response docs = Jsoup.connect("http://localhost:8080/site/data/addGouvernorate")
.data("name", "name")
.data("delegation", "delegation")
.data("district", "district")
.data("postalCode", "postalCode")
.method(Method.POST)
.execute();
}
}
I get the exception :
Exception in thread "main" org.jsoup.HttpStatusException: HTTP error fetching URL. Status=405, URL=http://localhost:8080/mintad/data/addGouvernorate
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:459)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:434)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:181)
at test.sofiane.rqs.Test.main(Test.java:21)
And with Postman application I get
Etat HTTP 405 - Request method 'POST' not supported
I'm using spring security also.
as follow
package com.site.spring.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* #author Sofiane
*/
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
//.antMatchers("/admin/**").denyAll()//.access("hasRole('ADMIN')")
.and().formLogin().loginPage("/login")
.defaultSuccessUrl("/welcome").usernameParameter("username").passwordParameter("password")
.and().exceptionHandling().accessDeniedPage("/404");
// #formatter:off
}
}
A guess : it can be the csrftoken which I have to send since I'm behind spring security but how to generate it ?
Please help because it's driving me insane guys
Hi friends i am developing my own oauth2 server having resource server and authorization server configuration i have partially completed my own oauth2 server but unable to get oauth token using token endpoint http://localhost:8080/oauth/token.
OAuthConfig:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
#Configuration
public class OAuth2Config {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login").permitAll().and()
.authorizeRequests().anyRequest().authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token",
"password").scopes("openid");
}
}
}
SpringSecurityConfig:
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Created by qasim on 12/3/16.
*/
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(-10)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/oauth/authorize").authenticated().and()
.authorizeRequests().anyRequest().permitAll().and().httpBasic().and()
.authorizeRequests().antMatchers("/oauth/confirm_access").authenticated();
}
}
In OauthConfig class i have used inmemory to store client details.
At this point i do not have anything in my resource server though i do not nedd it right now . I just want to create token which i am sure it will generated via Authorization server.
Now when i open this url 'http://localhost:8080/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://localhost:8080/fd/redirectauthorization' i got this screen.
After providing credentials
This is my custome oauth screen i am not using default oauth approval screen
My OAuth Controller is
#Controller
#RequestMapping("/oauth")
public class OauthController {
#Autowired
ClientDetailsService clientDetailsService;
#RequestMapping("/confirm_access")
public String confirmAccess(HttpServletRequest httpServletRequest, HttpSession httpSession){
// logic
return "oauthAccess";
}
}
On Approval Controller takes me to the redirected url with some code value
Code for redirection
#Controller
#RequestMapping("/fd")
public class RedirectController {
#RequestMapping("/redirectauthorization")
public String redirectauthorization(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, ModelMap modelMap, HttpSession httpSession){
return "authorizationcode";
}
}
Now i got code appended in url using this code i try to get token using curl command but getting Bad Credential error or Unauthorized error(401) as shown in below picture
curl acme:acmesecret#localhost:8080/oauth/token -d 'grant_type=authorization_code&code=WeIYSm'
Can anyone guide me to generate oauth token
Your curl should look more like this:
curl acme:acmesecret#localhost:8080/oauth/token -d
I am trying to create a JUnit test case for spring mvc rest controller and service that is accessed by controller.
I am using Mockito for doing the above. I'm am able to successfully invoke the mock injected controller from the test case and also when I debug I see that the mocked userService is available in the mocked contoller, but the method within the service object is not getting invoked. While debugging I observe that it just steps over the service method call.
I do not see any kind of exception too.
I am using
Maven 3
Spring 4.1.1 Release version
Junit 4.11
Java version 1.6
I have pasted my code below:
1. Test Class
package controller;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/spring/application-config.xml")
public class UserControllerTest {
private MockMvc mockMvc;
#Mock
private IUserService userService;
#InjectMocks
private UserController controller;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testRegisterUser()throws Exception{
User user = new User();
user.setCity("City");
user.setTown("Town");
user.setFirstname("Name");
user.setLastname("LastName");
user.setPassword("abc#123");
user.setUsername("abc#gmail.com");
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(object);
}
}
2. Controller under Test
package com.myPackage.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
#RestController
#RequestMapping("/service/user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
#Autowired
private IUserService userService;
public IUserService getUserService() {
return userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
#RequestMapping(value = "/register/", method = RequestMethod.POST,headers="Accept=application/json")
public String registerUser(#RequestBody User user) {
logger.info("register user:"+user.toString());
String userDetails = userService.registerUser(user);
logger.info("user registered:"+userDetails);
return userDetails;
}
}
3. Service to be invoked within controller in test
The service method registerUser is to be invoked from controller above. I do not see any logs getting printed on the console when we run the test case. Also when debugging I see that userSerivice instance of type like this - IUserService$$EnhancerByMockitoWithCGLIB$$c00081eb is created but when I dig deep all to see the list of methods under mockhandlers registered methods, I only see 'toString' method in the list of invocations. Not sure if this gives some indication on why this method 'registerUser' in the service class below is not getting invoked during test case.
package com.myPackage.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import com.myPackage.dao.IUserDao;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
#Service("userService")
public class UserService implements IUserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
#Autowired
private IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
#Override
public String registerUser(User user) {
// TODO Auto-generated method stub
logger.info("New User for registration:",user);
if(user!=null && user.getUsername()!=null){
User alreadyExist = userDao.getUserByLogin(user.getUsername());
if(alreadyExist!=null){
return "userAlreadyExists";
}
}
logger.info("New User for registration complete:",user.getUser_id());
return null;
}
}
4. Interface implemented by UserService class in point 3 above
IUserService. The mocked userService in test class above is of type IUserService.
package com.myPackage.service;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
public interface IUserService {
public String registerUser(User user);
}
Your method (read code) within userService will never actually be invoked from your controller because, it has been mocked out by mockito.
You have defined this yourself by doing:
#Mock
private IUserService userService;
#InjectMocks
private UserController controller;
MockitoAnnotations.initMocks(this);
If you want to assert that the method has been called you can using,
verify(userService, times(1)).registerUser(any(User.class));
#Jonas: Thanks for your insight. If i don't mock userService then at the time of unit test, userService is found null under userController method matching registerUser. And so after reading somewhere i mocked userService. Also after doing the changes as suggested by you, the mocked service method still does not get invoked - Below are my changes -
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
Mockito.verify(userService, Mockito.times(1)).registerUser(Mockito.any(User.class));
If you want to test the actual service method, then autowire it:
#Autowired
private IUserService userService;
If you want to use a mock like you're doing now, you need to stub the method:
when(userService.registerUser(any(User.class))).thenReturn("expected string");
I developed a Spring MVC webapp with rest methods. I would love to use RestAssured to create JUnit test classes. From the documentation it looks really simple to do but I'm having some problem instead. Basically I want to use it to avoid a runtime Tomcat instance but the problem is that when I execute the JUnit test class I obtain the following exception:
org.apache.http.conn.HttpHostConnectException: Connection to http://localhost:8082 refused
...
Caused by: java.net.ConnectException: Connection refused
This is my JUnit class:
import static com.jayway.restassured.RestAssured.given;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;
import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"file:src/test/resources/spring/application-test-config.xml"})
#WebAppConfiguration
public class ExternalControllerTest {
private static final Logger logger = LoggerFactory.getLogger(ExternalControllerTest.class);
#Autowired
WebApplicationContext webApplicationContext;
#Before
public void setUp() throws Exception {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}
#Test
public void addClientDeviceId_Success() {
given().get("/api/test").then().statusCode(200);
}
}
I also tried to configure RestAssuredMockMvc directly with the controller class but the result is the same:
RestAssuredMockMvc.standaloneSetup(new ExternalController());
The controller looks like that:
#Controller
#RequestMapping("/api")
public class ExternalController {
public ExternalController() {
logger.info("ExternalController initialised!");
}
#RequestMapping(value="/test", method = RequestMethod.GET, produces={"application/json", "application/xml"})
public #ResponseBody DmsResponse test(HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_OK);
return null;
}
}
In this guide http://www.jayway.com/2014/01/14/unit-testing-spring-mvc-controllers-with-rest-assured/ is specified that "The most important aspect is that it’s simpler to get the environment started. You don’t need to bootstrap a container (for example Jetty or Tomcat), you simply initialize RestAssuredMockMvc with a context (for example a controller) and you’re ready to go." So I don't understand where the problem is. Any suggestion?
The war file deployed with Tomcat works fine.
Thanks
You are statically importing the wrong methods. You're importing given from com.jayway.restassured.RestAssured (io.restassured.RestAssured if using version 3.x) but you should statically import the methods from com.jayway.restassured.module.mockmvc.RestAssuredMockMvc (io.restassured.module.mockmvc.RestAssuredMockMvc in version 3.x). Refer to the documentation for more info.
I have seen everywhere in the Spring MVC examples on net that we can use HttpServlet request & response objects in the method parameter in the controller. But when I am using it. Given in the below code.
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class StudentController {
public void testSyntax(HttpServletRequest request, HttpServletResponse response)
{
System.out.println("Inside testSyntax");
}
}
The compiler is throwing an error. HttpServletRequest request cannot be resolved to a type.
I am using Spring MVC 3.0. Can anyone tell me the reason for it.
You are missing a couple of import statements.
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
Where you get these classes from typically is different per application container you use. My favorite example is Tomcat:
<tomcat_base_dir>/lib/servlet-api.jar