How to enable HTTP response caching in Spring Boot - java

I have implemented a REST server using Spring Boot 1.0.2. I'm having trouble preventing Spring from setting HTTP headers that disable HTTP caching.
My controller is as following:
#Controller
public class MyRestController {
#RequestMapping(value = "/someUrl", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<String> myMethod(
HttpServletResponse httpResponse) throws SQLException {
return new ResponseEntity<String>("{}", HttpStatus.OK);
}
}
All HTTP responses contain the following headers:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: 0
Pragma: no-cache
I've tried the following to remove or change those headers:
Call setCacheSeconds(-1) in the controller.
Call httpResponse.setHeader("Cache-Control", "max-age=123") in the controller.
Define #Bean that returns WebContentInterceptor for which I've called setCacheSeconds(-1).
Set property spring.resources.cache-period to -1 or a positive value in application.properties.
None of the above have had any effect. How do I disable or change these headers for all or individual requests in Spring Boot?

Turns out the no-cache HTTP headers are set by Spring Security. This is discussed in http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#headers.
The following disables the HTTP response header Pragma: no-cache, but doesn't otherwise solve the problem:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Prevent the HTTP response header of "Pragma: no-cache".
http.headers().cacheControl().disable();
}
}
I ended up disabling Spring Security completely for public static resources as following (in the same class as above):
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/public/**");
}
This requires configuring two resource handlers to get cache control headers right:
#Configuration
public class MvcConfigurer extends WebMvcConfigurerAdapter
implements EmbeddedServletContainerCustomizer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Resources without Spring Security. No cache control response headers.
registry.addResourceHandler("/static/public/**")
.addResourceLocations("classpath:/static/public/");
// Resources controlled by Spring Security, which
// adds "Cache-Control: must-revalidate".
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCachePeriod(3600*24);
}
}
See also Serving static web resources in Spring Boot & Spring Security application.

There are a lot of ways in spring boot for http caching. Using spring boot 2.1.1 and additionally spring security 5.1.1.
1. For resources using resourcehandler in code:
You can add customized extensions of resources this way.
registry.addResourceHandler
Is for adding the uri path where to get the resource
.addResourceLocations
Is for setting the location in the filesystem where the resources are located(
given is a relative with classpath but absolute path with file::// is also possible.)
.setCacheControl
Is for setting the cache headers (self explanatory.)
Resourcechain and resolver are optional (in this case exactly as the default values.)
#Configuration
public class CustomWebMVCConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/*.js", "/*.css", "/*.ttf", "/*.woff", "/*.woff2", "/*.eot",
"/*.svg")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)
.cachePrivate()
.mustRevalidate())
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
}
2. For resources using application properties config file
Same as above, minus the specific patterns, but now as config.
This configuration is applied to all resources in the static-locations listed.
spring.resources.cache.cachecontrol.cache-private=true
spring.resources.cache.cachecontrol.must-revalidate=true
spring.resources.cache.cachecontrol.max-age=31536000
spring.resources.static-locations=classpath:/static/
3. At controller level
Response here is the HttpServletResponse injected in the controller method as parameter.
no-cache, must-revalidate, private
getHeaderValue will output the cache options as string. e.g.
response.setHeader(HttpHeaders.CACHE_CONTROL,
CacheControl.noCache()
.cachePrivate()
.mustRevalidate()
.getHeaderValue());

I have found this Spring extension: https://github.com/foo4u/spring-mvc-cache-control.
You just have to do three steps.
Step 1 (pom.xml):
<dependency>
<groupId>net.rossillo.mvc.cache</groupId>
<artifactId>spring-mvc-cache-control</artifactId>
<version>1.1.1-RELEASE</version>
<scope>compile</scope>
</dependency>
Step 2 (WebMvcConfiguration.java):
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CacheControlHandlerInterceptor());
}
}
Step 3 (Controller):
#Controller
public class MyRestController {
#CacheControl(maxAge=31556926)
#RequestMapping(value = "/someUrl", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<String> myMethod(
HttpServletResponse httpResponse) throws SQLException {
return new ResponseEntity<String>("{}", HttpStatus.OK);
}
}

Overriding of default caching behavior for a particular method can be done in the below way:
#Controller
public class MyRestController {
#RequestMapping(value = "/someUrl", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<String> myMethod(
HttpServletResponse httpResponse) throws SQLException {
return new ResponseEntity.ok().cacheControl(CacheControl.maxAge(100, TimeUnit.SECONDS)).body(T)
}
}

The CacheControl class is a fluent builder, which makes it easy for us to create different types of caching:
#GetMapping("/users/{name}")
public ResponseEntity<UserDto> getUser(#PathVariable String name) {
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS))
.body(new UserDto(name));
}
Let's hit this endpoint in our test, and assert that we have changed the headers:
given()
.when()
.get(getBaseUrl() + "/users/Michael")
.then()
.header("Cache-Control", "max-age=60");

I run into similar problem. I wanted to get just some of dynamic resources (images) cached in the browser. If image changes (not very often) I change the part of uri... This is my sollution
http.headers().cacheControl().disable();
http.headers().addHeaderWriter(new HeaderWriter() {
CacheControlHeadersWriter originalWriter = new CacheControlHeadersWriter();
#Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
Collection<String> headerNames = response.getHeaderNames();
String requestUri = request.getRequestURI();
if(!requestUri.startsWith("/web/eventImage")) {
originalWriter.writeHeaders(request, response);
} else {
//write header here or do nothing if it was set in the code
}
}
});

#Configuration
#EnableAutoConfiguration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
}

If you don't care to have your static resources authenticated, you could do this:
import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toStaticResources;
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.requestMatchers(toStaticResources().atCommonLocations());
}
...
}
and in your application.properties:
spring.resources.cache.cachecontrol.max-age=43200
See ResourceProperties.java for more properties that can be set.

I used below lines in my controller.
ResponseEntity.ok().cacheControl(CacheControl.maxAge(secondWeWantTobeCached, TimeUnit.SECONDS)).body(objToReturnInResponse);
Please note that Response will have header Cache-Control with value secondWeWantTobeCached. However if we are typing url in addressbar and pressing enter, Request will always be sent from Chrome to server. However if we hit url from some link, browser will not send a new request and it will be taken from cache.

Related

Spring Security returns 404 instead of 403 when using #PreAuthorize

After struggling with this for a few days (searching SO for similar questions, doing trial & error), I am tempted to give up...
So the problem is I have a REST service based on Spring Boot using Spring Security and JWT for authentication. Now I want to secure some of the methods to be only called by authorized people using the #PreAuthorize-annotation.
This seems to work partly because instead of calling the method Spring returns 404. I would have expected 403.
I have read this SO-question and tried the answers given there, but it did not help. I have moved the #EnableGlobalMethodSecurity(prePostEnabled = true)-Annotation from my SecurityConfiguration to the Application class as suggested elsewhere, still it does not work.
My security configuration looks like this:
#Configuration
#Profile("production")
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Value("${adDomain}")
private String adDomain;
#Value("${adUrl}")
private String adUrl;
#Value("${rootDn}")
private String rootDn;
#Value("${searchFilter}")
private String searchFilter;
private final AuthenticationManagerBuilder auth;
private final SessionRepository sessionRepository;
#Autowired
public SecurityConfiguration(AuthenticationManagerBuilder auth, SessionRepository sessionRepository) {
this.auth = auth;
this.sessionRepository = sessionRepository;
}
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/static/**", "/api/web/logout")
.antMatchers(HttpMethod.POST, "/api/web/login");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // Using JWT there is no need for CSRF-protection!
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
ActiveDirectoryLdapAuthenticationProvider adProvider =
new ActiveDirectoryLdapAuthenticationProvider(adDomain, adUrl, rootDn);
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
adProvider.setSearchFilter(searchFilter);
adProvider.setUserDetailsContextMapper(new InetOrgPersonContextMapper());
auth.authenticationProvider(adProvider);
return super.authenticationManagerBean();
}
}
The controller method looks like this
#RequestMapping(path = "/licenses", method = RequestMethod.GET)
#PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> getAllLicenses(#RequestParam("after") int pagenumber, #RequestParam("size") int pagesize
, #RequestParam("searchText") String searchText) {
List<LicenseDTO> result = ...
return new ResponseEntity<Object>(result, HttpStatus.OK);
}
I am quite sure I am missing something very simple, but I just cannot figure out what.
By the way: if the user requesting the licenses has the ADMIN role everything works as expected, so the problem is not a real 404.
You need to define the exceptionHandling at security configuration as follows,
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // Using JWT there is no need for CSRF-protection!
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new AccessDeniedExceptionHandler())
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}
You can define AccessDeniedExceptionHandler class as follows,
public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException ex) throws IOException, ServletException {
response.setStatus(HttpStatus.FORBIDDEN);
}
}
I finally found a solution fitting my purposes. I do not know if this is the best way to deal with this, but just adding an ExceptionHandler did the trick. Somewhere deep inside the filterchain the 403 mutates to 404 when there is no such handler in place.
Perhaps I am to dump to read and understand the documentation, but I did not find anything that suggest you have to do this. So maybe I am wrong solving the problem like this, but here is the code that did the trick (it is a really basic implementation that should be improved over time):
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(Throwable.class)
#ResponseBody
public ResponseEntity<String> handleControllerException(Throwable ex) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
if(ex instanceof AccessDeniedException) {
status = HttpStatus.FORBIDDEN;
}
return new ResponseEntity<>(ex.getMessage(), status);
}
}
Global method security can be enabled with the help of annotation #EnableGlobalMethodSecurity(prePostEnabled=true) . The combination of this and #Preauthorize will create a new proxy for your controller and it will loose the Request mapping which will result in 404 Exception.
To handle this you can use the annotation #EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) which is there in your SecurityConfiguration class.
Provided the details in another answer as well.

How to log JSON or XML request in a database or log file before processing in Spring boot using #Requestbody annotation

How do I log a JSON or XML request in a database or log file before processing in Spring boot using #RequestBody annotation?
Using which class can I perform this?
Or any link would be helpful.
You can use filter (CommonsRequestLoggingFilter class) approach or you can use below code with custom implementation
#Component
public class AppRequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) {
HttpServletRequest requestCacheWrapperObject = new ContentCachingRequestWrapper(request);
//your implementation
//sample method you can use: requestCacheWrapperObject.getParameterMap(); requestCacheWrapperObject.getContentAsByteArray();
return true;
}
#Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
//your implementation
}
}
#Configuration
public class AppMVCConfig implements WebMvcConfigurer {
#Autowired
private AppRequestInterceptor appRequestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(appRequestInterceptor)
.addPathPatterns("/**");
}
}
In order to log request payloads, you can use Spring provided filter CommonsRequestLoggingFilter.
Add following bean into your Spring-Boot config and change log level of org.springframework.web.filter package to DEBUG.
#Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
CommonsRequestLoggingFilter rlFilter = new CommonsRequestLoggingFilter();
rlFilter.setIncludePayload(true);
return rlFilter;
}
Also, Spring Boot provides Actuator Endpoint (/actuator/httptrace) for HTTP request logging out of the box. Check the below link for more details on this:
Spring Boot Actuator

Spring Boot-Angular - Entering Url in Address Bar results in 404

Need help on the basics -
I have integrated Angular and Spring Boot.
I made production build of the Angular app and copied the 6 files in the Spring boot static resource folder.
By default when I hit localhost:8080 index.html is rendered as Spring boot Automatically registers it as welcome page.
Now when i am inside angular i can navigate to different component via ANGULAR ROUTER and the url is also changing.
But when i copy the same URL for example - localhost:8080/myTask and enter it in url address bar it throws 404 resource not found.
Because it hits the Spring controller first and since there is no mapping for that it fails.
In the class where you have extended WebMvcConfigurerAdapter in Spring Boot, inside addViewControllers method, you should do something like this
#Override
public void addViewControllers(final ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/myTask").setViewName("forward:/");
}
for forwarding, all request, you can do registry.addViewController("/**").setViewName("forward:/");
Update Thanks Jonas for the Suggestion. Since WebMvcConfigurerAdapter is deprecated in Spring 5.0, you can implement the above logic by extending WebMvcConfigurer
// the perfect solution(from jhipster)
#Controller
public class ClientForwardController {
#GetMapping(value = "/**/{path:[^\\.]*}")
public String forward() {
return "forward:/";
}
}
If you don't use Spring MVC (for example, you are using Jersey), you can also solve this by using a javax.servlet.Filter:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class AngularRoutingFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
String requestURI = httpServletRequest.getRequestURI();
if (shouldDispatch(requestURI)) {
request.getRequestDispatcher("/").forward(request, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
private boolean shouldDispatch(String requestURI) {
/* Exclude/Inlclude URLs here */
return !(requestURI.startsWith("/api") || requestURI.equals("/"));
}
}

Custom HTTP Methods in Spring MVC

I am trying to create a custom Spring MVC Controller for a resource that would handle the COPY HTTP method.
#RequestMapping accepts only the following RequestMethod values: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS and TRACE.
Is there any recommended way of handling custom HTTP methods in Spring MVC Controller?
The Servlet specification allows only for GET, HEAD, POST, PUT, DELETE, OPTIONS or TRACE HTTP methods. This can be seen in the Apache Tomcat implementation of the Servlet API.
And this is reflected in the Spring API's RequestMethod enumeration.
You can cheat your way around those by implementing your own DispatcherServlet overriding the service method to allow COPY HTTP method - changing it to POST method, and customize the RequestMappingHandlerAdapter bean to allow it as well.
Something like this, using spring-boot:
#Controller
#EnableAutoConfiguration
#Configuration
public class HttpMethods extends WebMvcConfigurationSupport {
public static class CopyMethodDispatcher extends DispatcherServlet {
private static final long serialVersionUID = 1L;
#Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
if ("COPY".equals(request.getMethod())) {
super.doPost(request, response);
}
else {
super.service(request, response);
}
}
}
public static void main(final String[] args) throws Exception {
SpringApplication.run(HttpMethods.class, args);
}
#RequestMapping("/method")
#ResponseBody
public String customMethod(final HttpServletRequest request) {
return request.getMethod();
}
#Override
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
final RequestMappingHandlerAdapter requestMappingHandlerAdapter = super.requestMappingHandlerAdapter();
requestMappingHandlerAdapter.setSupportedMethods("COPY", "POST", "GET"); // add all methods your controllers need to support
return requestMappingHandlerAdapter;
}
#Bean
DispatcherServlet dispatcherServlet() {
return new CopyMethodDispatcher();
}
}
Now you can invoke the /method endpoint by using COPY HTTP method. Using curl this would be:
curl -v -X COPY http://localhost:8080/method

How to mock a SecurityContext

Endpoints with Jersey.
I want to secure an endpoint with a ContainerRequestFilter
#Provider
#Secured
public class AuthorizationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
final SecurityContext securityContext =
requestContext.getSecurityContext();
//TODO: on logger here...
System.out.printf("Filtering %s request... AuthorizationRequestFilter\n", requestContext.getMethod());
requestContext.getHeaders().add("X-Secured-By", "Jersey >_<");
System.out.printf("SecurityContext: %s (%s).\n", securityContext, securityContext.getAuthenticationScheme());
if (securityContext == null || !securityContext.isUserInRole("privileged")) {
requestContext.abortWith(new UnauthorizedResponse().getResponse());
}
}
}
The annotation #Secured:
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Secured {}
So I can do this:
#Path("foobar")
public class FooResource {
//...
#Context
SecurityContext securityContext;
//...
#GET
#Secured
#Path(value = "foo")
#Produces(MediaType.APPLICATION_JSON)
public Response getFoo(#Context SecurityContext sc, #Context UriInfo ui, #Context HttpHeaders hh) {
// ...
}
//...
And I'm doing it right (I think), because with my test I don't even pass through the getFoo endpoint but is the ContainerRequestFilter that kicks me out. In fact I receive this (the "X-Secured-By" header is hand-made):
Headers: {X-Secured-By=[Jersey >_< kicked you out!], Content-Length=[97], Date=[Wed, 03 Dec 2014 17:46:50 GMT], Content-Type=[application/json], X-Powered-By=[Jersey ^_^]}
Response: InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9998/urler/test, status=401, reason=Unauthorized}}
Now it would be nice to mock the SecurityContext.
This is what I'm doing... and if I'm here, it's obviously silly and/or wrong.
public class UrlerResourceTest extends JerseyTest {
//....
#Override
public TestContainerFactory getTestContainerFactory() {
GrizzlyTestContainerFactory grizzlyTestContainerFactory = new GrizzlyTestContainerFactory();
System.out.printf("The GrizzlyTestContainerFactory: %s ", grizzlyTestContainerFactory);
// just for debugging...
return grizzlyTestContainerFactory;
}
#Test
public void testSecuredEndpoint() throws JSONException {
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
Mockito.when(securityContext.isUserInRole(anyString())).thenReturn(true);
Mockito.when(securityContext.getAuthenticationScheme()).thenReturn("Just Mocking...");
ReflectionTestUtils.setField(resource, "securityContext", securityContext, SecurityContext.class);
final Response response = target("foobar")
.path("foo")
.request(MediaType.APPLICATION_JSON)
.get();
System.out.println(getFormattedStringResponseInfo(response));
JSONObject entity = new JSONObject(response.readEntity(String.class));
assertTrue(entity.get("secured").equals(true));
assertTrue(response.getHeaders().containsKey("X-Secured-By"));
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
How can I mock the SecurityContext in my tests?
Thank you so much in advance.
Disclaimer: I'm not really a Mockito user, but from what I understand, mocking is used for situations where you have injected class dependencies (fields), and you mock those dependencies. In which case you still need to set the field with the mocked object. For example
public class TestClass {
TestService testService;
public void doTest() {
System.out.println(testService.getString());
}
public void setTestService(TestService testService) {
this.testService = testService;
}
}
public class TestService {
public String getString() {
return "Hello world";
}
}
#Test
public void toTest() {
TestService testService = Mockito.mock(TestService.class);
Mockito.when(testService.getString()).thenReturn("Hello Squirrel");
TestClass testClass = new TestClass();
testClass.setTestService(testService);
testClass.doTest();
}
You can see we are setting the the TestService in the TestClass with the mocked object. It's not greatest example, as we could simple instantiate TestService, but it shows, from my understanding, how the mocking should work.
That being said, I don't see how it is possible to do this with the AuthorizationRequestFilter, as it's handled by the test container, and we are not instantiating it for a unit test. Even if we were, it would seem intrusive (and redundant) to add a SecurityContext field.
So without a full integration test, where we are starting the server, and using the server's authentication capabilities, it will be difficult to handle the SecurityContext per this use case, as the SecurityContext is created by the container, taking information from the underlying servlet containers authentication mechanism.
One way you can achieve this though (which IMO doesn't seem very elegant - but works), without a full integration test, is to create a a filter which performs before your AuthorizationRequestFilter, and set the SecurityContext from there. Testing aside, this is actually pretty common in cases where we need to implement outr own custom authentication mechanism.
An example of how you might do this for your unit test, might be something like:
public class UrlerResourceTest extends JerseyTest {
...
#Override
public Application configure() {
return new ResourceConfig(FooResource.class)
.register(AuthorizationRequestFilter.class)
.register(AuthenticationFilter.class);
}
#Provider
#Priority(Priorities.AUTHENTICATION)
public static class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return "Stackoverflow";
}
};
}
#Override
public boolean isUserInRole(String string) {
return "privileged".equals(string);
}
#Override
public boolean isSecure() { return true; }
#Override
public String getAuthenticationScheme() { return "BASIC"; }
});
}
}
...
}
This filter will perform before the AuthorizationRequestFilter because of the #Priority annotation. We've set it to Priorities.AUTHENTICATION which will before before any other filter without such annotation. (See Priorities API and Priorities with Jersey. Also the SecurityContext will be passed along between filters and also be injected into your resource class.
As I said, I don't think this is very elegant to have to create another filter, but it works for this purpose. Also I am not too familiar with the Jersey Test Framework, as I'm still beginning with it, but there are many configuration options for deployment within a servlet context. I don't know if we can configure the needed authentication mechanism for this case, but it might be something worth looking into.
Edit: In the beginning I explained about setting the field for the test object, but we can also pass the mocked object to a method. For example we could mock the ContainterRequestContext in the filter method, and call filter ourselves, passing the mocked ContainerRequestContext. But this is only useful when we are actually unit testing the filter class and instantiating it ourselves, which is not the case here.

Categories