Recently I started using Spark Java Framework (2.7.2) to create a lightweight web application. One of its requirements is that the application must be deployed to an Apache Tomcat Server 8.5.
I've managed to set things going, but I have not been able to set any custom cookie.
I have used the following method but none worked.
response.cookie("my_cookie", "value");
response.cookie("/path", "my_cookie", "value", -1, false, true);
It seems like tomcat is setting correctly the JSESSIONID cookie but I have no control over this cookie generation and I would like to generate a random and unique cookie, in order to be used for user authorization.
EDIT:
The control flow for setting the cookie is this
// In the main application
before("/*", AccessController.setSession);
// Method for setting an existing session
public static Filter setSession = (Request request, Response response) -> {
// If the user is not set in the session
if (!SessionUtil.hasSession(request)) {
// Obtain the cookie session ID
String sessionId = SessionUtil.getCookie(request);
System.out.println(sessionId);
// Obtain the user according to the session ID
User user = app.getUserFromSession(sessionId);
System.out.println(user != null);
// if does exists we set the session
if (user != null)
SessionUtil.setSession(request, user);
}
};
// Methods for the session
public static boolean hasSession(Request request) {
if (request.session().attribute("user") == null)
return false;
return true;
}
public static String getCookie(Request request) {
return request.cookie(COOKIE_NAME);
}
public static void setSession(Request request, User user) {
request.session().attribute("user", user);
}
This is called when a login is succesfull. Cookie is stored in the user database persisting sessions
public static void setSession(Response response, String cookie) {
response.cookie(COOKIE_NAME, cookie);
}
Related
We have the following:
Java 8 web app developed with Spring Boot and embedded Jetty.
The UI for the app is built using react.
The backend exposes multiple REST APIs via Jersey.
The authentication is done using SAML with Okta as the IDP. We use spring-security-saml-core for SAML authentication.
The Java app is fronted by Nginx for SSL termination but this issue is reproducible without Nginx too.
The issue we have been noticing is that the user session times-out after the session timeout time despite user activity. Following is an excerpt of the application.properties related to session and JSESSIONID cookie:
# Session #
server.session.cookie.domain=domain.example.com
server.session.cookie.http-only=true
server.session.cookie.max-age=-1
server.session.cookie.name=JSESSIONID
server.session.cookie.path=/
server.session.cookie.secure=true
server.session.persistent=false
server.session.timeout=900
server.session.tracking-modes=cookie
# Custom #
auth.cookie-max-age=900
The cookie-mag-age above dictates the lifespan of other cookies we create to store other user details, e.g user id. Following code gets called when Okta sends back the assertion on valid auth. This is a custom class extended from SavedRequestAwareAuthenticationSuccessHandler
#Override
public void onAuthenticationSuccess(
final HttpServletRequest request,
final HttpServletResponse response,
final Authentication authentication)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
Object credentials = authentication.getCredentials();
if (credentials instanceof SAMLCredential) {
SAMLCredential samlCredential = (SAMLCredential) credentials;
NameID nameId = samlCredential.getNameID();
if (nameId != null) {
String nameIdValue = nameId.getValue();
UserDetail userDetail = userManager.getUserByName(nameIdValue, false);
if(userDetail != null) {
session.setAttribute(Constants.SESSION_USERID_FIELD, userDetail.getId());
String token = csrfTokenManager.getTokenFromSession(request);
if(token == null) {
token = csrfTokenManager.generateAndSaveToken(request, response);
response.addHeader(CsrfTokenManager.TOKEN_HEADER_NAME, token);
}
Cookie idCookie = AuthUtil.createCookie(
Constants.SESSION_USERID_FIELD,
userDetail.getId(),
appConfig.getCookieMaxAge(), true);
response.addCookie(idCookie);
}
}
}
getRedirectStrategy().sendRedirect(request, response,
appConfig.getAuthSuccessRedirectUrl());
}
We call the REST APIs from Javascript using isomorphic fetch as follows:
export const fetchAllProjects = () => {
return dispatch => {
dispatch(requestAllProjects())
return fetch(`/rest/private/v1/projects`, {
credentials: 'same-origin'
})
.then(errorMessageUtil.handleErrors)
.then(response => response.json())
.then(json => dispatch(receiveAllProjects(json)))
.catch(error => errorMessageUtil.dispatchError(error, dispatch, appActions.displayBadRequestMessage))
}
}
The credentials: 'same-origin', sends all the cookies to the backend including the JESESSIONID and the other cookies set above. We have an authentication filter that checks for valid session and not sends 401
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
HttpSession session = httpServletRequest.getSession(false);
if(session == null) {
this.sendUnauthorizedResponse(httpServletResponse);
} else {
String userIdFromCookie = null;
Cookie[] cookies = httpServletRequest.getCookies();
for(Cookie cookie : cookies) {
if(Constants.SESSION_USERID_FIELD.equals(cookie.getName())) {
userIdFromCookie = cookie.getValue();
break;
}
}
String userId = (String)session.getAttribute(Constants.SESSION_USERID_FIELD);
if(userIdFromCookie != null && userId != null
&& userId.equals(userIdFromCookie)) {
HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(httpServletRequest);
requestWrapper.addHeader(Constants.USERID_HEADER_NAME, userId);
chain.doFilter(requestWrapper, httpServletResponse);
} else {
this.sendUnauthorizedResponse(httpServletResponse);
}
}
}
I am sure, we are not doing something right because the timeout time for the session is for the inactivity.
One other question is, do calls to the REST APIs qualify as valid activity for session time-out to extend?
Found the problem with my logic. The code above tries to get the userId from the cookie and the userId from the session and then compare and if not matched, redirect the user to login.
The issue was the timeout time for the session and the cookie expiry time, auth.cookie-max-age=900, were same so even though the session was active because of activity, the cookie was getting expired and userId values didn't match.
My approach is to increase the max age of the cookie to a bigger value, so UI can keep sending the cookies with valid values and if the session really expires due to inactivity, its the userId value from the session that will be null and the user will need to re-login.
I am very new to java servlet programming. I have been writing a simple program for practicing java session. There are two .jsp file. first one called index.jsp, and another one is selection.jsp. And there is a servlet called controller. At first the index.jsp will be called, and user will be submit a input. That will be redirect in servlet controller. In that servlet will check whether it is new request or not. If new then it redirect to other page, else will do some other work.
I am checking whether it is new request or not by session.isNew() method. But it always says it is not new session. But, if I disable the browser cookies option then it is working fine. Now what is my observation is that when in the first I request the index.jsp to the container it assign a session along with that request. So when it comes to servlet it treat as a old session. I got this idea from Head first book Servlet and JSP.
Here is my servlet code -
public class Controller extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String user;
HttpSession session = request.getSession(false);
if (session == null) {
user = request.getParameter("user");
if (user == null) {
response.sendRedirect("index.jsp");
}
session.setAttribute("username", user);
SelectItem selectItem = new SelectItem();
selectItem.setUser(user);
response.sendRedirect("selection.jsp");
session.setAttribute("selectItem", selectItem);
} else {
String selectionItem = request.getParameter("selection");
SelectItem selectItem = (SelectItem) session.getAttribute("selectItem");
if (selectItem != null) {
selectItem.add(selectionItem);
session.setAttribute("selectItem", selectItem);
}
response.sendRedirect("selection.jsp");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
So, how to determine whether it is a new session or old one? Thank you.
HttpSession.isNew API:
Returns true if the client does not yet know about the session or if the client chooses not to join the session. For example, if the server used only cookie-based sessions, and the client had disabled the use of cookies, then a session would be new on each request.
So, you're getting true because the client has cookies disabled. The "new session" check in done in the else block of this check:
HttpSession session = request.getSession(false);
if (session == null) {
// create new session
session = request.getSession();
} else {
// existing session so don't create
}
In your code, you don't appear to be creating a new session when a new session is detected. Perhaps that's where you're stumbling.
Note: learning the basic Servlet API is a good thing. However, for my professional work I use frameworks which simplify my programming, like Spring Boot and Spring Security.
I have a small spring MVC-app with session, with also some small amount of REST methods.
If I copy the JSESSIONID and use it with a 'curl'-command I'm able to access the rest methods from a different computer with a different IP, and thus 'faking' a session.
Is there a way to "bind" a session to one IP-address?
You can bind a session to IP address using a custom filter:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
boolean chainCompleted = implementEnforcement(request, response);
if (!chainCompleted) {
filterChain.doFilter(request, response);
}
}
private boolean implementEnforcement(HttpServletRequest request, HttpServletResponse response) throws IOException {
final String key = "enforcement.ip";
HttpSession session = request.getSession(false);
if (session != null) {
// we have a session
String ip = request.getRemoteAddr();
String ipInSession = session.getAttribute(key);
if (ipInSession == null) {
session.setAttribute(key, ip);
} else {
if (!ipInSession.equals(ip)) {
// JSESSIONID is the same, but IP has changed
// invalidate the session because there is a probability that it is
// a session hijack
session.invalidate();
return true;
}
}
}
return false;
}
It remembers user's IP address and then compares current IP with the remembered one: if it differs, the session is destroyed.
you can use Spring security hasIpAddress() check spring security refrence
You should read through Session Fixation Attack Protection in the Spring documentation, where it is written, that you can configure that inside the session-management tag
<session-mangagement session-fixation-protection="migrateSession|none|newSession">
migrateSession - creates a new session and copies the existing session attributes to the new session. This is the default.
none - Don't do anything. The original session will be retained.
newSession - Create a new "clean" session, without copying the existing session data.
a session will be fixed to a set of variables like browser agent, IP. So in your case the browser agent of curl will not match and the provided sessionid will be of no use
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.
I just enabled Session in my Google AppEngine/Java + GWT application. And how do I use it? How do I get session ID and play will all good stuff from it? Are there any real examples of simple login page where I'm just entering LoginName and Password, then it goes to the server over RPC call, authenticates against database and sends Session ID back to the client.
I have following code already but don't know what to do next:
GWT Login Form:
public class LoginForm {
private final LoginServiceAsync loginService = GWT.create(LoginService.class);
VerticalPanel loginVp = new VerticalPanel();
TextBox loginTxt = new TextBox();
TextBox passTxt = new TextBox();
Button loginBtn = new Button("Login");
public Widget getLoginWidget(){
loginBtn.addClickHandler(new ClickHandler(){
public void onClick(ClickEvent arg0) {
loginService.authenticateUser(loginTxt.getText(), passTxt.getText(),
new AsyncCallback<String>(){
public void onFailure(Throwable caught) {
InfoPanel.show(InfoPanelType.HUMANIZED_MESSAGE, "No Connetion", "Problem conneting to the server.");
}
public void onSuccess(String result) {
InfoPanel.show(InfoPanelType.HUMANIZED_MESSAGE, "Session ID", "Your session id is: " + result);
GWT.log("Setting up session", null);
String sessionID = result;
final long DURATION = 1000 * 60 * 60 * 24 * 14; //duration remembering login. 2 weeks
Date expires = new Date(System.currentTimeMillis() + DURATION);
Cookies.setCookie("sid", sessionID, expires, null, "/", false);
}
}
);
}
});
loginVp.add(loginTxt);
loginVp.add(passTxt);
loginVp.add(loginBtn);
return loginVp;
}
}
RPC Servlet:
public class LoginServiceImpl extends RemoteServiceServlet implements LoginService{
//Sends back to the client session id
public String authenticateUser(String login, String password){
String sessionId = new String();
// TODO: figure out how to work with session id in GAE/J
sessionId = "How to get session id?";
return sessionId;
}
public Boolean checkIfSessionIsValid(String sessionId){
//TODO: figure out how to check user's credentials
return true;
}
}
Any hints in the right direction would be helpful.
Thanks.
Enabling session support gives you a standard Servlet HttpSession.
This will be tracked by means of a cookie (called JSESSONID), which is managed by the servlet container under the covers. You do not need to care about the session id.
You can then set attributes (server-side) that will be associated with the session (so that you can retrieve them later).
HttpServletRequest request = this.getThreadLocalRequest();
HttpSession session = request.getSession();
// in your authentication method
if(isCorrectPassword)
session.setAttribute("authenticatedUserName", "name");
// later
if (session.getAttribute("authenticatedUserName") != null)
This should also work with Ajax requests from GWT.
Please refer to any Servlet tutorial for more details.
The drawback of sessions on GAE (compared to other servlet engines) is that they are serialized in and loaded from the database every time, which could be expensive, especially if you put a lot of data in there.
Here is how you can get the session in GAE:
this.getThreadLocalRequest().getSession();