How to migrade Spring cloud security oauth2 from Angel.SR3 to Brixton.M3 - spring-security-oauth2

I just upgraded Spring Cloud from Angel.SR3 to Brixton.M3 and the Oauth2 security part now is broken. I've been stuck in it for few days.
Here is the Angel.SR3 code.
Application.java
package com.gaoshin;
import java.security.Principal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.security.oauth2.resource.EnableOAuth2Resource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#EnableOAuth2Resource
#RestController
#RequestMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public class Application {
#RequestMapping(value="/", method = RequestMethod.GET)
public String hi(Principal p) {
return p!=null ? "Hello " + p.getName() : "Hello guest";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ResourceServer.java
package com.gaoshin;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
#Configuration
#EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().cacheControl().disable();
http.authorizeRequests().antMatchers("/**").permitAll();
}
}
application.yml:
server:
port: 9020
spring:
oauth2:
resource:
userInfoUri: http://apie.pr720.com:8081/auth/user
Everything works fine. I can test with curl command below and it returns the right user name.
curl "http://localhost:9020" -H "Authorization: Bearer 2d48412a-2e5a-425c-acbe-ab2ab31295dc"
After upgrading to Brixton.M3, since EnableOAuth2Resource is no longer exist, I replaced it with EnableOAuth2Sso. The same curl command now returns error below,
{"timestamp":1448484209821,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"URI must not be null","path":"/"}
The log says:
java.lang.IllegalArgumentException: URI must not be null
at org.springframework.util.Assert.notNull(Assert.java:115) ~[spring-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.util.UriComponentsBuilder.fromUriString(UriComponentsBuilder.java:186) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.util.DefaultUriTemplateHandler.initUriComponentsBuilder(DefaultUriTemplateHandler.java:106) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.util.DefaultUriTemplateHandler.expand(DefaultUriTemplateHandler.java:100) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:556) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:475) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.oauth2.provider.token.RemoteTokenServices.postForMap(RemoteTokenServices.java:138) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.provider.token.RemoteTokenServices.loadAuthentication(RemoteTokenServices.java:107) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager.authenticate(OAuth2AuthenticationManager.java:83) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:150) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]

As Dave mentioned at https://github.com/spring-cloud/spring-cloud-security/issues/81, the configuration is different.
The config prefix changed from spring.oauth2.* to security.oauth2.*

Related

How to auto login after successful registration in spring boot?

Yes, I know this question is already asked. But regard to this question I have a different problem as well. authenticationManager object of AuthenticationManager in SecurityServiceImpl class which is not authenticating the details which caused me to stuck at this point.
I have tried to give message after each step by which I can track the particular code whether they are working or not and I found in SecurityServiceImpl class that it does not work after this code
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
I don't know this is just my understanding. Help me out of this problem and please provide me if you have better code snippet to solve this particular problem.
SecurityConfiguration
package com.demo.practice.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
private UserDetailsService userDetailsService;
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/register","/login").permitAll()
.antMatchers("/student/**").hasAuthority("STUDENT")
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/dashboard");
http.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
UserServiceImpl
package com.demo.practice.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.demo.practice.model.Credentials;
import com.demo.practice.model.Role;
import com.demo.practice.model.User;
import com.demo.practice.repository.UserRepository;
#Service
public class UserServiceImpl implements UserServiceInterface {
#Autowired
private BCryptPasswordEncoder encoder;
#Autowired
UserRepository userRepo;
#Override
public void saveUser(User user,Credentials credential) {
user.setCredential(credential);
user.getCredential().setUsername(user.getEmail());
user.getCredential().setRoles(Role.STUDENT);
user.getCredential().setPassword(encoder.encode(user.getCredential().getPassword()));
userRepo.save(user);
}
#Override
public User findUserByEmail(String name) {
User user=userRepo.findUserByEmail(name);
return user;
}
}
UserDetailsServiceImpl
package com.demo.practice.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.demo.practice.model.Credentials;
import com.demo.practice.repository.CredentialRepository;
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
CredentialRepository credRepo;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Credentials credential =credRepo.findByUsername(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority(credential.getRoles().toString()));
return new org.springframework.security.core.userdetails.User(credential.getUsername(), credential.getPassword(), grantedAuthorities);
}
}
SecurityServiceImpl
package com.demo.practice.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
#Service
public class SecurityServiceImpl implements SecurityServiceInterface {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class);
#Override
public String findLoggedInUsername() {
Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails)userDetails).getUsername();
}
return null;
}
#Override
public void autologin(String username, String password) {
System.out.println("in autologin "+username);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
System.out.println("in autologin at userdetails"+userDetails);
logger.info("after userdetails "+userDetails);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(),userDetails.getAuthorities());
logger.info("in autologin after usernamepasswordauthentication! ", usernamePasswordAuthenticationToken);
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
logger.info("after authentication manager ", usernamePasswordAuthenticationToken);
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
logger.debug(String.format("Auto login %s successfully!", username));
}
else System.out.println("auto login failed");
}
}
you are missing one thing, if you want to have an authentication session based, you need to add WebAuthenticationDetails to the token like:
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(principalUser, null, List.of(new SimpleGrantedAuthority(principalUser.getRole())));
token.setDetails(new WebAuthenticationDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
the WebAuthenticationDetails from the doc:
Records the remote address and will also set the session Id if a session already exists (it won't create one).
for more info about the login/logout process take a look at:
https://github.com/pezetem/spring-security-angular-skeleton/blob/master/src/main/java/com/pezetem/blog/code/spring_security_custom_authorizers/security/SecurityController.java
UPDATE:
ok, there could be another reason why it is not working for you. Spring Security adds default prefix to the roles. It is equal to ROLE_. It means that when you have an endpoint configured for .hasAuthority("STUDENT") role must be equal to ROLE_STUDENT, take a look at the line where you create a user, you assign role with Role.STUDENT and the enum value should be ROLE_STUDENT not STUDENT

Spring MockMvc doesn't contain cookies

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.

Spring 4 mvc I always get Request method 'POST' not supported

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

How to get oauth token using authorization server endpoint in spring boot

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

Unable to invoke method in mocked service from mocked controller using mockito with spring web mvc

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");

Resources