SecurityContextHolder + @Async fixes and improvements (#930)

* @Async and Spring Security

* @Async with SecurityContext propagated

* Spring and @Async

* Simulated Annealing algorithm

* Simulated Annealing algorithm

* Rebase

* Rebase

* SA further fixes

* Slope One plus package refactoring

* SlopeOne refactoring

* Async improvements and fixes

* Remove unnecessary bean

* Final fixes to Spring Security @Async

* Async Spring MVC
This commit is contained in:
maibin 2017-01-02 11:06:44 +01:00 committed by GitHub
parent 7844b1d4e4
commit 4018d45a2c
8 changed files with 46 additions and 57 deletions

View File

@ -1,7 +1,6 @@
package org.baeldung.spring; package org.baeldung.spring;
import org.baeldung.security.MySavedRequestAwareAuthenticationSuccessHandler; import org.baeldung.security.MySavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -25,6 +24,7 @@ public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
public SecurityJavaConfig() { public SecurityJavaConfig() {
super(); super();
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
} }
// //
@ -47,7 +47,7 @@ public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
.antMatchers("/api/csrfAttacker*").permitAll() .antMatchers("/api/csrfAttacker*").permitAll()
.antMatchers("/api/customer/**").permitAll() .antMatchers("/api/customer/**").permitAll()
.antMatchers("/api/foos/**").authenticated() .antMatchers("/api/foos/**").authenticated()
.antMatchers("/api/async/**").authenticated() .antMatchers("/api/async/**").permitAll()
.and() .and()
.httpBasic() .httpBasic()
// .and() // .and()
@ -67,13 +67,4 @@ public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
return new SimpleUrlAuthenticationFailureHandler(); return new SimpleUrlAuthenticationFailureHandler();
} }
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}
} }

View File

@ -3,6 +3,7 @@ package org.baeldung.spring;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
@ -13,6 +14,7 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration @Configuration
@ComponentScan("org.baeldung.web") @ComponentScan("org.baeldung.web")
@EnableWebMvc @EnableWebMvc
@EnableAsync
public class WebConfig extends WebMvcConfigurerAdapter { public class WebConfig extends WebMvcConfigurerAdapter {
public WebConfig() { public WebConfig() {

View File

@ -2,6 +2,7 @@ package org.baeldung.web.controller;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.apache.log4j.Logger;
import org.baeldung.web.service.AsyncService; import org.baeldung.web.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@ -9,29 +10,30 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
@Controller @Controller
public class AsyncController { public class AsyncController {
private static final Logger log = Logger.getLogger(AsyncService.class);
@Autowired @Autowired
private AsyncService asyncService; private AsyncService asyncService;
@RequestMapping(method = RequestMethod.POST, value = "/upload")
public Callable<Boolean> processUpload(final MultipartFile file) {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
// ...
return true;
}
};
}
@RequestMapping(method = RequestMethod.GET, value = "/async") @RequestMapping(method = RequestMethod.GET, value = "/async")
@ResponseBody @ResponseBody
public Boolean checkIfContextPropagated() throws Exception{ public Object standardProcessing() throws Exception {
return asyncService.checkIfPrincipalPropagated().call() && asyncService.checkIfContextPropagated(SecurityContextHolder.getContext()); log.info("Outside the @Async logic - before the async call: "
+ SecurityContextHolder.getContext().getAuthentication().getPrincipal());
asyncService.asyncCall();
log.info("Inside the @Async logic - after the async call: "
+ SecurityContextHolder.getContext().getAuthentication().getPrincipal());
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
@RequestMapping(method = RequestMethod.GET, value = "/async2")
@ResponseBody
public Callable<Boolean> springMVCAsyncTest() {
return asyncService.checkIfPrincipalPropagated();
} }
} }

View File

@ -4,8 +4,8 @@ import java.util.concurrent.Callable;
public interface AsyncService { public interface AsyncService {
void asyncCall();
Callable<Boolean> checkIfPrincipalPropagated(); Callable<Boolean> checkIfPrincipalPropagated();
Boolean checkIfContextPropagated(Object context);
} }

View File

@ -12,25 +12,25 @@ public class AsyncServiceImpl implements AsyncService {
private static final Logger log = Logger.getLogger(AsyncService.class); private static final Logger log = Logger.getLogger(AsyncService.class);
@Override
public Callable<Boolean> checkIfPrincipalPropagated() {
Object before = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("Before new thread: " + before);
return new Callable<Boolean>() {
public Boolean call() throws Exception {
Object after = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("New thread: " + after);
return before == after;
}
};
}
@Async @Async
@Override @Override
public Boolean checkIfContextPropagated(Object context) { public void asyncCall() {
log.info("Before @Async: " + context); log.info("Inside the @Async logic: " + SecurityContextHolder.getContext().getAuthentication().getPrincipal());
log.info("Inside @Async: " + SecurityContextHolder.getContext());
return context == SecurityContextHolder.getContext();
} }
@Override
public Callable<Boolean> checkIfPrincipalPropagated() {
Object before
= SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("Before new thread: " + before);
return new Callable<Boolean>() {
public Boolean call() throws Exception {
Object after
= SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("New thread: " + after);
return before == after;
}
};
}
} }

View File

@ -34,9 +34,4 @@
</authentication-provider> </authentication-provider>
</authentication-manager> </authentication-manager>
<beans:bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
p:targetClass="org.springframework.security.core.context.SecurityContextHolder"
p:targetMethod="setStrategyName" p:arguments="MODE_INHERITABLETHREADLOCAL" />
</beans:beans> </beans:beans>

View File

@ -32,6 +32,7 @@
<param-name>throwExceptionIfNoHandlerFound</param-name> <param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value> <param-value>true</param-value>
</init-param> </init-param>
<async-supported>true</async-supported>
</servlet> </servlet>
<servlet-mapping> <servlet-mapping>
<servlet-name>api</servlet-name> <servlet-name>api</servlet-name>
@ -42,12 +43,13 @@
<filter> <filter>
<filter-name>springSecurityFilterChain</filter-name> <filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter> </filter>
<filter-mapping> <filter-mapping>
<filter-name>springSecurityFilterChain</filter-name> <filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern> <url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher> <dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher> <dispatcher>ASYNC</dispatcher>
</filter-mapping> </filter-mapping>
<!-- <welcome-file-list> --> <!-- <welcome-file-list> -->

View File

@ -12,7 +12,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpSession; import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
@ -42,10 +41,8 @@ public class AsyncControllerTest {
} }
@Test @Test
public void testProcessUpload() throws Exception { public void testAsync() throws Exception {
MockMultipartFile jsonFile = new MockMultipartFile("json", "", "application/json", mockMvc.perform(MockMvcRequestBuilders.get("/async")).andExpect(status().is5xxServerError());
"{\"json\": \"someValue\"}".getBytes());
mockMvc.perform(MockMvcRequestBuilders.fileUpload("/upload").file(jsonFile)).andExpect(status().isOk());
} }
} }