I'd like create Spring request Interceptor which will be able to get some data from session and change some #Autowired components before request.
I can create Interceptor and register it, but it can't get access to session beans:
#Component
#Scope(value="session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TokenInterceptor extends HandlerInterceptorAdapter {
#Autowired
private MyServicePerSession myServicePerSession;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println(myServicePerSession.getName()); // NullPointerException!!!
return true;
}
}
Above in the method preHandle(...) per each request I get NullPointerException.
Here is my config:
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
//...
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor());
}
}
How I said everything work fine except injecting MyServicePerSession.
I will really appreciate if you can give me advice about it, or some other ways to solve that problem.
You are trying to set a new object but you have to set a spring bean.
new TokenInterceptor() // is not spring bean
#Autowired private TokenInterceptor tokenInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor);
// You have to set bean here
}
If this doesn't work, you can check this http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s02.html
Related
I'm currently using RemoteTokenServices class:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Value("${auth-server.url}")
private String authEndpoint;
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
#Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId(clientId);
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
return tokenServices;
}
}
I want to be able to mock this easily and properly for all my endpoints integration tests, knowing that:
the JWT is decoded in a OncePerRequestFilter to get some crucial info
I'm not interested in testing auth failures (well I am but that's not something that we want to do on each endpoint)
Is there a standard way to:
Produce a JWT token by hand ?
Mock all token service accesses easily ?
The expected result would be that I can write an endpoint test with only a few extra lines to setup the right JWT in the request, and the token service would agree on its validity dumbly.
Given that we don't want to test security at all, the best solution for this kind of case is to:
use standard Spring tests security management #WithMockUser along with MockMvc
adapt the ResourceServerConfigurerAdapter for tests:
create a base class that hosts all the config except for tokens
create an inheriting class for non-tests profiles (#ActiveProfiles("!test")) that hosts the token specific configuration
create an inheriting class for test profile that deactivates the remote token check (security.stateless(false);)
make the test classes use test profile
inject the proper token-extracted infos at the right time in tests
Here is how it was implemented in practice:
Base ResourceServerConfigurerAdapter so that the configuration has a major common part between tests and non-tests contexts:
public class BaseResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll().and().cors().disable().csrf().disable().httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.accessDeniedHandler(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED));
}
}
Its implementation outside for non-test:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Profile("!test")
public class ResourceServerConfiguration extends BaseResourceServerConfiguration {
#Value("${auth-server.url}")
private String authEndpoint;
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
#Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId(clientId);
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
return tokenServices;
}
}
And for tests:
#Configuration
#EnableResourceServer
#ActiveProfiles("test")
public class TestResourceServerConfigurerAdapter extends BaseResourceServerConfiguration {
#Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
super.configure(security);
// Using OAuth with distant authorization service, stateless implies that the request tokens
// are verified each time against this service. In test, we don't want that because we need
// properly isolated tests. Setting this implies that the security is checked only locally
// and allows us to mock it with #WithMockUser, #AutoConfigureMockMvc and autowired MockMVC
security.stateless(false);
}
}
Inject token specific info with a request filter for tests:
#Component
#ActiveProfiles("test")
public class TestRequestFilter extends OncePerRequestFilter {
private Optional<InfoConf> nextInfoConf = Optional.empty();
// Request info is our request-scoped bean that holds JWT info
#Autowired
private RequestInfo info;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
if (nextInfoConf.isPresent()) {
info.setInfoConf(nextInfoConf.get());
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
public void setNextInfoConf(InfoConf nextInfoConf) {
this.nextInfoConf = Optional.of(nextInfoConf);
}
public void clearNextInfoConf() {
nextInfoConf = Optional.empty();
}
}
And of course make the JWT parsing do nothing when there's no JWT.
We also wrote a small utility component to create the relevant info to inject.
A typical integration test will be like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#ActiveProfiles("test")
public class TestClass {
#Autowired
protected MockMvc mockMvc;
#Before
public void before() {
// Create an user in DB
// Inject the related information in our filter
}
#After
public void after() {
// Cleanup both in DB and filter
}
#Test
#WithMockUser
public void testThing() throws Exception {
// Use MockMVC
}
}
Another solution is to indeed mock the ResourceServerTokenServices but in fact it's much more a pain to build proper tokens, and using Spring's standard security mock seems much more appropriate.
Help me please, or show other ways to resolve this problem.
#RestController
#RequestMapping("/users")
public class UserController {
#RequestMapping("/login")
public String logIn() {
return "";
}
#RequestMapping("/getUserData")
#FilterThisRequest
public String getUserData(#PathVariable Long userId) {
return user;
}
}
And I have AuthFilter extends GenericFilterBean which makes a certain logic. How can I make that the filter execute only before methods which have #FilterThisRequest? Or there are better practices to resolve this problem?
Check FilterRegistrationBean reference guide at https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners-beans.
Make FilterRegistrationBean available to Spring via a #Configuration class, the below example will ensure that authFilter runs only for /getUserData. Note that it is URL (and not method) based filtering.
#Autowired AuthFilter authfilter;
....
....
#Bean
public FilterRegistrationBean authFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(authfilter);
registration.addUrlPatterns("/web-app-name/getUserData/");
return registration;
}
I would suggest you for the Interceptor.
#Configuration
public class Config extends WebMvcConfigurerAdapter {
#Autowired
RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor).addPathPatterns("/getUserData","/user");
}
}
Interceptor -
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
}
You can override Interceptor's prehandle and postHandle according to your need.
I have a HandlerInterceptorAdapter like
#Component
public class TestInterceptor extends HandlerInterceptorAdapter{
#Value("${thing:defaultValue}")
private String thing;
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// Do something with thing, but thing is null.
}
}
Is it not possible to get config values injected into this class? What's going on here? I would have expected it to at least have the default value but it has nothing.
You need to make sure that Spring is actually instantiating the Component :)
So, like
#Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
#Autowired
TestInterceptor test;
#Override
public void addInterceptors(InterceptorRegistry registry){
InterceptorRegistration testreg = registry.addInterceptor(test);
// ...
}
}
By Autowiring it in to the Configurer, it makes Spring aware of it.
I am trying to do an image upload API. I have a ImageUpload task as follows,
#Component
#Configurable(preConstruction = true)
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ImageUploadTask implements Callable<JSONObject> {
#Autowired
private ImageUploadService imageUploadService;
#Override
public JSONObject call() throws Exception {
....
//Upload image via `imageUploadService`
imageUploadService.getService().path('...').post('...'); // Getting null pointer here for imageUploadService which is a WebTarget
}
}
The ImageUploadService looks like the below,
#Component
public class ImageUploadService {
#Inject
#EndPoint(name="imageservice") //Custom annotation, battle tested and works well for all other services
private WebTarget imageservice;
public WebTarget getService() {
return imageservice;
}
}
Here is the spring boot application class,
#ComponentScan
#EnableSpringConfigured
#EnableLoadTimeWeaving(aspectjWeaving=EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableAutoConfiguration
public class ImageApplication extends SpringBootServletInitializer {
#Bean
public InstrumentationLoadTimeWeaver loadTimeWeaver() throws Throwable {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new RequestContextListener());
}
public static void main(String[] args) throws IOException {
SpringApplication.run(ImageApplication.class);
}
}
Additional information :
Spring version of dependencies are at 4.2.5.RELEASE
pom.xml has dependencies added for spring-aspects and
spring-instrument
I am getting a NullPointerException in ImageUploadTask. My suspicion is that #Autowired doesn't work as expected.
Why wouldn't work and how do I fix this?
Is it mandatory to use #Autowired only when I use #Conigurable, why not use #Inject? (though I tried it and getting same NPE)
By default the autowiring for the #Configurable is off i.e. Autowire.NO beacuse of which the imageUploadService is null
Thus update the code to explicity enable it either as BY_NAME or BY_TYPE as below.
#Component
#Configurable(preConstruction = true, autowire = Autowire.BY_NAME)
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ImageUploadTask implements Callable<JSONObject> { .... }
Rest of the configuration viz. enabling load time weaving seems fine.
Also regarding #Inject annotation have a look here which pretty much explains the difference (or similarity perhaps)
Is it possible to wire a Spring MVC Interceptor using annotations and if so could someone provide me with an example of how to do so?
By wire via annotation I am referring to doing as little in the XML configuration as possible. For example in this configuration file I found at http://www.vaannila.com/spring/spring-interceptors.html;
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" p:interceptors-ref="loggerInterceptor" />
<bean id="loggerInterceptor" class="com.vaannila.interceptor.LoggerInterceptor" />
How little configuration could you get away with there? I imagine an #Autowired would remove the need to explicitly declare the bean in line 2, but would it be possible to get rid of line 1 with an annotation as well?
Stumbled upon this question while searching exactly this. Finally I found out that it works in Spring 3.1 using #EnableWebMVC in conjunction with WebMvcConfigurerAdapter.
Simple Example:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="webapp.base.package")
public class WebApplicationConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor());
}
}
As far as I know, there are no ways to configure Spring MVC interceptors without XML at all.
However, there are some simplifications with mvc namespace in the latest versions of Spring 3.0.x (not Spring 3.0.0!):
<mvc:interceptors>
<bean class="com.vaannila.interceptor.LoggerInterceptor" />
</mvc:interceptors>
See also:
MVC Simplifications in Spring 3.0
I implemented a working solution using a custom #Interceptor annotation in the spirit of Spring's #Controller annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
#Documented
#Component
public #interface Interceptor {
String[] pathPatterns() default {};
String[] excludePathPatterns() default {};
}
This annotation should be applied to HandlerInterceptor types like so:
#Interceptor
public class BuildTimestampInterceptor extends HandlerInterceptorAdapter {
private final String buildTimestamp;
public BuildTimestampInterceptor(#Value("${build.timestamp}") String buildTimestamp) {
this.buildTimestamp = buildTimestamp;
}
#Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
req.setAttribute("buildTimestamp", buildTimestamp);
return true;
}
}
Finally, the processor class, InterceptorProcessor, is a Spring bean that extends WebMvcConfigurerAdapter and implements BeanPostProcessor in order to scan for the custom #Interceptor annotations and register beans having that anntation as HandlerInterceptors inside the overridden addInterceptors method:
#Component
public class InterceptorProcessor extends WebMvcConfigurerAdapter implements BeanPostProcessor {
private final Map<HandlerInterceptor,Interceptor> interceptors = new HashMap<>();
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
scanForInterceptorAnnotation(bean, beanName);
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String string) throws BeansException {
return bean;
}
protected void scanForInterceptorAnnotation(Object bean, String beanName) {
Optional<Interceptor> optionalInterceptor = getInterceptorAnnotation(bean.getClass());
if (optionalInterceptor.isPresent() && bean instanceof HandlerInterceptor) {
interceptors.put((HandlerInterceptor) bean, optionalInterceptor.get());
}
}
private Optional<Interceptor> getInterceptorAnnotation(Class cls) {
Annotation[] annotations = cls.getAnnotationsByType(Interceptor.class);
if (hasValue(annotations)) {
return Optional.of((Interceptor) annotations[0]);
}
return Optional.empty();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
interceptors.forEach((HandlerInterceptor key, Interceptor val) -> {
InterceptorRegistration registration = registry.addInterceptor(key);
if (hasValue(val.pathPatterns())) {
registration.addPathPatterns(val.pathPatterns());
}
if (hasValue(val.excludePathPatterns())) {
registration.excludePathPatterns(val.excludePathPatterns());
}
});
}
private static <T> boolean hasValue(T[] array) {
return array != null && array.length > 0;
}
}
Just remember to have your spring application scan for this processor bean in order to have it actually register your #Interceptors. Something like:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"org.my.controller", "org.my.utils.processor"})
public class WebConfig extends WebMvcConfigurerAdapter {...
I dont know about spring-AOP but if you're using AspectJ via Spring you can use #Aspect, #Pointcut, #Advise and more...
there's also a nice article on howto use these annotation with Spring AOP here:
http://java-x.blogspot.com/2009/07/spring-aop-with-aspecj-annotations.html
like Markus Kreusch'answers,It also could work like this
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="webapp.base.package")
public class WebApplicationConfig extends WebMvcConfigurerAdapter {
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping RequestMappingHandlerMapping= super.requestMappingHandlerMapping();
Object[] interceptors = new Object[1];
interceptors[0] = new RoleInterceptor();
RequestMappingHandlerMapping.setInterceptors(interceptors);
return RequestMappingHandlerMapping;
}
}