Http401 in tests but working in normal usage - spring

I'm using keycloak, but for tests I turned it off
keycloak.enabled = false
In my config I used almost everything to pass test as anonymous
public class KeyCloakConfig extends KeycloakWebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http
.anonymous().and() .csrf().disable()
.authorizeRequests()
.antMatchers("/ingredients").permitAll();
}
}
In test I've got 401 even when with #WithAnonymousUser
#WebMvcTest(value = IngredientController.class)
#TestPropertySource("classpath:application-development.properties")
class IngredientControllerTest {
#MockBean
IngredientService ingredientService;
#Autowired
MockMvc mockMvc;
#AfterEach
void tearDown() {
reset(ingredientService);
}
#Test
#WithAnonymousUser
void getAllIngredients() throws Exception {
given(ingredientService.findAll()).willReturn(Arrays.asList(new Ingredient(),new Ingredient()));
mockMvc.perform(get("/ingredients"))
.andExpect(status().isOk());
then(ingredientService).should().findAll();
assertThat(ingredientService.findAll()).hasSize(2);
}
}
What is funny when I use annotation #WithMockUser in test everything is ok, also turn on my web service and I go to this url everything is also ok
Also I've got basic authentication beacuse config class is disabled, I'm sure it's basic because in log I can see Using generated security password: 52cce531-b308-4d90-b76f-8984a88879fd. How to add to context slice configuration class ?
#SpringJUnitConfig(classes = TestSecurityConfig.class) doesn't work

This other stackoverflow question may help you out:
Spring Boot - How to disable Keycloak?.
The solution here is to add
spring.autoconfigure.exclude=org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration
to your application-development.properties file

Related

disable spring formlogin and basic auth

I have the following spring boot 2.0 config but I am still getting the basic auth login screen. I DO NOT want to disable all spring security like almost every post on the internet suggests. I only want to stop the form login page and basic auth so I can use my own.
I have seen all the suggestions with permitAll and exclude = {SecurityAutoConfiguration.class} and a few others that I can't remember anymore. Those are not what I want. I want to use spring security but I wan my config not Spring Boots. Yes I know many people are going to say this is a duplicate but I disagree because all the other answers are to disable spring security completely and not just stop the stupid login page.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomSecurity extends WebSecurityConfigurerAdapter {
private final RememberMeServices rememberMeService;
private final AuthenticationProvider customAuthProvider;
#Value("${server.session.cookie.secure:true}")
private boolean useSecureCookie;
#Inject
public CustomSecurity(RememberMeServices rememberMeService, AuthenticationProvider customAuthProvider) {
super(true);
this.rememberMeService = rememberMeService;
this.bouncerAuthProvider = bouncerAuthProvider;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/**").antMatchers("/webjars/**").antMatchers("/swagger-resources/**")
.antMatchers("/swagger-ui.html");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable().formLogin().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).headers().frameOptions().disable();
http.authenticationProvider(customAuthProvider).authorizeRequests().antMatchers("/health").permitAll()
.anyRequest().authenticated();
http.rememberMe().rememberMeServices(rememberMeService).useSecureCookie(useSecureCookie);
http.exceptionHandling().authenticationEntryPoint(new ForbiddenEntryPoint());
}
}
If you want to redirect to your own login page, i can show your sample code and configuration
remove the http.httpBasic().disable().formLogin().disable();, you should set your own login page to redirect instead of disable form login
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/my_login").permitAll().and().authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/my_login");
}
then create your own LoginController
#Controller
public class LoginController {
#RequestMapping("/my_login")
public ModelAndView myLogin() {
return new ModelAndView("login");
}
}
you can specified the login with thymeleaf view resolver

How test correctly the LoginForm through #WithAnonymousUser

About Spring Security
For the class that extends WebSecurityConfigurerAdapter I have the following for the configure method:
.and()
.formLogin()
.loginPage("/perfom/login")
.loginProcessingUrl("/perform_/login")
.usernameParameter("username")//default
.passwordParameter("password")//default
.defaultSuccessUrl("/welcome")
.failureUrl("/perfom/login?error") //default is /login?error
.permitAll()
It works fine in runtime. Until here no problems.
When Spring MVC Test plays I have the following:
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build();
}
#Test
#WithAnonymousUser
public void saveOneHtmlGetForNonAuthenticatedPrincipalTest() throws Exception {
resultActions = mockMvc.perform(get(url).with(csrf().asHeader()).accept(MediaType.TEXT_HTML_VALUE)
.header("Accept-Language", ClientUserLocale.ENGLISH.getLocale().toString())).andDo(print());
resultActions.andExpect(status().isFound())
.andExpect(redirectedUrl("/perfom/login"));
}
The test fails with the following error message:
java.lang.AssertionError:
Redirected URL expected:</perfom/login>
but was:<http://localhost/perfom/login>
Not sure if it is the expected behavior or something missing, not sure why
http://localhost/appears.
I had the same problem and was able to fix it using redirectedUrlPattern.
In your case the solution should be:
.andExpect(redirectedUrlPattern("**/perfom/login"))

Unable to set up basic authentication with Spring Boot REST API

I'm trying to set up a RESTful API with Spring Boot and I'm trying to enable basic authentication. How come I keep hitting a 403 Access Denied error? I'm sending my credentials as a header in Postman (see image attached). If I remove .anyRequest.authenticated(), it works fine. I don't want to remove that though because I would like basic authentication for every endpoint. What am I doing wrong?
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/h2-console/*").permitAll()
.anyRequest().authenticated();
http.csrf().disable();
http.headers().frameOptions().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
Controller.java
#RestController
public class Controller {
#RequestMapping("/test")
public String index() {
return "Greetings from Spring Boot!";
}
}
After digging around in the Spring docs, it seems I understand what each of the chained method calls are for.
Anyway, the simple answer is that I needed .and().httpBasic() to enable Basic HTTP Authentication over my REST API.
.anyRequest().authenticated() simply mandates that every request is authenticated, but did not specify what method. Adding basic authentication means we can use basic auth to authenticate a user.
See more.

Spring Boot setup with multiple authentication providers (API+Browser)

My application serves both API and browser. I've implemented API Token authentication with all custom providers and filter. The configuration now seems to interfere with the browser version.
I have two questions that I need advice on how to solve, as I'm not getting anywhere after digging through the documentation and other examples.
1) My StatelessAuthenticationFilter is being called despite a request
coming from the browser. I have e.g. specified the request matcher to "/api/**". Why is that?
2) The AuthenticationManager have not registered two AuthenticationProviders. This is my conclusion after debugging my StatelessAuthenticationFilter that's being called wrongly.
Here's the configuration classes that I have
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Order(1)
#Configuration
public static class A extends WebSecurityConfigurerAdapter {
#Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
#Autowired
ApiEntryPoint apiEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
StatelessAuthenticationFilter filter = new StatelessAuthenticationFilter();
AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/api/**");
filter.setRequiresAuthenticationRequestMatcher(requestMatcher);
filter.setAuthenticationManager(super.authenticationManager());
http.csrf().disable()
.exceptionHandling().authenticationEntryPoint(apiEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/user/register");
}
}
#Configuration
public static class B extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new DaoAuthenticationProvider());
}
}
}
As you can see, B class doesn't specify anything, yet when I access localhost:8080 the StatelessAuthenticationFilter is called. What is going on here?
In class A you are configuring the StatelessAuthenticationFilter to use a requestMatcher. Whatever you do with that, spring does not know or care about that.
You must also restrict your security configuration using
http.antMatcher("/api/**")
otherwise its configured for every URI and the StatelessAuthenticationFilter will be invoked for every request, exactly as you described.
You should also annotate class A and B with #Order as shown in the example at multiple-httpsecurity

WIthMockUser doesn't work

I can't use #WithMockUser, no matter what I do it doesn't provide authorized user in spring test. Maybe it's because its resource server but..
Config:
#Configuration
#EnableResourceServer
class ResourceServerConfig extends ResourceServerConfigurerAdapter
...
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().and().authorizeRequests().antMatchers("/**").hasRole("USER");
}
...
}
And test class
#BeforeClass
public void setup(){
mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity())
.build();
}
#Test
#WithMockUser()
public void shouldAddValueToStore() throws Exception {
ResultActions response = mockMvc.perform(post("/bucket/key")
.content("value"));
...
}
I keep getting 401 Access is denied (user is anonymous); redirecting to authentication entry point. I've tried with setting usernames, roles in annotation parameters, passing with(user..) to mockMvc, nothing helps. It's spring security 4.0.4.RELEASE.
ok, it works after setting 'stateless' parameter to 'false' as described here: https://github.com/spring-projects/spring-security-oauth/issues/385
and using with(user..
ResultActions response = mockMvc.perform(post("/bucket/key")
.with(user("john#example.com"))
.content("value"));
WithMockUser is still not working, but this method is good enough for now.

Resources