Serving static web resources in Spring Boot & Spring Security application - spring-mvc

I am trying to develop Spring Boot web application and securing it using Spring security java configuration.
After placing my static web resources in 'src/main/resources/public' as advised here in Spring blog, I am able to get the static resources. i.e hitting https://localhost/test.html in browser do serves the html content.
Problem
After I enabled Spring Security, hitting the static resource URL requires authentication.
My relevent Spring Security Java config looks like this:-
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.
authorizeRequests()
.antMatchers("/","/public/**", "/resources/**","/resources/public/**")
.permitAll()
.antMatchers("/google_oauth2_login").anonymous()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/home")
.and()
.csrf().disable()
.logout()
.logoutSuccessUrl("/")
.logoutUrl("/logout") // POST only
.and()
.requiresChannel()
.anyRequest().requiresSecure()
.and()
.addFilterAfter(oAuth2ClientContextFilter(),ExceptionTranslationFilter.class)
.addFilterAfter(googleOAuth2Filter(),OAuth2ClientContextFilter.class)
.userDetailsService(userService);
// #formatter:on
}
How should I configure antMatchers to permit static resources placed inside src/main/resources/public ?

There are a couple of things to be aware of:
The Ant matchers match against the request path and not the path of the resource on the filesystem.
Resources placed in src/main/resources/public will be served from the root of your application.
For example src/main/resources/public/hello.jpg would be served from http://localhost:8080/hello.jpg
This is why your current matcher configuration hasn't permitted access to the static resources. For /resources/** to work, you would have to place the resources in src/main/resources/public/resources and access them at http://localhost:8080/resources/your-resource.
As you're using Spring Boot, you may want to consider using its defaults rather than adding extra configuration. Spring Boot will, by default, permit access to /css/**, /js/**, /images/**, and /**/favicon.ico. You could, for example, have a file named src/main/resources/public/images/hello.jpg and, without adding any extra configuration, it would be accessible at http://localhost:8080/images/hello.jpg without having to log in. You can see this in action in the web method security smoke test where access is permitted to the Bootstrap CSS file without any special configuration.

#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**"); // #3
}
Ignore any request that starts with "/resources/". This is similar to configuring http#security=none when using the XML namespace configuration.

This may be an answer (for spring boot 2) and a question at the same time.
It seems that in spring boot 2 combined with spring security everything (means every route/antmatcher) is protected by default if you use an individual security mechanism extended from
WebSecurityConfigurerAdapter
If you don´t use an individual security mechanism, everything is as it was?
In older spring boot versions (1.5 and below) as Andy Wilkinson states in his above answer places like public/** or static/** are permitted by default.
So to sum this question/answer up - if you are using spring boot 2 with spring security and have an individual security mechanism you have to exclusivley permit access to static contents placed on any route. Like so:
#Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final ThdAuthenticationProvider thdAuthenticationProvider;
private final ThdAuthenticationDetails thdAuthenticationDetails;
/**
* Overloaded constructor.
* Builds up the needed dependencies.
*
* #param thdAuthenticationProvider a given authentication provider
* #param thdAuthenticationDetails given authentication details
*/
#Autowired
public SpringSecurityConfiguration(#NonNull ThdAuthenticationProvider thdAuthenticationProvider,
#NonNull ThdAuthenticationDetails thdAuthenticationDetails) {
this.thdAuthenticationProvider = thdAuthenticationProvider;
this.thdAuthenticationDetails = thdAuthenticationDetails;
}
/**
* Creates the AuthenticationManager with the given values.
*
* #param auth the AuthenticationManagerBuilder
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(thdAuthenticationProvider);
}
/**
* Configures the http Security.
*
* #param http HttpSecurity
* #throws Exception a given exception
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.antMatchers("/management/**").hasAnyAuthority(Role.Role_Engineer.getValue(),
Role.Role_Admin.getValue())
.antMatchers("/settings/**").hasAnyAuthority(Role.Role_Engineer.getValue(),
Role.Role_Admin.getValue())
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin()
.authenticationDetailsSource(thdAuthenticationDetails)
.loginPage("/login").permitAll()
.defaultSuccessUrl("/bundle/index", true)
.failureUrl("/denied")
.and()
.logout()
.invalidateHttpSession(true)
.logoutSuccessUrl("/login")
.logoutUrl("/logout")
.and()
.exceptionHandling()
.accessDeniedHandler(new CustomAccessDeniedHandler());
}
}
Please mind this line of code, which is new:
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
If you use spring boot 1.5 and below you don´t need to permit these locations (static/public/webjars etc.) explicitly.
Here is the official note, what has changed in the new security framework as to old versions of itself:
Security changes in Spring Boot 2.0 M4
I hope this helps someone.
Thank you!
Have a nice day!

Here is the ultimate solution, after 20+ hours of research.
Step 1. Add 'MvcConfig.java' to your project.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}
Step 2. Add configure(WebSecurity web) override to your SecurityConfig class
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
Step 3. Place all static resources in webapp/resources/..

If you are using webjars. You need to add this in your configure method:
http.authorizeRequests().antMatchers("/webjars/**").permitAll();
Make sure this is the first statement. For example:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/webjars/**").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.usernameParameter("email")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.deleteCookies("remember-me")
.logoutSuccessUrl("/")
.permitAll()
.and()
.rememberMe();
}
You will also need to have this in order to have webjars enabled:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
...
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
...
}

#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
String[] resources = new String[]{
"/", "/home","/pictureCheckCode","/include/**",
"/css/**","/icons/**","/images/**","/js/**","/layer/**"
};
http.authorizeRequests()
.antMatchers(resources).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout().logoutUrl("/404")
.permitAll();
super.configure(http);
}
}

i had the same issue with my spring boot application, so I thought it will be nice if i will share with you guys my solution. I just simply configure the antMatchers to be suited to specific type of filles. In my case that was only js filles and js.map. Here is a code:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/index.html", "/", "/home",
"/login","/favicon.ico","/*.js","/*.js.map").permitAll()
.anyRequest().authenticated().and().csrf().disable();
}
}
What is interesting. I find out that resources path like "resources/myStyle.css" in antMatcher didnt work for me at all. If you will have folder inside your resoruces folder just add it in antMatcher like "/myFolder/myFille.js"* and it should work just fine.

In the latest Spring Security 6, the WebSecurityConfigurerAdapter is deprecated.
Declare a WebSecurityCustomizer bean instead.
#Bean
public WebSecurityCustomizer ignoringCustomizer() {
return (web) -> web.ignoring().requestMatchers("...");
}

It,s work for spring security 6.0.*
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeHttpRequests()
.requestMatchers(
"/home/**",
"/login/**",
"/account/starter/**",
"/register/**",
"/plugins/**",
"/dist/**",
"/js/**",
"/**/favicon.ico").permitAll()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
"/plugins/**",
"/dist/**",
"/js/**",
... they are located in resources/
plugins, dist, js - these are the names of directories with resources

Related

No x-auth-token header is found after logging in using Spring's MockMVC test API

Please consider the following configuration
Spring Boot application:
#SpringBootApplication
#EnableRedissonHttpSession
#ComponentScan(basePackages = { "com.ja.pi" })
public class PiApp {
#Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
Web security configuration:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserHandler userHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//#formatter:off
.anonymous().disable() // Disable anonymous sessions
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(getLoginSuccessHandler())
.failureHandler(getLoginFailureHandler())
.loginPage("/login")
.usernameParameter("email")
.permitAll()
.and()
.logout()
.logoutUrl("/user/logout");
//#formatter:on
}
And the following test code:
MockHttpServletRequestBuilder requestBuilder = post("/login").contentType("application/x-www-form-urlencoded").param("email", user.getEmail()).param("password", user.getPassword());
ResultActions result = mockMvc.perform(requestBuilder).andExpect(status().isOk());
MockHttpServletResponse response = result.andReturn().getResponse();
String token = response.getHeader("x-auth-token");
The problem is that token is always null and I can't perform actions that require an authenticated session!
But when I startup the Spring Boot application and use a REST client to simulate the same action of login, I find the x-auth-token header returned back in the HTTP response headers.
What should I do with the test API to allow receiving the x-auth-token ?
At first, I was creating the web-app context this way
mockMvc = webAppContextSetup(webApplicationContext).apply(springSecurity()).build();
But the solution is to obtain an instance of the SessionRepositoryFilter filter and add it to the web-app context. The filter is responsible for returning the x-auth-token header.
SessionRepositoryFilter<?> filter = webApplicationContext.getBean(SessionRepositoryFilter.class);
mockMvc = webAppContextSetup(webApplicationContext).addFilters(filter).apply(springSecurity()).build();

How to configure thymeleaf-extras-springsecurity4 without xml?

I'm trying to use something like this code snippet in my view, however the content is always shown regardless of the user's role.
<div sec:authorize="hasRole('ROLE_ADMIN')">
<!-- Some admin content -->
</div>
Add to your build.gradle the following dependency:
compile("org.springframework.boot:spring-boot-starter-security")
You must also add Spring Security configuration as in example:
#Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("ADMIN", "USER")
.and().withUser("user").password("user").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/index/**").permitAll()
.and().authorizeRequests().antMatchers("/login", "logout").permitAll()
.and().formLogin().loginPage("/login").defaultSuccessUrl("/").permitAll()
.and().logout()
.deleteCookies("remove")
.invalidateHttpSession(true)
.logoutUrl("/logout")
.logoutSuccessUrl("/logout-success")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
}
Read more at Securing a Web Application.

Spring OAuth2 uses wrong authentication details for a token

I am having following configuration in Spring Security wiht OAuth2:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
//#EnableOAuth2Sso
public class FVSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("secret")
.roles("USER", "ROLE1")
.and()
.withUser("admin")
.password("password")
.roles("ADMIN","USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/**").hasRole("USER")
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler())
.and()
.formLogin();
}
Resource server configuraion:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("sampleClient")
.authorizedGrantTypes("implicit")
.scopes("read")
.autoApprove(true)
.accessTokenValiditySeconds(30)
.and()
.withClient("user")
.secret("secret")
.scopes("read", "write")
.autoApprove(true)
.authorizedGrantTypes(
"password","authorization_code", "refresh_token")
.and()
.withClient("admin")
.secret("password")
.scopes("read", "write", "custom")
.autoApprove(true)
.authorizedGrantTypes(
"password","authorization_code", "refresh_token");
}
And the following Rest Controller
#RestController
public class OAuthTestController {
#PreAuthorize("hasRole('ROLE_ROLE1') and #oauth2.hasScope('read')")
#RequestMapping(value="/api/user/test1", method = RequestMethod.GET)
public String testGETWithRole() {
return "[GET] Needs role ROLE1";
}
#RequestMapping(value="/api/admin/test1", method = RequestMethod.GET)
public String testWithAdminRole() {
return "[GET] Needs Admin role";
}
And the resource server:
#Configuration
#EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers("/api/**")
.and().authorizeRequests()
.antMatchers("/api/**").access("hasRole('USER')")
.antMatchers("/api/admin/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
Basically I am expecting Spring to use OAuth2 for authentication and use spring security roles to allow resource access.
I generated and admin token and afterwards an user token. I am expecting when using admin token to be able to access /api/admin resource and when using user token to not be able.
I just noticed that the form login is not prompted always, so I suppose Spring remembers the authenticated user.
When I used the admin token I got access denied. When debugging in Spring sources I noticed that the authentication was an instance of UserNamePasswordAuthenticationPassword with the user details, not admin as expected. I suppose is because last time I used those credentials in login form.
Is it a way to force spring to use credentials for corresponding token, or to force Spring show login form each time?
P.S. I use Postman to test the API.
Thanks.

Adding a custom filter to be invoked after spring-security filter in a Servlet 3+ environment

I'm using Spring-Security 3.2.4 and Spring Boot 1.1.0 (and it's related dependencies versions 4.X).
I'm writing a web application that will be run in an embedded tomcat.
I'm trying to add two additional filters(not related to Spring security) that one of them will be invoked before the Spring-Security-FilterChainProxy and the other one will be invoked after the Spring-Security-FilterChainProxy.
My Spring-Security configuration files:
#Configuration
#EnableWebMvcSecurity
public class SecurityCtxConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("pass").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.usernameParameter("user").passwordParameter("password");
}
}
And the Main class (Application.class):
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
#Bean
RequestFilter beforeSpringSecurityFilter(){
return new RequestFilter();
}
#Bean
RequestFilter afterSpringSecurityFilter(){
return new RequestFilter();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And the Filter implementation:
public class RequestFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
}
}
Is there a way to controll the invocation order when taking in account the FilterChainProxy (that is beeing created by the WebSecurityConfigurerAdapter ?
To be percise, the required order is:
request-filter-1
Spring-Security FilterChain
request-filter-2
Thanks
Agree with everything stated by Dave Syer ;) but wished to add a Java Config example of using the FilterRegistrationBean.
In my situation, I was finding that my custom security filter (using Spring Security) was being fired twice for every request. Adding the FilterRegistrationBean config fixed this.
#Bean(name = "myFilter")
public MyAuthenticationFilter myAuthenticationFilter(final MyAuthenticationEntryPoint entryPoint) {
final MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setEntryPoint(entryPoint);
return filter;
}
/**
* We do this to ensure our Filter is only loaded once into Application Context
*
*/
#Bean(name = "authenticationFilterRegistration")
public FilterRegistrationBean myAuthenticationFilterRegistration(final MyAuthenticationFilter filter) {
final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
(Regarding my specific issue of filter being registered twice in Application Context - Rather than using a FilterRegistrationBean, I also found re-implementing the MyAuthenticationFilter to inherit from OncePerRequestFilter instead of GenericFilterBean also worked. However, OncePerRequestFilter support is from Servlet 3.x upwards and since I was writing a public library, support from Servlet 2.x may be needed)
The FilterChainProxy use by Spring Security is not Ordered (if it was you could order all your filters). But you should be able to register it in a FilterRegistrationBean which is Ordered and register your other filters the same way. In the case of the security filter you can inject it by name into the registration bean. The others you can probably inject by calling a #Bean method.
At some point spring boot exposed the security filter as a property. This is now pretty easy to do.
In you application.yml:
spring:
security:
filter:
order: 20
And some filter you want to invoke after Spring Security does it's thing:
#Bean
public FilterRegistrationBean<Filter> afterAuthFilterRegistrationBean() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
//a filter that extends OncePerRequestFilter
AfterAuthFilter afterAuthFilter = new AfterAuthFilter();
registrationBean.setFilter(afterAuthFilter);
//this needs to be a number greater than than spring.security.filter.order
registrationBean.setOrder(30);
return registrationBean;
}
For a filter that is executed before Spring security, set the order to a number less than 20.
If you are using web.xml approaches, you can follow this:
https://stackoverflow.com/a/11929129/1542363
If you using Java config approaches, you can do this in WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(your-request-filter-1, ChannelProcessingFilter.class);
http.addFilterAfter(your-request-filter-2, SwitchUserFilter.class);
}
Always check the library version you are using, and refer to the specific document for the correct order of the filter chains:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#ns-custom-filters
Or, if you using AbstractSecurityWebApplicationInitializer, you can use the insertFilters or appendFilters.
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
More info You can refer this:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-multipart

Spring Boot Testing: Cannot Autowire springSecurityFilterChain on Test Class

I am still wresting with various annotations in setting up a test context under spring boot.
I have been referring to this article, which is refreshingly clear on how to deal with various contexts under Spring Boot. The problem remaining is that I cannot seem to find an annotation combination that will make the springSecurityFilterChain visible in both the main application context (driven from here):
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
and from the test application context begun here:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestPersistenceConfig.class,MvcConfig.class,SecurityConfig.class},loader=AnnotationConfigContextLoader.class)
//#SpringApplicationConfiguration(classes = {TestPersistenceConfig.class,MvcConfig.class,SecurityConfig.class})
#WebAppConfiguration
public class ApplicationIntegrationTest {
MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
//#Resource(name="springSecurityFilterChain")
#Autowired
private FilterChainProxy springSecurityFilterChain;
#Autowired
private UserDao userDao;
#Autowired
private ClientDao clientDao;
#Autowired
private RoleDao roleDao;
UUID key = UUID.fromString("f3512d26-72f6-4290-9265-63ad69eccc13");
#Before
public void setup() {
// using the web application to initate the mock
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilter(springSecurityFilterChain).build();
// our other choice is using another controller config
//mockMvc = MockMvcBuilders.annotationConfigSetup(ExampleApplicationContext.class).build();
// here we should build up the data structure using hibernate
List<Client> clients = new ArrayList<>();
Client clientEN = new Client();
clientEN.setDeviceId("444444444");
clientEN.setLanguage("en-EN");
clientEN.setAgentId("444444444|68:5b:35:8a:7c:d0");
Client clientENDomain = clientDao.save(clientEN);
clients.add(clientENDomain);
List<Role> roles = new ArrayList<>();
Role roleUser = new Role();
roleUser.setRole("user");
Role roleUserDomain = roleDao.save(roleUser);
roles.add(roleUserDomain);
Role roleAdmin = new Role();
roleAdmin.setRole("admin");
Role roleAdminDomain = roleDao.save(roleAdmin);
roles.add(roleAdminDomain);
User user = new User();
user.setLogin("user");
user.setPassword("password");
user.setClients(clients);
user.setRoles(roles);
userDao.save(user);
}
#Test
public void thatViewBootstrapUsesHttpNotFound() throws Exception {
// testing that a correct login into the form will result in a cookie being set
MvcResult result = mockMvc.perform(post("/login")
.param("username", "user").param("password", "password")).andReturn();
Cookie c = result.getResponse().getCookie("my-cookie");
Cookie[] cookies = result.getResponse().getCookies();
for (int i = 0; i <= cookies.length; i++) {
System.out.println("cookie " + i + " name: " + cookies[i].getName());
System.out.println("cookie " + i + " value: " + cookies[i].getValue());
}
//assertThat(c.getValue().length(), greaterThan(10));
// No cookie; 401 Unauthorized
mockMvc.perform(get("/")).andExpect(status().isUnauthorized());
// With cookie; 200 OK
mockMvc.perform(get("/").cookie(c)).andExpect(status().isOk());
// Logout, and ensure we're told to wipe the cookie
result = mockMvc.perform(delete("/session")).andReturn();
c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), is(0));
}
}
By the way #SpringApplicationConfiguration doesn't seem to work in any circumstance, contrary to the doco. The security config is as follows:
#Configuration
#EnableWebMvcSecurity
#ComponentScan({
"com.touchcorp.touchpoint.security",
"com.touchcorp.touchpoint.service",
"com.touchcorp.touchpoint.model.dao"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DeviceUsernamePasswordAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Order(2)
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=1")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
}
Can anyone see why the springSecurityFilterChain is invisible ("No beans of FileterChainProxy type found"). Thanks, I'm pulling my hair out here.
I think I am just a bit unclear as to the purpose of all the annotations. The Spring Boot reference is good, but it doesn't really extend beyond an established baseline. It seems that as soon as you have to combine spring security, hibernate and mvc together, it starts to get complicated and its not clear as to what one is to do.
I would be worried about why #SpringApplicationConfiguration is not working because it is in extensive use elsewhere (e.g. in Spring Boot samples) and it works fine there. Maybe a classpath issue? How about linking to a complete project that others can try to reproduce your problem?
You have 2 different application contexts (one for the test and one in your Application) so it wouldn't be surprising if they behaved differently. In particular the Application has #EnableAutoConfiguration and you test (as far as we can see) does not, so there's one difference that's worth looking into. But nothing is obviously wrong with the test.
Here's an example of a test that autowires the Security filter: https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/sparklr/src/test/java/org/springframework/security/samples/config/ApplicationConfigurationTests.java. It works. Here's another: https://github.com/cloudfoundry/uaa/blob/master/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/audit/AuditCheckMvcMockTests.java.
Thanks to you Dave Syer,
I have made a couple of changes that seem to resolve the various missing pieces:
a test class that begins with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestApplicationConfig.class,TestPersistenceConfig.class,MvcConfig.class,SecurityConfig.class},loader=AnnotationConfigWebContextLoader.class)
#WebAppConfiguration
public class ApplicationIntegrationTest {
and a "marker" Config class, which acts as a component scanner:
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.touchcorp.touchpoint"})
public class TestApplicationConfig {
}
All pars seem to work aside from the data layer, which cannot find any of my domain objects, but this appears to be restricted to the JPA/Hibernate config, not so much an application problem.
Thanks again.

Resources