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
Related
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;
}
I'm trying to change the Domain and Path of a Cookie in a Webfilter.
The Cookie is added to the Response by the Soteria RememberMeInterceptor.
This is my Webfilter:
#WebFilter(filterName = "DomainCookieFilter",
urlPatterns = {"/*"},
dispatcherTypes = {DispatcherType.ASYNC, DispatcherType.REQUEST})
public class DomainCookieFilter implements Filter {
private static Logger logger = Logger.getLogger(DomainCookieFilter.class.getSimpleName());
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.info("DomainCookieFilter invoked!!!");
chain.doFilter(request, new DomainCookieResponseWrapper((HttpServletResponse) response));
}
}
And this the ResponseWrapper:
public class DomainCookieResponseWrapper extends HttpServletResponseWrapper {
private static Logger logger = Logger.getLogger(DomainCookieResponseWrapper.class.getSimpleName());
public DomainCookieResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void addCookie(Cookie cookie) {
logger.info("Add Cookie: " + cookie.getName());
if(cookie.getName().equals("JREMEMBERMEID") && cookie.getMaxAge() != 0) {
cookie.setDomain(".domain.local");
cookie.setPath("/");
logger.info("Add Cookie: changed Domain");
}
super.addCookie(cookie);
}
}
The DomainCookieResponseWrapper.addCookie-Method is never called, and I don't understand why.
So what am I missing here?
A few tries later I came to the conclusion that Authenticationrequests are not filtered.
I finally came up with a different aproach.
I wrote another RemembermeInterceptor preceding the default one, in wich the Response is replaced by my Responsewrapper.
#Interceptor
#RememberMe
#Priority(PLATFORM_BEFORE + 209)
public class PreRememberMeInterceptor implements Serializable {
private static final long serialVersionUID = 1L;
#AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Object[] newParams = { ctx.getParameters()[0],
new DomainCookieResponseWrapper((HttpServletResponse) ctx.getParameters()[1]),
ctx.getParameters()[2] };
ctx.setParameters(newParams);
return ctx.proceed();
}
}
This maybe isn't the best Solution, but it works.
If anyone has a better approach, please let me know.
I have a requirement to inject custom headers into every request a spring boot application is getting, for this, I have written some code but it seems it is not doing its work. For a brief, I have implemented the Filter interface and defined the doFilter method, extended the HttpServletRequestWrapper class, and overridden getHeader() and getHeaderNames() method to take into account the custom headers I am reading from the properties file.
But, the moment I get into the controller and check the request I am not getting my custom headers that were set through the MyReqWrapper. Below is the code, I've also tried searching it in Stackoverflow but couldn't find the solution on what is/could be wrong here. Can someone point me in the right direction?
Also, please point me on how to test whether custom headers are actually set or not.
This is Filter implementation
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ReqFilter implements Filter {
private static final String CUSTOMHEADERENABLED = "customheadersenabled";
private static final String CUSTOMHEADERCOUNT = "customheaderscount";
#Autowired
private Environment env;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
boolean customHeadersEnabled = Boolean.parseBoolean(env.getProperty(CUSTOMHEADERENABLED, "false"));
int count = Integer.parseInt(env.getProperty(CUSTOMHEADERCOUNT, "0"));
if (customHeadersEnabled && count > 0) {
MyReqWrapper myReq = new MyReqWrapper((HttpServletRequest) servletRequest);
myReq.processMyHeaders(count, env);
filterChain.doFilter(customRequest, servletResponse);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
catch(ServletException ex){
throw ex;
}
}
#Override
public void destroy() {
//
}
}
This is custom request wrapper extending HttpServletRequestWrapper
final class MyReqWrapper extends HttpServletRequestWrapper {
private static final String CUSTOMHEADERPREFIX = "header1";
private final Map<String, String> myHeaders;
public MyReqWrapper(HttpServletRequest request) {
super(request);
myHeaders = new HashMap<>();
}
#Override
public String getHeader(String name) {
String headerValue = myHeaders.get(name);
if (headerValue != null){
return headerValue;
}
return ((HttpServletRequest) getRequest()).getHeader(name);
}
#Override
public Enumeration<String> getHeaderNames() {
Set<String> set = new HashSet<>(myHeaders.keySet());
Enumeration<String> headerNames = ((HttpServletRequest) getRequest()).getHeaderNames();
while (headerNames.hasMoreElements()) {
String n = headerNames.nextElement();
set.add(n);
}
return Collections.enumeration(set);
}
public void processMyHeaders(int headerCount, Environment env) {
while(headerCount > 0){
String [] headerKeyValue = Objects.requireNonNull(env.getProperty(String.format("%1$s%2$s", CUSTOMHEADERPREFIX, headerCount--)))
.split(":");
this.myHeaders.put(headerKeyValue[0], headerKeyValue[1]);
}
}
}
This was solved for me and I forgot to update this with an answer.
So the problem was I was using HttpServletRequest class from two different namespaces in the ReqFilter and controller classes, namely one from "org.apache.catalina.servlet4preview.http.HttpServletRequest" and another from "javax.servlet.http.HttpServletRequest".
Once I used uniform namespace in both the files I could access the headers from controller classes.
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).
I am trying to write a junit for a spring controller whose signature is something like this
#RequestMapping(value = { "/addPharmcyInLookUpTable.form" }, method = { org.springframework.web.bind.annotation.RequestMethod.POST })
public String processSubmitAddPhl(#ModelAttribute PhrmcyAdmin phrmcyAdmin,
BindingResult result, SessionStatus status,
HttpServletRequest request) throws Exception {
.....
....
}
The junit for this is
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/applicationContext.xml",
"classpath:/puela-app-config.xml" }, inheritLocations = true)
public class AddPharmacyInLookUpTableControllerTest {
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(
AddPharmacyInLookUpTableControllerTest.class);
}
#InjectMocks
private AddPharmacyInLookUpTableController controller;
private static MockHttpServletRequest request;
private static MockHttpServletResponse response;
#Autowired
private HandlerMapping handlerMapping;
#Autowired
private HandlerAdapter handlerAdapter;
#BeforeClass
public static void runBeforeAllTest() throws Exception {
System.out.println("Running one time Setup");
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
private ModelAndView handle(final HttpServletRequest request,
final HttpServletResponse response) throws Exception {
final HandlerExecutionChain handler = handlerMapping
.getHandler(request);
Assert.assertNotNull(
"No handler found for request, check you request mapping",
handler);
final Object controller = handler.getHandler();
for (final HandlerInterceptor interceptor : handlerMapping.getHandler(
request).getInterceptors()) {
if (!interceptor.preHandle(request, response, controller)) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
}
#Test
public void processRequestAddPhl_post() throws Exception
{
PhrmcyAdmin phrmcyAdmin = new PhrmcyAdmin();
phrmcyAdmin.setPhlCalMailbox("Test");
phrmcyAdmin.setPhlMailPharmacy("FootHill");
request.setMethod("POST");
request.setRequestURI("/addPharmcyInLookUpTable.form");
// Code goes here
MockHttpSession session = new MockHttpSession();
ModelAndView mv = handle(request, response);
assertEquals(mv.getViewName(), "addPhrmcyInTable.view");
}
}
I am trying to send this model object phrmcyAdmin along with the request. Any idea how we can deal with the model object??