I'm facing an issue authenticating my app under junit-test.
I've got a CustomAuthenticationProvider
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserRepository userRepository;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//businness logic
return auth;
}
}
A SecurityConfig, that uses it
#Configuration
#EnableWebSecurity
#Import(CustomAuthenticationProvider.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//some permission filters here
}
}
And my test, that is supposed to call on a Rest API and make sure, that answer is Ok.
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class})
#SpringApplicationConfiguration(classes = {MyApplication.class},locations = {"/dbContext.xml"})
#TestPropertySource("/application.properties")
#WebIntegrationTest
public class SimpleTest {
#Autowired
protected WebApplicationContext webAppContext;
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(webAppContext);
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication user = customAuthenticationProvider.authenticate(new UsernamePasswordAuthenticationToken("admin", "123"));
context.setAuthentication(user);
SecurityContextHolder.setContext(context);
}
#Test
public void makeSureLoginIsOk() {
given().when().get("/myurl").then().statusCode(200);
}
}
Well, the test always failes, because GET returns 401, instead of 200.
Can anyone help, what it wrong with SecurityContext?
Finally found an answer:
This post How to Mock the security context in Spring MVC for testing was helpfull
#Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(webAppContext);
RestAssuredMockMvc.enableLoggingOfRequestAndResponseIfValidationFails();
Authentication user = customAuthenticationProvider.authenticate(new UsernamePasswordAuthenticationToken("admin", "123"));
RestAssuredMockMvc.authentication(user); //add this
SecurityContextHolder.getContext().setAuthentication(user);
}
#Test
public void makeSureLoginIsOk() {
RestAssuredMockMvc.get("/myurl").then().statusCode(200); //change given to RestAssured
}
Related
I use spring-security-oauth2.
I would like to custom configure the /oauth/token end-point.
After registration a new client, I need to use login and password from this client and create a token for him and return this token.
Can I custom configure this /oauth/token endpoint?
Can I return a custom error if I receive incorrect information for this end-point (incorrect login or password)?
Can I put here a client-id and client-secret and don`t enter this in front-end?
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private TokenStore tokenStore;
#Autowired
private JwtAccessTokenConverter jwtTokenEnhancer;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).tokenEnhancer(jwtTokenEnhancer).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
}
I'm slowly moving into understanding Spring Cloud Security. I've created an authorization service and it works when authorizing and returning a token, but doesn't return any current user details when using that token, when getting these from OAuth2Authentication. This two lines return an NPE:
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
OAuth2Authentication user isn't instantiated and is null, while I understood that it should be by default instantiated by Spring Security. Maybe I am missing some configuration beans? Thanks in advance!
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableAuthorizationServer
public class AuthorizationServiceApplication {
#RequestMapping(value = {"/user"}, produces = "application/json")
public Map <String, Object> user (OAuth2Authentication user) {
Map <String, Object> userInfo = new HashMap <>();
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userInfo;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.class
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Value("${token.secret}")
private String secret;
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
public OAuth2Config (AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
WebSecurityConfigurer.class
#Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean () throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
// TODO: implemented DB stuff
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser("deniss").password("deniss1").roles("USER")
.and()
.withUser("oksana").password("oksana").roles("USER, ADMIN");
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(csrfTokenRepository());
}
}
In the end I got it working like this:
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
public class AuthorizationServiceApplication {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping("/user")
public Principal user(Principal user) {
log.info("User information display for User: " + user.getName());
return user;
}
#Bean
UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("deniss").password("deniss").roles("USER").build());
return manager;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
//TODO: refactor to recieve this info from config server
#Value("${token.secret}")
private String secret;
#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("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
}
SecurityConfigurer.class
#Configuration
#EnableGlobalAuthentication
public class SecurityConfigurer extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
// TODO: implemented DB stuff
#Override
public void init(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService);
}
}
I met the same problem, it seems a bug of new version. I changed Spring Boot 1.5.9.RELEASEļ¼Spring Cloud Edgware.RELEASE backed to Spring Boot 1.4.4.RELEASEļ¼Spring Cloud Camden.SR5, the problem disappeared.
Set security.oauth2.resource.filter-order=3 configuration property to restore the ordering used in previous versions. See enter link description here for more details.
I've implemented BasicAuth in my SpringBoot application to authenticate few URLs. Somewhat like:
Configuration class
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${auth.authenticated}")
private String[] allAuthenticated;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(allAuthenticated).authenticated()
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) { 1
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
// .... Rest of the code
}
application.yml
auth.authenticated: /onlineshop/v1/ecart,/onlineshop/v1/wishlist
It is working fine, but I want to unit test this.
I was thinking of a simple test case where I can direclty make an HTTP request to either /onlineshop/v1/ecart OR /onlineshop/v1/wishlist and somehow check if they are authenticated or not.
I came across this MockMVC section, and coded below class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=WebSecurityConfig.class)
public class BasicAuthTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity()) 1
.build();
}
#Test
public void shouldBeAuthenticated() throws Exception {
mvc.perform(get("/onlineshop/v1/ecart").with(httpBasic("user","password"))).andExpect(status().isOk());
}
}
But every time it gives me 404 error. So I'm not sure if I've configured it properly or what?
Also if there is some other better way to test BasicAuth, kindly suggest.
Thank You
I'm new to spring security. Try to use it for project with a rest backend. For my backend certain urls need to be open, certain urls need to have httpbasic auth / https and certain urls need a token authentication.
I'm trying to set this up using a test with web mvc. Trying to test it by using controller methods:
#RequestMapping(value="/auth/signup", method=RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void test(){
System.err.println("Controller reached!");
}
#RequestMapping(value="/auth/login", method=RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void test2(){
System.err.println("Controller reached!");
}
My Spring Security Config locks like the following:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
#Configuration
#Order(1)
public static class FreeEndpointsConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/signup").permitAll()
.and().csrf().disable();
}
}
#Configuration
#Order(2)
public static class HttpBasicAuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/login").hasAnyRole("USER")
.and().httpBasic()
.and().csrf().disable();
}
}
}
My Test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={RootContext.class, WebSecurityConfig.class})
#WebAppConfiguration
public class AccountSecurityTest {
#Autowired
private WebApplicationContext wac;
private MockMvc securityMockMvc;
#Before
public void SetupContext() {
securityMockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity()).build();
}
#Test
public void testSigInFree() throws Exception {
MockHttpServletRequestBuilder post = post("/auth/signup");
securityMockMvc.perform(post).andExpect(status().isOk());
}
#Test
public void testLoginHttpBasic() throws Exception {
MockHttpServletRequestBuilder post = post("/auth/login");
securityMockMvc.perform(post).andExpect(status().isOk());
}
}
The testmethod "testLoginHttpBasic" is green. But I would expect a failure, because i'm trying to configure / enforce httpbasic authentication. Where is my mistake?
Change
http.authorizeRequests().antMatchers("/auth/signup").permitAll()
to
http.antMatcher("/auth/signup").authorizeRequests().anyRequest().permitAll()
and
http.antMatcher("/auth/login").authorizeRequests().anyRequest().hasAnyRole("USER")
to
http.authorizeRequests().antMatchers("/auth/login").hasAnyRole("USER").
Your second test will fail.
Why do you need this change?
http.authorizeRequests()... creates a SecurityFilterChain that matches every URL. As soon as one SecurityFilterChain matches the request all subsequent SecurityFilterChains will never be evaluated. Hence, your FreeEndpointsConfig consumed every request.
With http.antMatcher("...") in place you restrict every SecurityFilterChain to a particular URL (pattern). Now FreeEndpointsConfig matches only /auth/signup and HttpBasicAuthConfig /auth/login.
Small improvement
You can make several URLs like paths to static resources (js, html or css) public available with WebSecurity::configure. Override WebSecurity::configure in your WebSecurityConfig
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers("/auth/signup");
}
and FreeEndpointsConfig isn't required anymore.
I've done a very simple demo app to try testing of Spring Boot security.
This is my App configuration
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#SpringBootApplication
public class DemoApplication extends WebSecurityConfigurerAdapter {
#Autowired
private SecurityService securityService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(securityService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
http.csrf().disable();
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
My UserDetailsService implementation accepts all users with password 'password' granted admin role to the 'admin' user.
#Service
public class SecurityService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities;
if (username.equals("admin")) {
authorities = Arrays.asList(() -> "ROLE_ADMIN", () -> "ROLE_BASIC");
} else {
authorities = Arrays.asList(() -> "ROLE_BASIC");
}
return new User(username, "password", authorities);
}
}
And I finally created a simple test to check it:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
#WebAppConfiguration
public class DemoApplicationTests {
#Autowired
private AuthenticationManager authenticationManager;
#Test
public void thatAuthManagerUsesMyService() {
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken("admin", "password")
);
assertTrue(auth.isAuthenticated());
}
}
I expected the test to pass, but I got a BadCredentialsException instead. After debugging I realized that the AuthenticationManager injected by Spring in the test is not the one I configured. While digging the object in the eclipse debugger I saw that the UserDetailsServer was an InMemoryUserDetailsManager.
I also checked that the configure() methods in DemoApplication are called. What am I doing wrong?
Per WebSecurityConfigurerAdapter api reference for authenticationManagerBean()
Override this method to expose the
AuthenticationManager from configure(AuthenticationManagerBuilder) to
be exposed as a Bean.
So just override authenticationManagerBean() in your WebSecurityConfigurerAdapter and expose it as a bean with #Bean.
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}