Provide different ClientInterceptor per request using Spring Web Services - java

I've created a custom web service client by extending WebServiceGatewaySupport and also implement custom ClientInterceptor to log some request/response data.
I have to create new interceptor for every call because it has to store some data about the request.
The problem occurs when I make two or more calls to my client. The first request applies its own interceptor with its clientId. The second should do the same. But since both requests use the same WebServicetemplate in my client, the second request replaces the interceptor with its own, with its clientId there.
As a result, I should get the following output to the console:
Request: clientId-1
Request: clientId-2
Response: clientId-1
Response: clientId-2
But I got this:
Request: clientId-1
Request: clientId-2
Response: clientId-2
Response: clientId-2
Here is come code examples (just for understanding how it should work):
#Data
class Response {
private final String result;
public Response(String result) {
this.result = result;
}
}
#Data
class Request {
private final String firstName;
private final String lastName;
}
#Data
class Context {
private final String clientId;
}
#Data
class Client {
private final String clientId;
private final String firstName;
private final String lastName;
}
class CustomInterceptor extends ClientInterceptorAdapter {
private final String clientId;
public CustomInterceptor(String clientId) {
this.clientId = clientId;
}
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Request: " + clientId);
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Response: " + clientId);
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Error: " + clientId);
return true;
}
}
#Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor(context.getClientId())};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate().marshalSendAndReceive(request);
}
}
#Service
#RequiredArgsConstructor
class CustomService {
private final CustomClient customClient;
public String call(Request request, Context context) {
Response response = customClient.sendRequest(request, context);
return response.getResult();
}
}
#RestController
#RequestMapping("/test")
#RequiredArgsConstructor
class CustomController {
private final CustomService service;
public CustomController(CustomService service) {
this.service = service;
}
#PostMapping
public String test(#RequestBody Client client) {
Request request = new Request(client.getFirstName(), client.getLastName());
Context context = new Context(client.getClientId());
return service.call(request, context);
}
}
Is it possible to implement custom interceptors with some state for each call? Preferably without any locks on WebServicetemplate to avoid performance degradation.

Okay. I've found the solution for my case.
I've created an implementation of WebServiceMessageCallback and using it I'm saving data of each request not in interceptor but in WebServiceMessage's mime header.
#Data
class CustomMessageCallback implements WebServiceMessageCallback {
private final String clientId;
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
MimeHeaders headers = ((SaajSoapMessage) message).getSaajMessage().getMimeHeaders();
headers.addHeader("X-Client-Id", clientId);
}
}
And pass this callback in my client implementation:
#Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor()};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate()
.marshalSendAndReceive(request, new CustomMessageCallback(context.getClientId()));
}
}
So now I can get this data while processing request/response/error via interceptor.
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String clientId = ((SaajSoapMessage) messageContext.getRequest())
.getSaajMessage()
.getMimeHeaders()
.getHeader("X-Client-Id")[0];
System.out.println("Request: " + clientId);
return true;
}

Related

Springboot #DeleteMapping respond 404, but response body is empty

I have problem with #DeleteMapping.
Situation is like below.
If I request to /v1/cache/{cacheEntry} with method DELETE,
It respond with 404, but body was empty. no message, no spring default json 404 response message.
If i request to /v1/cache/{cacheEntry} with method POST,
It respond with 405 and body was below. (This action is correct, not a bug.)
If I change #DeleteMapping to #PostMapping, and request /v1/cache/{cacheEntry} with method POST, It respond success with code 200.
{
"timestamp": 1643348039913,
"status": 405,
"error": "Method Not Allowed",
"message": "",
"path": "/v1/cache/{cacheEntry}"
}
// Controller
#Slf4j
#RestController
#RequestMapping("/v1/cache")
#RequiredArgsConstructor
public class CacheController {
private final CacheService cacheService;
#PostMapping("/{cacheEntry}")
public CacheClearResponse clearCacheEntry(#PathVariable("cacheEntry") CacheChannels cacheEntry) {
try {
log.info("Cache entry :: " + cacheEntry);
cacheService.evictCacheEntry(cacheEntry);
return CacheClearResponse.builder()
.result(
RequestResult.builder()
.code(9200)
.message("SUCCESS")
.build()
)
.common(
Common.builder().build()
)
.date(LocalDateTime.now())
.build();
} catch (Exception e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
return CacheClearResponse.builder()
.result(
RequestResult.builder()
.code(9999)
.message(sw.toString())
.build()
)
.common(
Common.builder().build()
)
.date(LocalDateTime.now())
.build();
}
}
}
}
// CacheService
#Service
#RequiredArgsConstructor
public class CacheService {
private final CacheManager cacheManager;
public void evictCacheEntry(CacheChannels cacheEntry) {
Cache cache = cacheManager.getCache(cacheEntry.getCacheName());
if (cache != null) {
cache.clear();
}
}
public void evictCache(CacheChannels cacheEntry, String cacheKey) {
Cache cache = cacheManager.getCache(cacheEntry.getCacheName());
if (cache != null) {
cache.evict(cacheKey);
}
}
}
// Enum
#Getter
#AllArgsConstructor
public enum CacheChannels {
CACHE_TEN_MIN(Names.CACHE_TEN_MIN, Duration.ofMinutes(10)),
CACHE_HALF_HR(Names.CACHE_HALF_HR, Duration.ofMinutes(30)),
CACHE_ONE_HR(Names.CACHE_ONE_HR, Duration.ofHours(1)),
CACHE_THREE_HR(Names.CACHE_THREE_HR, Duration.ofHours(3)),
CACHE_SIX_HR(Names.CACHE_SIX_HR, Duration.ofHours(6)),
CACHE_ONE_DAY(Names.CACHE_ONE_DAY, Duration.ofDays(1));
private final String cacheName;
private final Duration cacheTTL;
public static CacheChannels from(String value) {
return Arrays.stream(values())
.filter(cacheChannel -> cacheChannel.cacheName.equalsIgnoreCase(value))
.findAny()
.orElse(null);
}
public static class Names {
public static final String CACHE_TEN_MIN = "cache10Minutes";
public static final String CACHE_HALF_HR = "cache30Minutes";
public static final String CACHE_ONE_HR = "cache1Hour";
public static final String CACHE_THREE_HR = "cache3Hours";
public static final String CACHE_SIX_HR = "cache6Hours";
public static final String CACHE_ONE_DAY = "cache1Day";
}
}
// Converter
#Slf4j
public class StringToCacheChannelConverter implements Converter<String, CacheChannels> {
#Override
public CacheChannels convert(String source) {
log.info("Convert Target: " + source);
return CacheChannels.from(source);
}
}
// Security Config
#Configuration
#EnableWebSecurity
#Order(1)
public class APISecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${spring.security.auth-token-header-name:Authorization}")
private String apiKeyHeader;
#Value("${spring.security.secret}")
private String privateApiKey;
#Override
protected void configure(HttpSecurity http) throws Exception {
APIKeyAuthFilter filter = new APIKeyAuthFilter(apiKeyHeader);
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String requestedApiKey = (String) authentication.getPrincipal();
if (!privateApiKey.equals(requestedApiKey)) {
throw new BadCredentialsException("The API Key was not found or not the expected value");
}
authentication.setAuthenticated(true);
return authentication;
}
});
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
.authorizeRequests()
.antMatchers("/v1/cache/**")
.authenticated();
}
}
// Filter
#Slf4j
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String apiKeyHeader;
public APIKeyAuthFilter(String apiKeyHeader) {
this.apiKeyHeader = apiKeyHeader;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpServletRequest) {
log.info("Check authenticated.");
return httpServletRequest.getHeader(apiKeyHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpServletRequest) {
return "N/A";
}
}
// Web Config
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCacheChannelConverter());
}
#Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter();
}
}
This can be expected the controller was loaded, endpoint was mapped.
I tried change #DeleteMapping to #PostMapping and it was successfully respond against to POST request.
What am I missing?
I found reason why received 404 without any messages.
My tomcat is on remote server. It configured with security-constraint and disable DELETE method for all enpoints.
I just comment out it and It work properly with delete method.

Implement Spring Interceptor

I want to implement Spring Interceptor in order to print every received and send API XML request. I tried this test code:
#SpringBootApplication
#EntityScan(".....")
public class Application extends SpringBootServletInitializer implements WebMvcConfigurer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
// check if restTeamplate doesn't already have other interceptors
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RestTemplateHeaderModifierInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
#Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RestTemplateHeaderModifierInterceptor());
}
};
}
}
Component for logging:
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import io.micrometer.core.instrument.util.IOUtils;
#Component
public class RestTemplateHeaderModifierInterceptor implements ClientHttpRequestInterceptor, HandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("[ ");
for (byte b : body) {
sb.append(String.format("0x%02X ", b));
}
sb.append("]");
LOGGER.debug("!!!!!!!!!!!!!!! Input " + sb.toString());
System.out.println("!!!!!!!!!!!!!!!");
ClientHttpResponse response = execution.execute(request, body);
InputStream inputStream = response.getBody();
String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
LOGGER.debug("!!!!!!!!!!!!!!! result " + result);
System.out.println("!!!!!!!!!!!!!!!");
return response;
}
}
But nothing is printed into the console in DEBUG mode. Any idea where I'm wrong? Probably this component is not registered or I'm missing some important configuration?
According to your code, you registered an empty list of interceptors in your RestTemplate. Try to change your code as follows:
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
// check if restTeamplate doesn't already have other interceptors
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RestTemplateHeaderModifierInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
More info is here.
That interceptor will serve outgoing requests.
For income requests, you have to inherit your interceptor from HandlerInterceptorAdapter:
public class MyIncomeRequestInterceptor extends HandlerInterceptorAdapter {
//...
}
and then register it with WebMvcConfigurer in the following way, for example:
#Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyIncomeRequestInterceptor());
}
};
}
More info is here.
In both cases, it's not necessary to make beans from your interceptors (you can remove annotation #Component).
UPDATE
A working example:
#Slf4j
#RestController
#ControllerAdvice
#SpringBootApplication
public class Application implements WebMvcConfigurer, ResponseBodyAdvice<Object> {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#GetMapping("/hello")
public ResponseEntity<?> hello() {
return ResponseEntity.ok(Map.of("message", "hello"));
}
#EventListener
public void onReady(final ApplicationReadyEvent e) {
Map result = restTemplate().getForObject("http://localhost:8080/hello", Map.class);
if (result != null) {
log.info("[i] Request result: '{}'", result.get("message"));
}
}
#Bean
public RestTemplate restTemplate() {
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
RestTemplate restTemplate = new RestTemplate(factory);
var interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) interceptors = new ArrayList<>();
interceptors.add(new OutgoingInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new IncomingInterceptor());
}
#Override
public boolean supports(final MethodParameter returnType, final Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(final Object body, final MethodParameter returnType, final MediaType selectedContentType, final Class<? extends HttpMessageConverter<?>> selectedConverterType, final ServerHttpRequest request, final ServerHttpResponse response) {
log.info("[i] ResponseBodyAdvice: response body {}", body);
return body;
}
class OutgoingInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] bytes, final ClientHttpRequestExecution execution) throws IOException {
log.info("[i] Outgoing interceptor: requested URL is '{}'", request.getURI());
ClientHttpResponse response = execution.execute(request, bytes);
String body = StreamUtils.copyToString(response.getBody(), Charset.defaultCharset());
log.info("[i] Outgoing interceptor: response body is '{}'", body);
return response;
}
}
class IncomingInterceptor implements HandlerInterceptor {
#Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView mw) throws Exception {
log.info("[i] Incoming interceptor: requested URL is '{}'", request.getRequestURL().toString());
}
}
}
To log the response body of every metod of controllers IMO it's better to use ResponseBodyAdvice implementation with #ControllerAdvice annotation (see above in the code).
Result:
2019-01-16 14:05:07.260 : [i] Outgoing interceptor: requested URL is 'http://localhost:8080/hello'
2019-01-16 14:05:07.366 : [i] ResponseBodyAdvice: response body {message=hello}
2019-01-16 14:05:07.383 : [i] Incoming interceptor: requested URL is 'http://localhost:8080/hello'
2019-01-16 14:05:07.387 : [i] Outgoing interceptor: response body is '{"message":"hello"}'
2019-01-16 14:05:07.402 : [i] Request result: 'hello'
Repo: sb-web-interceptors-demo
Applying AOP Aspect Oriented Programming for logging, you can do this:
Create an Aspect to intercept before enter to Controller.
Create point cut expression for before and after
Aspect for logging:
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Enumeration;
#Aspect
#Component
public class LoggingAspect {
private static final String CONTROLLER_EXPRESION = "within(#org.springframework.stereotype.Controller *) && execution(* *.*(..))";
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
/**
* Before -> Any resource annotated with #Controller annotation and all method
* and function taking HttpServletRequest as first parameter.
*
* #param joinPoint
* #param request
*/
#Before(CONTROLLER_EXPRESION)
public void logBefore(JoinPoint joinPoint, HttpServletRequest request) {
log.debug("Entering in Method : {}", joinPoint.getSignature().getName());
log.debug("Class Name : {}", joinPoint.getSignature().getDeclaringTypeName());
log.debug("Arguments : {}", Arrays.toString(joinPoint.getArgs()));
log.debug("Target class : {}", joinPoint.getTarget().getClass().getName());
if (null != request) {
log.debug("Start Header Section of request ");
log.debug("Method Type : {}", request.getMethod());
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement().toString();
String headerValue = request.getHeader(headerName);
log.debug("Header Name: {} Header Value : {}", headerName, headerValue);
}
log.debug("Request Path info : {}", request.getServletPath());
log.debug("End Header Section of request ");
}
}
/**
* After -> All method within resource annotated with #Controller annotation.
*
* #param joinPoint
* #param result
*/
#AfterReturning(pointcut = CONTROLLER_EXPRESION, returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) {
String returnValue = this.getValue(result);
log.debug("Method Return value : {}", returnValue);
}
/**
* After -> Any method within resource annotated with #Controller annotation and throws an exception ...Log it
* #param joinPoint
* #param exception
*/
#AfterThrowing(pointcut = CONTROLLER_EXPRESION, throwing = "exception")
public void logAfterThrowing(JoinPoint joinPoint, Throwable exception) {
log.error("An exception has been thrown in {} {}", joinPoint.getSignature().getName(), " ()");
log.error("Cause : {}", exception.getCause());
}
/**
* Around -> Any method within resource annotated with #Controller annotation.
* #param joinPoint
* #return
* #throws Throwable
*/
#Around(CONTROLLER_EXPRESION)
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - start;
log.debug("Method {}.{} () execution time : {} ms", className, methodName, elapsedTime);
return result;
} catch (IllegalArgumentException e) {
log.error("Illegal argument {} in {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName());
throw e;
}
}
private String getValue(Object result) {
String returnValue = null;
if (null != result) {
if (result.toString().endsWith("#" + Integer.toHexString(result.hashCode()))) {
returnValue = ReflectionToStringBuilder.toString(result);
} else {
returnValue = result.toString();
}
}
return returnValue;
}
}

Spring websocket upgraderequest

I have a spring application.
I need to put a value to the initial handshake.
The url looks like: ws://localhost:8080/chat?key=value
I need this key=value in my Websocket Handler.
How can I access it?
Websocket Configuration:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// alle origins erlauben
registry.addHandler(chatWebSocketController(), "/chat").addInterceptors(new HttpSessionHandshakeInterceptor())
.setAllowedOrigins("*");
}
#Bean
public ChatWebSocketController chatWebSocketController() {
return new ChatWebSocketController();
}
}
Websocket Handler method:
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
if (session.getAttributes().containsKey("key")) {
List<String> userMap = session.getHandshakeHeaders().get("key");
JwtTokenUtil jwtTokenUtil = new JwtTokenUtil();
String token = userMap.get(0);
if (jwtTokenUtil.validateToken(token)) {
User userToStore = new User(jwtTokenUtil.getUsernameFromToken(token));
userUsernameMap.put(session, userToStore);
LOGGER.info("User with name " + jwtTokenUtil.getUsernameFromToken(token) + "and IP "
+ session.getRemoteAddress() + " successfully connected");
sendConnectMessage(session, userToStore);
}
} else {
session.close(CloseStatus.POLICY_VIOLATION);
}
}
Found the solution by myself. You have to write your own HandshakeInterceptor, there you have access to the http parameter. so you can put this to your attribbutes map.
public class HttpHandshakeInterceptor implements HandshakeInterceptor {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
attributes.put("sessionId", session.getId());
attributes.put("key", servletRequest.getServletRequest().getParameterMap().get("key"));
}
return true;
}
#Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
//nothing to do
}
}

Filter/ContainerRequestContext asynchronous handling (Jeresy/JAX-RS)

I'm currently running into an issue where my HTTP Request is being sent into the "processing phase" before the asynchronous request that deems if authentication is valid is completed.
Here is an example of the filter causing the issue:
#Provider
public class AuthenticationFilter implements ContainerRequestFilter {
private static final Response ACCESS_DENIED = Response.status(Response.Status.UNAUTHORIZED).build();
private static final Response INTERNAL_SERVER_ERROR = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(final ContainerRequestContext context) throws IOException {
System.out.println("Filter called");
Method method = resourceInfo.getResourceMethod();
if(method.isAnnotationPresent(NoAuthorizationRequired.class)) {
return;
}
if(method.isAnnotationPresent(AuthorizationRequired.class)) {
AuthorizationRequest request = (new AuthorizationRequest(false) {
#Override
public void onCompleted(ParallelResult superResult) {
AuthorizationResult result = (AuthorizationResult)superResult;
if(result.successful()) {
System.out.println("Authentication completed -- Process resource");
} else {
context.abortWith(ACCESS_DENIED);
}
}
});
request.setTask(new AuthorizationTask(request));
Worker.work(request);
} else {
System.err.println("[SEVERE] IMPLEMENTATION FAULT. Authorization annotation not found for method: " + method.getName());
context.abortWith(INTERNAL_SERVER_ERROR);
}
}
}
How can I make it so the request will not enter "processing phase" until the asynchornous request is completed. (Polls mydatabase).

PostProcessInterceptor: How to access HTTP request?

I have an interceptor that looks like
#Interceptor
#Provider
#ServerInterceptor
#SecurityChecked
public class SecurityCheckInterceptor implements PreProcessInterceptor, AcceptedByMethod, PostProcessInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityCheckInterceptor.class);
#Nullable
#Override
public ServerResponse preProcess(final HttpRequest request, final ResourceMethod method) throws Failure, WebApplicationException {
final List<String> authToken = request.getHttpHeaders().getRequestHeader(AUTH_TOKEN);
if (authToken == null || !isValidToken(authToken.get(0))) {
final ServerResponse serverResponse = new ServerResponse();
serverResponse.setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
return serverResponse;
}
return null;
}
#SuppressWarnings("rawtypes")
#Override
public boolean accept(final Class declaring, final Method method) {
// return declaring.isAnnotationPresent(SecurityChecked.class);
return method.isAnnotationPresent(SecurityChecked.class);
}
#Override
public void postProcess(final ServerResponse response) {
LOGGER.info("post-processing response " + response.getEntity());
}
}
What I want ?
- Every time the response goes back I need to add a new AUTH_TOKEN value
- The original request has access to request headers and one of the header is of the form
signature:user:expires
I need the access to user form this request header to generate a new time-based token
How can I have access to request headers?
I added
#Context HttpServletRequest servletRequest;
and that gave me access to headers.
My modified Interceptor looks like
#Interceptor
#Provider
#ServerInterceptor
#SecurityChecked
public class SecurityCheckInterceptor implements PreProcessInterceptor, AcceptedByMethod, PostProcessInterceptor {
private static final Pattern PATTERN = Pattern.compile(":");
#Context
HttpServletRequest servletRequest;
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityCheckInterceptor.class);
#Nullable
#Override
public ServerResponse preProcess(final HttpRequest request, final ResourceMethod method) throws Failure, WebApplicationException {
final List<String> authToken = request.getHttpHeaders().getRequestHeader(AUTH_TOKEN);
if (authToken == null || !isValidToken(authToken.get(0))) {
final ServerResponse serverResponse = new ServerResponse();
serverResponse.setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
return serverResponse;
}
return null;
}
#SuppressWarnings("rawtypes")
#Override
public boolean accept(final Class declaring, final Method method) {
// return declaring.isAnnotationPresent(SecurityChecked.class);
return method.isAnnotationPresent(SecurityChecked.class);
}
#Override
public void postProcess(final ServerResponse response) {
final String header = servletRequest.getHeader(AUTH_TOKEN);
LOGGER.info("post-processing response " + header);
final String authToken = TokenUtils.createToken(PATTERN.split(header)[1]);
}
}
and in logs I see
(http--0.0.0.0-9090-1) post-processing response InvalidTokenValue:user:1377552546572

Categories