Please tell me how can I get IP address when using AuthenticationFailureBadCredentialsEvent ?
#Autowired
private LoginAttemptService loginAttemptService;
#EventListener
public void onAuthenticationFailure(AuthenticationFailureBadCredentialsEvent event) {
// How can I get the ip address here?
}
The option shown below does not work.
WebAuthenticationDetails auth = (WebAuthenticationDetails) event.getAuthentication();
auth.getRemoteAddress();
I would give a try two possible solutions (I have not tested them) based on the fact that javax.servlet.ServletRequest interface has information about the remote host.
1. Using AuthenticationFailureHandler:
The interface AuthenticationFailureHandler has a method onAuthenticationFailure​(HttpServletRequest, HttpServletResponse, AuthenticationException) that has the request javax.servlet.http.HttpServletRequest available.
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res, AuthenticationException e) throws IOException, ServletException {
// implementation
}
}
2. Invoking RequestContextHolder:
This context holder is able to obtain request attributes containing the servlet request currently bound to the thread through the method currentRequestAttributes.
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
ServletRequest servletRequest = attributes.getRequest();
Related
I'm looking for a way to log all my requests and responses in the database (1 record = 1 request + 1 response).
My use case in details:
Log record in database with request URL, params, IP, start date etc.
Update database record (when request finish) and save response,
exceptions, end date etc.
I'm trying to do with custom OncePerRequestFilter and it work's almost OK. But I have problem with handling exceptions annotated with annotation #ResponseStatus. This kind of exceptions (thrown in controllers) I can't catch in my custom doFilter method. Do you know any way to capture these exceptions in filter? Unless I should do this in some other way?
AuditFilter:
#Component
public class AuditFilter extends OncePerRequestFilter {
private Logger logger = Logger.getLogger(AuditFilter.class.getName());
private RequestAuditRepository repository;
AuditFilter(RequestAuditRepository repository) {
this.repository = repository;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
doFilterWrapped(wrapRequest(request), wrapResponse(response), filterChain);
}
private void doFilterWrapped(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response, FilterChain filterChain)
throws ServletException, IOException {
RequestAuditLog requestAuditLog = new RequestAuditLog();
String catchedExceptionMsg = null;
try {
beforeRequest(requestAuditLog, request);
filterChain.doFilter(request, response);
}
catch (Exception e) {
// Not called when exception with #ResponStatus annotation throwed
catchedExceptionMsg = e.getMessage();
throw e;
}
finally {
afterRequest(requestAuditLog, catchedExceptionMsg, request, response);
response.copyBodyToResponse();
}
}
...
}
BadRequestException:
#ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
public BadRequestException(String message) {
super(message);
}
}
I think the BadRequestException is handled even before your custom filter gets triggered and therefore you can't catch this exception in your filter.
What you could do is that you write your own ExceptionHandler additionally to your filter and log your stuff there.
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(BadRequestException.class)
public void handleError(BadRequestException ex) {
// do your stuff here
}
}
I have a real nice application listener registered as seen below.
#Component
public class MyLogger implements ApplicationListener {
#Override
public void onApplicationEvent(ApplicationEvent event) {
// not doing anything here
// Check out the 'RequestHandledEvent' below though.
}
#EventListener(RequestHandledEvent.class)
public void onApplicationEvent(RequestHandledEvent event) {
// I'd like to get the HttpServletRequest that was just handled.
// Furthermore, I'd really like to get the userPrincipal that was
// on the request, so that I can know made the request
// it seems I can do this
// but the principal seems to already have been cleared.
// is there a more straightforward way to get the request?
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
I feel like I should be able to do something like
event.getRequest()
But I haven't found a successful way to do so.
Long story short, I'd like like ONE PLACE in my application that I can get at the request that came in, and the principal that was on that request, so that I can do some logging, etc.
Should I be looking at a different application event maybe?
You can get request with proper security principal by registering servlet filter like:
#Component
public static class RequestLoggingFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final Principal userPrincipal = request.getUserPrincipal();
// ...do what you want with principal and request info here
filterChain.doFilter(request, response);
}
}
To control order of fiters in filter chain you can either annotate your filter class with #Order annotation or register filter like this:
#Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
RequestLoggingFilter filter = new RequestLoggingFilter();
registrationBean.setFilter(flter);
registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
return registrationBean;
}
Is there any way to obtain STOMP client IP address? I am intercepting inbound channel but I cannot see any way to check the ip address.
Any help appreciated.
You could set the client IP as a WebSocket session attribute during the handshake with a HandshakeInterceptor:
public class IpHandshakeInterceptor implements HandshakeInterceptor {
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// Set ip attribute to WebSocket session
attributes.put("ip", request.getRemoteAddress());
return true;
}
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
}
Configure your endpoint with the handshake interceptor:
#Override
protected void configureStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").addInterceptors(new IpHandshakeInterceptor()).withSockJS();
}
And get the attribute in your handler method with a header accessor:
#MessageMapping("/destination")
public void handlerMethod(SimpMessageHeaderAccessor ha) {
String ip = (String) ha.getSessionAttributes().get("ip");
...
}
Below example updated to get the exact remote client ip:
#Component
public class IpHandshakeInterceptor implements HandshakeInterceptor {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// Set ip attribute to WebSocket session
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
String ipAddress = servletRequest.getServletRequest().getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = servletRequest.getServletRequest().getRemoteAddr();
}
attributes.put("ip", ipAddress);
}
return true;
}
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
}
Tried to add this information as a comment, but stackoverflow complains that it is too long for the comment, so I post it as answer to
It is possible to get this attribute in service without passing
SimpMessageHeaderAccessor ? I mean something similiar to injecting
HttpServletRequest – shark Nov 23 '15 at 15:02
question.
I was able to achieve "close" results with such syntax:
#MessageMapping("/destination")
#SendTo("/topic/someTopic")
public String send(#Header("simpSessionAttributes") Map<String, Object> sessionAttributes) {
String clientsAddress = sessionAttributes.get("ip"));
return "The sender's address is: " + clientsAddress ;
}
I am not familiar to the origin of the "simpSessionAttributes" name of the header of the message, but I noticed that if I put the information in a way it is described in this thread by the #Sergi Almar - I get such result. But perhaps this name "simpSessionAttributes" may depend on some environment configuration or particular implementation of the message framework idk...
Also I do not mind to include this detalization as a part of the answer of the #Sergi Almar.
I'm running a Spring Boot 1.2.3 application with embedded Tomcat.
I'd like to inject a custom contextPath on every request, based on the first part of the URL.
Examples:
http://localhost:8080/foo has by default contextPath="" and should get contextPath="foo"
http://localhost:8080/foo/bar has by default contextPath="" and should get contextPath="foo"
(URLs without path should stay as is)
I tried to write a custom javax.servlet.Filter with #Order(Ordered.HIGHEST_PRECEDENCE), but it seems like I'm missing something. Here's the code:
#Component #Order(Ordered.HIGHEST_PRECEDENCE)
public class MultiTenancyFilter implements Filter {
private final static Pattern pattern = Pattern.compile("^/(?<contextpath>[^/]+).*$");
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
final String requestURI = req.getRequestURI();
Matcher matcher = pattern.matcher(requestURI);
if(matcher.matches()) {
chain.doFilter(new HttpServletRequestWrapper(req) {
#Override
public String getContextPath() {
return "/"+matcher.group("contextpath");
}
}, response);
}
}
#Override public void init(FilterConfig filterConfig) throws ServletException {}
#Override public void destroy() {}
}
This should simply take the String after the first / and before (if any) the second / and then use it as return value for getContextPath().
But Spring #Controller #RequestMapping and Spring Security's antMatchers("/") does not seem to respect it. Both still work as if contextPath="".
How can I dynamically override the context path for each request?
Got it working!
Spring Security docs ( http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html ) say: "Spring Security is only interested in securing paths within the application, so the contextPath is ignored. Unfortunately, the servlet spec does not define exactly what the values of servletPath and pathInfo will contain for a particular request URI. [...] The strategy is implemented in the class AntPathRequestMatcher which uses Spring's AntPathMatcher to perform a case-insensitive match of the pattern against the concatenated servletPath and pathInfo, ignoring the queryString."
So I just did override servletPath and contextPath (even if it's not used by Spring Security). Additionally I added some small redirect, because normally when hitting http://localhost:8080/myContext you get redirected to http://localhost:8080/myContext/ and Spring Securities Ant Matcher did not like the missing trailing slash.
So here's my MultiTenancyFilter code:
#Component #Order(Ordered.HIGHEST_PRECEDENCE)
public class MultiTenancyFilter extends OncePerRequestFilter {
private final static Pattern pattern = Pattern.compile("^(?<contextPath>/[^/]+)(?<servletPath>.*)$");
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Matcher matcher = pattern.matcher(request.getServletPath());
if(matcher.matches()) {
final String contextPath = matcher.group("contextPath");
final String servletPath = matcher.group("servletPath");
if(servletPath.trim().isEmpty()) {
response.sendRedirect(contextPath+"/");
return;
}
filterChain.doFilter(new HttpServletRequestWrapper(request) {
#Override
public String getContextPath() {
return contextPath;
}
#Override
public String getServletPath() {
return servletPath;
}
}, response);
} else {
filterChain.doFilter(request, response);
}
}
#Override
protected String getAlreadyFilteredAttributeName() {
return "multiTenancyFilter" + OncePerRequestFilter.ALREADY_FILTERED_SUFFIX;
}
}
It simply extracts the contextPath and servletPath using the URL schema mentioned here: https://theholyjava.wordpress.com/2014/03/24/httpservletrequest-requesturirequesturlcontextpathservletpathpathinfoquerystring/
Additionally I had to provide a custom getAlreadyFilteredAttributeName method, because else the filter got called twice. (This resulted in stripping the contextPath twice)
I'm creating a log manager for my controllers that logs every action in it and returned values
My controllers are defined in this way:
#Controller
#RequestMapping(value="/ajax/user")
public class UserController extends AbstractController{
#RequestMapping(value="/signup")
public #ResponseBody ActionResponse signup(#Valid SignupModel sign) {
ActionResponse response=new ActionRespone();
response.setMessage("This is a test message");
return response;
}
}
and I defined a HandlerInterceptor to log output of each handler:
#Component
public class ControllerInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
LogManager log=new LogManager();
log.setMessage();//I need returned ActionResponse here
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
where I use log.setMessage(); I need my ActionResponse's message (This is a test message) which is returned from signup method
How can I do this?
An interceptor is not the right place to do what you want since it's not capable of getting the return value of the handler.
You can achieve what you wan't without changing any existing code using aspect oriented programming (AOP). For this to work in spring you'll need to include the jars for spring-aop and AspectJ.
Creating the aspect and advice
#Aspect
#Component
public class ActionResponseLoggerAspect {
private static final Logger logger = LoggerFactory.getLogger(ActionResponseLoggerAspect.class);
#AfterReturning(pointcut="execution(* your.package.UserController.*(..)))", returning="result")
public void afterReturning(JoinPoint joinPoint , Object result) {
if (result instanceof ActionResponse) {
ActionResponse m = (ActionResponse) result;
logger.info("ActionResponse returned with message [{}]", m.getMessage());
}
}
}
The afterReturning method will be executed every time a controller method returns.
Enabling #AspectJ Support
Enable AspectJ support by adding this to your XML configuration.
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
For more info see the spring docs.