I have the following code which is supposed to get user's IP:
public String getUserIP()
{
Object details = getDetails();
if (details instanceof WebAuthenticationDetails)
{
return ((WebAuthenticationDetails)details).getRemoteAddress();
}
return "";
}
#Nullable
public Object getDetails()
{
Authentication authentication = getCurrentUserAuth();
return authentication != null ? authentication.getDetails() : null;
}
However, under some unknown circumstances it returns 127.0.0.1 instead of real IP.
I decided to re-write like that:
public String getUserIP()
{
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attr.getRequest();
String ip = request.getHeader("X-Forwarded-For").split(',')[0];
return ip;
}
But in some cases the header X-Forwarded-For is null. The exception only occurs where getUserIP() from the first snippet returns valid IP address. What's the problem? The web server is tomcat. Thanks in advance.
You can update like this.
public String getUserIP()
{
ServletRequestAttributes attr = (ServletRequestAttributes)
RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attr.getRequest();
return request.getRemoteAddr();
}
You Can Try this
public static String getUserIP(HttpServletRequest request) {
String xForwardedForHeader = request.getHeader("X-Forwarded-For");
if (xForwardedForHeader == null) {
return request.getRemoteAddr();
} else {
// As of https://en.wikipedia.org/wiki/X-Forwarded-For
// The general format of the field is: X-Forwarded-For: client, proxy1, proxy2 ...
// we only want the client
return new StringTokenizer(xForwardedForHeader, ",").nextToken().trim();
}
}
Related
What is the Java Equivalent for HttpContext in C#? I need to convert the following to Java. This code is basically extracting a cookie.
// This method returns the username from the login cookie, or null if no user is logged in.
public string ExtractUser(HttpContext context)
{
// Get the correct cookie from the request
var Cookie = context.Request.Cookies["dummyUser"];
// Return the cookie's value if it exists
if ((Cookie != null) && (Cookie.Value != null))
return Cookie.Value;
// Return null otherwise
return null;
}
Code base from here: https://sisense.dev/guides/sso/jwt/#actions
Try HttpServletRequest
See Here getCookies() method
public static String getCookie(HttpServletRequest req,String name) {
Cookie[] cookies = req.getCookies();
if(cookies!=null) {
for (Cookie cookie : cookies) {
if(cookie.getName().equals(name)) {
return cookie.getValue();
}
}
}
return null;
}
I have implemented a zuul gateway service for the communication between some micro services that i have wrote. I have a specific scenario like i want to change the service path in one of my custom filter and redirected to some other service. Is this possible with the zuul gateway?. I have tried putting "requestURI" parameter with the updated uri to the request context in my route filter but that didn't worked out well
Please help me out guys
thanks in advance
yes, you can. for that you need to implement ZuulFilter with type PRE_TYPE, and update response with specified Location header and response status either 301 or 302.
#Slf4j
public class CustomRedirectFilter extends ZuulFilter {
#Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
#Override
public int filterOrder() {
return FilterConstants.SEND_FORWARD_FILTER_ORDER;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
String requestUrl = ctx.getRequest().getRequestURL().toString();
if (shouldBeRedirected(requestUrl)) {
String redirectUrl = generateRedirectUrl(ctx.getRequest());
sendRedirect(ctx.getResponse(), redirectUrl);
}
return null;
}
private void sendRedirect(HttpServletResponse response, String redirectUrl){
try {
response.setHeader(HttpHeaders.LOCATION, redirectUrl);
response.setStatus(HttpStatus.MOVED_PERMANENTLY.value());
response.flushBuffer();
} catch (IOException ex) {
log.error("Could not redirect to: " + redirectUrl, ex);
}
}
private boolean shouldBeRedirected(String requestUrl) {
// your logic whether should we redirect request or not
return true;
}
private String generateRedirectUrl(HttpServletRequest request) {
String queryParams = request.getQueryString();
String currentUrl = request.getRequestURL().toString() + (queryParams == null ? "" : ("?" + queryParams));
// update url
return updatedUrl;
}
}
I´m currently messing around with JAX-RS specifically Resteasy, because it "just works" with Wildfly and I don´t have to configure anything. That´s really the only reason I use that.
I did already implement Basic Authentication, looking forward to replacing it with OAuth2 later, just did this now for simplicity reasons.
The ContainerRequestFilter looks like this
#Provider
public class SecurityFilter implements ContainerRequestFilter {
private static final String AUTHORIZATION_HEADER_KEY = "Authorization";
private static final String AUTHORIZATION_HEADER_PREFIX = "Basic ";
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if(isAuthenticated(containerRequestContext) == false)
containerRequestContext.abortWith(createUnauthorizedResponse("Access denied."));
}
private boolean isAuthenticated(ContainerRequestContext containerRequestContext) {
List<String> authHeader = containerRequestContext.getHeaders().get(AUTHORIZATION_HEADER_KEY);
ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) containerRequestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
if (authHeader != null && authHeader.size() > 0) {
String authToken = authHeader.get(0).replaceFirst(AUTHORIZATION_HEADER_PREFIX, "");
byte[] decoded = null;
try {
decoded = Base64.getDecoder().decode(authToken);
} catch (IllegalArgumentException ex) {
return false;
}
String decodedString = new String(decoded);
StringTokenizer tokenizer = new StringTokenizer(decodedString, ":");
String username = null, password = null;
if(tokenizer.countTokens() < 2)
return false;
username = tokenizer.nextToken();
password = tokenizer.nextToken();
if (DbController.isValid(username, password, rolesAnnotation.value()))
return true;
}
return false;
}
private Response createUnauthorizedResponse(String msg) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity("{ \"Unauthorized\" : \"" + msg + "\" }")
.type(MediaType.APPLICATION_JSON)
.build();
}
}
It works fine with postman. And I do realize that the main usage of such apis is in well other programs.
But it would be nice, if opened in a browser it would ask you to enter your credentials, instead of just telling you that you are not authorized, with no way to really enter your credentials. Unless you do some trickery to manually put it in the header, but then you might as well just use postman.
If I put a security constraint with auth-constraint role admin it does give a login dialog, but then the authorization does not work and it just keeps asking for authorization.
Is there anything else that I can do instead of containerRequestContext.abortWith? Or do I need to use a completely different approach and it just won´t work with ContainerRequestFilter?
You need to add the WWW-Authenticate header to the response that you abort with. This header tells the browser that it should present the default browser login form.
private static final String CHALLENGE_FORMAT = "%s realm=\"%s\"";
private Response createUnauthorizedResponse() {
return Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, String.format(CHALLENGE_FORMAT, "Basic", "Access"))
.type(MediaType.TEXT_PLAIN_TYPE)
.entity("Credentials are required to access this resource.")
.build();
And here's what the login should look like on Chrome
I need to capture public ip of the system calling our api, that system is a desktop Application thats calling our rest api and I am using the following code
#RequestMapping(value = "/api", method = RequestMethod.POST)
public #ResponseBody JSON_Response sendMessage(#RequestBody Objectjson objjson,HttpServletRequest
request) {
LOG.debug("sending message via payless server");
inputjson.setSentFrom("Message Sent From Api");
// inputjson.setHostip(""+request.getRemoteHost());
//I am using following to capture it
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getLocalAddr());
LOG.debug("--------------------------"+request.getRemoteHost());
LOG.debug("--------------------------"+request.getPathInfo());
LOG.debug("--------------------------"+request.getPathTranslated());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
JSON_Response response = sendMessageInterface.processInput(inputjson);
LOG.debug("sending response message " + response.getStatusDescription());
return response;
}
I am getting my own server ip in the ip address.If i call the rest api from postman i am getting the correct ip address.
Please let me know if you find any other way to retrieve public ip .
I am using Wildfly Server wildfly-10.1.0.Final
This is the method that i Use to get the remote user IP address. Hope it helps
public HttpServletRequest getRequest() {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)attribs).getRequest();
return request;
}
return null;
}
public String getClientIp() {
String remoteAddr = "";
HttpServletRequest request = getRequest();
if (request != null) {
remoteAddr = request.getHeader("X-FORWARDED-FOR");
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getRemoteAddr();
}
}
return remoteAddr;
}
I need to get current session Id without hitting the session (to give it a chance to expire).
I've used Cookies from Servlet code in order keep the session not-touched and then make the session expires after its timeout time.
I am using the following code:
public static String getSessionId(HttpServletRequest request)
{
String sessionId = "";
String logMsg = "";
if (request != null)
{
String sessionTimeout = PropertiesReader.SESSION_TIMEOUT_SCHEMA;
if (sessionTimeout != null && SessionHelper.SESSION_TIMEOUT_FIXED.equalsIgnoreCase(sessionTimeout))
{
logMsg = "FIXED: Getting SessionId from Cookies with activating the session";
Cookie[] cookies = request.getCookies();
if (cookies != null)
{
for (Cookie cook : cookies)
{
if ("JSESSIONID".equalsIgnoreCase(cook.getName()))
{
sessionId = cook.getValue();
break;
}
}
}
} else
{
logMsg = "PER_USAGE: Getting SessionId from Session";
sessionId = request.getSession(false) != null ? request.getSession(false).getId() : "";
}
}else
{
logMsg = "Request object is null";
}
logger.info(logMsg + ", sessionId=" + sessionId);
return sessionId;
}
One one OC4J app server, it works fine. although on another oc4j server, the code of accessing cookies makes the session keep active and don't timeout!
EDIT:
I really stucked!, I've trying to place afilter to remove the JSESSIONID cookie and remove all cookies from the HttpServletRequest, but when I call getSession(false) on the request passed to the servlet, I got a valid Session!
class CookieRemovalHttpServletRequestWrapper extends HttpServletRequestWrapper
{
public static final String COOKIE_HEADER = "cookie";
public static final String JSESSIONID = "JSESSIONID";
public CookieRemovalHttpServletRequestWrapper(HttpServletRequest request)
{
super(request);
}
#Override
public String getHeader(String name)
{
if (COOKIE_HEADER.equalsIgnoreCase(name))
{
return "";
}
return super.getHeader(name);
}
#Override
public Enumeration getHeaderNames()
{
Enumeration e = super.getHeaderNames();
List l = new ArrayList();
while (e.hasMoreElements())
{
String headerName = (String) e.nextElement();
if (!COOKIE_HEADER.equalsIgnoreCase(headerName))
{
l.add(headerName);
}
}
return Collections.enumeration(l);
}
#Override
public Enumeration getHeaders(String name)
{
if (COOKIE_HEADER.equalsIgnoreCase(name))
{
return new Enumeration()
{
public boolean hasMoreElements()
{
return false;
}
public Object nextElement()
{
return null;
}
};
}
return super.getHeaders(name);
}
#Override
public Cookie[] getCookies()
{
Cookie[] cs = super.getCookies();
List<Cookie> cokRet = new ArrayList<Cookie>(cs.length);
for (Cookie c : cs)
{
if (c.getName().equalsIgnoreCase(JSESSIONID)) continue;
cokRet.add(c);
}
return cokRet.toArray(new Cookie[] {});
}
}
And really think to forget all about Session and just use the session Id as just a unique identifier to the user, and do it myself the hard way.
As to your code, don't do it the hard way, use HttpServletRequest#getRequestedSessionId() and HttpServletRequest#isRequestedSessionIdValid() instead to check the requested session ID and if it is valid.
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
// The session has been expired (or a hacker supplied a fake cookie).
}
As to your concrete problem:
the code of accessing cookies makes the session keep active and don't timeout!
No, the code doesn't do that. It's the HTTP request itself which does that. It is not true that whenever you don't call getSession() or something, the session timeout won't be postponed. It will be postponed on every single HTTP request fired by the client, regardless of whether you need the session in the code.
To learn about how sessions work, you may find this answer helpful: How do servlets work? Instantiation, sessions, shared variables and multithreading
The session expiring isn't dependent on your code accessing the session, it depends on the user making a request with that session. Every time the user makes a request, the session's timeout will reset itself.
If you want to not have the user's request re-set the timeout (ie. have a fixed-length session), then you will need to do additional things for configuring the session, including possibly using a different filter to handle sessions.
The session is not timeout, that is correct behavior, because request was accepted and session expiration is updated in any case.