spring: add session attribute in every controller model - java

I want to add common used attribute in model for every controller.

HandlerInterceptorAdapter can be used to intercept the request. For example, you can override preHandle to validate the session, and add the user to model in postHandle.
public class SessionValidator extends HandlerInterceptorAdapter{
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if (session == null || session.getAttribute("user") == null) {
return false;
}
return super.preHandle(request, response, handler);
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HttpSession session = request.getSession();
if (modelAndView != null) {
modelAndView.getModelMap().addAttribute("user", session.getAttribute("user"));
}
}
}

Related

How to get response body and request header at the same time in SpringBoot?

I want to use HandlerInterceptor to achive idempotent check, and if the request pass the intercepter, cache the response body which retrun from controller, and the key for cache is a token which from request header. but now, i can get request header(it contains the key for cache) in 'postHandle', but can't get response body(the value for cache) in this method. is there any way to achieve it?
here is my code:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Idempotent {
}
public class IdempotentInterceptor implements HandlerInterceptor {
#Autowired
private TokenService tokenService;
#Autowired
private RedisCache redisCache;
#Value("${idempotent.tokenName}")
private String TOKEN_NAME;
#Value("${idempotent.respCachePrefix}")
private String prefix;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
Idempotent methodAnnotation = method.getAnnotation(Idempotent.class);
if (methodAnnotation != null) {
if (!tokenService.checkIdempotent(request)) {
String token = request.getHeader(TOKEN_NAME);
log.info("duplicate request, IdempotentToken: {}", token);
Object result = redisCache.get(prefix + token);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
if (result != null) {
log.info("{} exist result, return the cached result: {}", token, result);
response.getWriter().print(JSON.toJSONString(result));
} else {
response.getWriter().print(JSON.toJSONString(new ResultDTO<>(ResultDTO.FAIL_CODE, "invalid request, please check idempotent token.")));
}
response.getWriter().flush();
return false;
}
return true;
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
Idempotent methodAnnotation = method.getAnnotation(Idempotent.class);
if (methodAnnotation != null) {
// I want to cache the response body here, but can't get infos...
redisCache.set(prefix + request.getHeader(TOKEN_NAME), "", 600L);
}
}
}
#GetMapping("/test/{data}")
#Idempotent
public ResultDTO test(#PathVariable("data") String data) {
return new ResultDTO(data);
}
in a word, i want to get both request headers and response body in one method

Log Request and response in Spring API

I want to implement Rest logging for API using Spring. I tried this:
public static String readPayload(final HttpServletRequest request) throws IOException {
String payloadData = null;
ContentCachingRequestWrapper contentCachingRequestWrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (null != contentCachingRequestWrapper) {
byte[] buf = contentCachingRequestWrapper.getContentAsByteArray();
if (buf.length > 0) {
payloadData = new String(buf, 0, buf.length, contentCachingRequestWrapper.getCharacterEncoding());
}
}
return payloadData;
}
public static String getResponseData(final HttpServletResponse response) throws IOException {
String payload = null;
ContentCachingResponseWrapper wrapper =
WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
wrapper.copyBodyToResponse();
}
}
return payload;
}
#PostMapping(value = "/v1", consumes = { MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE })
public PaymentResponse handleMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest requestCacheWrapperObject = new ContentCachingRequestWrapper(request);
requestCacheWrapperObject.getParameterMap();
.raw_request(readPayload(requestCacheWrapperObject))
.raw_response(getResponseData(response))
}
But I get NULL for request and response.
Do you know what is the proper way to get the payload from the request and the response?
So you just need to have your own interceptor.
#Component
public class HttpRequestResponseLoggingInterceptorAdapter extends HandlerInterceptorAdapter {
#Autowired
private LoggingUtils loggingutils;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
loggingutils.preHandle(request, response);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
#Nullable ModelAndView modelAndView) {
try {
loggingutils.postHandle(request, response);
} catch(Exception e) {
System.out.println("Exception while logging outgoing response");
}
}
}
Once that is done, you need to bind your new interceptor to existing interceptors.
#Configuration
public class InterceptorConfig implements WebMvcConfigurer {
#Autowired
private HttpRequestResponseLoggingInterceptorAdapter httpRequestResponseLoggingInterceptorAdapter;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(httpRequestResponseLoggingInterceptorAdapter);
}
}
Once that is done, your incoming requests for handlemessage method will be intercepted, and can do whatever pre/post processing you want to have.
Logging in this case.
Let me know if this helps.
Sounds like your usecase would be best suited with a class extending spring's org.springframework.web.servlet.handler.HandlerInterceptorAdapter.
Custom interceptors can override preHandle and postHandle - both of which it sounds like you are inclined to use.
EDIT:
// add to wherevere your source code is
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO: use 'request' from param above and log whatever details you want
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO: use 'response' from param above and log whatever details you want
}
}
// add to your context
<mvc:interceptors>
<bean id="customInterceptor" class="your.package.CustomInterceptor"/>
</mvc:interceptors>

Spring interceptor not working for partial REST endpoints

I have a Spring Boot application with REST endpoints defined like this
/usermanagement/v1/access/ldap
/usermanagement/v1/access/db
I have created a Spring Interceptor to intercept all incoming request with following pattern
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/usermanagement/v1/**");
}
RequestInterceptor
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
System.out.println("This is Post Handling the request");
}
}
This interceptor works only if client accesses the complete endpoint i.e
/usermanagement/v1/access/ldap
In case a partial endpoint is accessed,
/usermanagement/v1/access
interceptor is not called and a 404 is returned to the client.
Is there a way to change this behavior? The reason I am doing this is because I don't want to expose specific endpoints but common endpoints and make internal calls to services and return result through common endpoints.
You are using the wrong method. Try using afterCompletion instead of postHandle
new HandlerInterceptor() {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//This is called before handeling any request
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//This is called after successfully handeling a request. It will not be called in case of an exception
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//This will always be called after a request, even in case of an exception
}
}
With a request to a undefined endpoint /usermanagement/v1/access Spring will throw an exception. Therefor it never will enter postHandle.

Forcing Spring to return status 200 on HEAD requests

I need to make a Filter that will catch all HEAD requests and will return status 200 on them.
As I undertand, I need to create a Filter that will catch every HEAD request, which is done, and do something to return 200 on every requests, which is a question.
I mean filter catches request and able to do something with it, but I need not a request, but a response that will be 200. So what else can I do?
public class HttpHeadFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (isHttpHead(httpServletRequest)) {
chain.doFilter(new ForceHeadToOkStatusWrapper (httpServletRequest), response);
} else {
chain.doFilter(request, response);
}
}
public void destroy() {
}
private boolean isHttpHead(HttpServletRequest request) {
return "HEAD".equals(request.getMethod());
}
private class ForceHeadToOkStatusWrapper extends HttpServletRequestWrapper {
public ForceGetRequestWrapper(HttpServletRequest request) {
super(request);
}
//somethig here
}
}
Finally I've created an interceptor:
public class HttpHeadInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (isHttpHead(request)) {
response.setStatus(HttpStatus.SC_OK);
return false;
}
return true;
}
private boolean isHttpHead(HttpServletRequest request) {
return HttpMethod.HEAD.equals(request.getMethod());
}
}
And added it to WebMvcConfigurerAdapter:
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HttpHeadInterceptor());
}
And it works smooth)

Spring session and websockets: session not stored in session repository

I have an application with Spring Session and Spring Websockets. I use websocket handshake interceptor to set properties to websocket session
public class WebsocketHandshakeInterceptor 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(true);
if (session != null) {
attributes.put(ApplicationConstants.HTTP_SESSION_ID_KEY, session.getId());
attributes.put(ApplicationConstants.HTTP_SESSION_KEY, session);
}
}
return true;
}
#Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception ex) {
}
}
Then I catch SessionConnectEvent. Event contais needed information (http session and session id). But the session is null in sessionRepository
#Component
public class WebSocketListener implements ApplicationListener<SessionConnectEvent> {
#Autowired
private MapSessionRepository sessionManager;
#Override
public void onApplicationEvent(SessionConnectEvent event) {
String httpSessionId
= event.getWebSocketSession().getAttributes().get(ApplicationConstants.HTTP_SESSION_ID_KEY).toString();
sessionManager.getSession(httpSessionId); //returns null
}
}
It returns null with the first call, if I try to reconnect, it returns valid session. Can you show me my error? Thanks.

Categories

Resources