I have an app using Struts2 jsp and java..sessionid is created by container.I want to create my own session id and set to that particular session...just want to overwrite.I have aslo created a filter. session id.any clue
something like
session.setSessionId()
thanks..
You can do this using CookieInterceptor which can implement CookiesAware and then intercept the call to set your own sessionId.
Edit:
Just realized CookieInterceptor doesnt allow you to set a cookie, so I did something like this
In my execute method of my Action I did this:
public String execute() {
String jSessionId = null;
for (Cookie c : httpServletRequest.getCookies()) {
if (c.getName().equals("JSESSIONID"))
jSessionId = c.getValue();
}
System.out.println("Value Found In Request = " + jSessionId);
jSessionId = "TestingOverrideOfJSessionId";
Cookie myCookie = new Cookie("JSESSIONID", jSessionId);
myCookie.setMaxAge(60 * 60 * 24 * 365); // Make the cookie last a year
httpServletResponse.addCookie(myCookie);
return SUCCESS;
}
Result
Related
I am developing a social Twitter app for which I am using Spring Boot 2.2.2. I am following the instructions here https://careydevelopment.us/2017/05/24/implement-twitter-login-solution-spring-boot/.
I have just modified my code to be more REST oriented instead of RedirectView(as done in above link)
#PostMapping("/getToken")
public ResponseEntity<TwitterTokenResponse> getToken(HttpServletRequest request) throws Exception {
Twitter twitter = getTwitter();
String callbackUrl = "http://localhost:3000/settings";
RequestToken requestToken = twitter.getOAuthRequestToken(callbackUrl);
request.getSession().setAttribute("requestToken", requestToken);
*//setting twitter attribute in session*
request.getSession().setAttribute("twitter", twitter);
String twitterUrl = requestToken.getAuthorizationURL();
TwitterTokenResponse twitterTokenResponse = new TwitterTokenResponse(requestToken.getToken(), requestToken.getTokenSecret(), true, 0L, null);
log.info("Authorization url is " + twitterUrl);
log.info("Request token is " + requestToken);
return ResponseEntity.ok(twitterTokenResponse);
}
#PostMapping("/twitterCallback")
public ResponseEntity<TwitterTokenResponse> twitterCallback(#RequestParam(value = "oauth_verifier", required = false) String oauthVerifier, #RequestParam(value = "denied", required = false) String denied, HttpServletRequest request) throws Exception {
if (denied != null) {
log.error("Could not get token from Twitter! Access denied");
return null;
}
*//Getting twitter attribute back from session but it is null*
Twitter twitter = (Twitter) request.getSession().getAttribute("twitter");
RequestToken requestToken = (RequestToken) request.getSession().getAttribute("requestToken");
AccessToken token = twitter.getOAuthAccessToken(requestToken, oauthVerifier);
request.getSession().removeAttribute("requestToken");
TwitterTokenResponse twitterTokenResponse = new TwitterTokenResponse(token.getToken(), token.getTokenSecret(), true, token.getUserId(), token.getScreenName());
log.info("Access token is " + token);
return ResponseEntity.ok(twitterTokenResponse);
}
As can be seen in getToken() method I am setting the twitter variable in session and trying to get the same variable from session in twitterCallback() method. the twitter attribute I am getting from the session is always coming null. Any reasons why it is like that and what can be done to solve this?
Thank you
First - please check your sessionid in both controller methods, they must be the same. If they are not, so obviously you have a new session that means there is no "twitter" attribute on it.
Second - There may be a time period between your requests (between the first request to get token and second to fetch it), that can cause the session to kill and generate a new one that doesn't have "twitter" attribute anymore, So Please check your session timeout also.
What I want to do: I want to test my endpoint using RestAssured. The key is that the endpoint is available only for users who are logged in. For logging in I'm using spring security default endpoint with custom successHandler in which I'm setting some random token, saving it to database and returning in header "User-Token". I'm not creating a session on the back end. When I want to access a secured endpoint, front-end makes a call to it, with "User-Token" header. Then I'm using the token for checking in the database. Each token is different and random. Also I don't use any spring-security things for token. Now I want to test this behavior.
Technologies: React & Redux, Spring Boot, RestAssured, JUnit, Tomcat
What's not working: First of all, I'm not really sure how to obtain the token. I mean I can force it by hand to database to some test user, but AFAIK it's a bad bad practice. I read the documentation and come across part about auth().form. But below it was mentioned that it's not the best approach as have to made to the server in order to retrieve the webpage with the login details and it's not possible - webpage is totally separated from backend. I did try the approach nevertheless but it didn't work.
#Before
public void LogInUser(){
String loginUrl = "http://localhost:8080/login";
userToken =
given().auth().form("username","password").
when().get(loginUrl).getHeader("User-Token");
System.out.println(userToken);
}
So then I thought that maybe I don't need auth() at all - I don't need session, so calling the endpoint itself with data should be enough. I checked how data is passed from front-end to back-end and did this:
Form Data: username=something&password=something
#Before
public void LogInUser(){
String loginUrl = "http://localhost:8080/login";
userToken =
given().parameter("username=oliwka&password=jakies")
.when().get(loginUrl).getHeader("User-Token");
System.out.println(userToken);
}
And while it's passing, userToken is null. It's declared as class variable not method variable and it's String.
How can I obtain token for user and test my endpoint for which I need a token?
You can use below procedure to get the access token.
Step 1 : Create a method that will accept a json string and parse the data and return the access token. below is the method. You can use your preferable json parser library.
public String getAccessToken(String jsonStr) {
JSONParser parser = new JSONParser();
Object obj = null;
try {
obj = parser.parse(jsonStr);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JSONObject jsonObject = (JSONObject) obj;
String accessToken = (String) jsonObject.get("access_token");
System.out.println("access_token : " + accessToken);
return accessToken;
}
Step 2 : Now call your login api with username and password like below
String loginUrl = "http://localhost:8080/login";
Response res = null;
String returnValue = "";
response = given().param("username", "yourUserName")
.param("password", "yourpassword")
.param("client_id", "If Any otherwise skip it")
.param("grant_type", "If Any otherwise skip it")
.param("clear_all", "true")
.post(loginUrl);
returnValue = response.body().asString();
String accessToken = getAccessToken(returnValue);
Please let me know if you can get your desired access token.
I have implemented a custom AuthenticateAttribute, AuthUserSession and CredentialsAuthProvider. In the Execute method of my AuthenticateAttribute I do:
public override void Execute(IRequest request, IResponse response, object requestDto)
{
var session = request.GetSession() as IMyCustomAuthUserSession;
// Copy certain request headers into a dictionary on my session object
}
I need to store certain special headers that are sent to me for later use. This works correctly when authentication is not enabled. When authentication IS enabled and the user has to log in, the TryAuthenticate method of my CredentialsAuthProvider class fires:
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var session = authService.GetSession() as IMyCustomAuthUserSession;
}
The sessions in these to methods are not the same since the session I get in the TryAuthenticate method - which fires after the AuthenticateAttribute.Execute method - does not contain the headers I stored there.
The special headers are only sent in the very first call to the web server so I need to get them into the new session of the TryAuthenticate method.
How can I do this?
Passing Session data between logins is going to be difficult as Sessions are invalidated between Authentication attempts. You can choose to retain the same Session Cookies between logins by configuring the AuthFeature plugin with:
Plugins.Add(new AuthFeature(...) {
GenerateNewSessionCookiesOnAuthentication = false
});
Which will retain the same users ss-id/ss-pid cookies on Login.
Use SessionBag for persisting Data between Auth Sessinos
For persisting data outside of an Authenticated User Session you can use a SessionBag, e.g:
//Save
base.SessionBag["cart"] = new Cart { ... };
//Retrieve
var cart = base.SessionBag.Get<Cart>("cart");
Persisting User Data under a Custom Cookie
An alternative solution is to persist data under a Custom Cookie, that way it wont get invalidated by ServiceStack during Authentication.
Where you can register a Global Request Filter to ensure each client/browser has a custom Cookie Id, e.g:
GlobalRequestFilters.Add((req,res,dto) => {
var uniqueId = SessionExtensions.CreateRandomSessionId();
var httpRes = res as IHttpResponse;
httpRes.Cookies.AddPermanentCookie("my-id", uniqueId);
req.Items["my-id"] = uniqueId; //if also needed for this request
});
Then on subsequent requests you can persist data under your unique Cookie Id, e.g:
var uniqueId = req.GetSessionParam("my-id");
var cacheKey = $"urn:Cart:{uniqueId}";
var cache = req.GetCacheClient();
cache.Set(cacheKey, new Cart { ... });
Then later retrieve it with:
var uniqueId = req.GetSessionParam("my-id");
var cacheKey = $"urn:Cart:{uniqueId}";
var cache = req.GetCacheClient();
var cart cache.Get<Cart>(cacheKey);
I am using ServiceStack v4 with custom Authentication. This is setup and working correctly. I can call the /auth service and get a returned AuthorizationResponse with unique SessionId.
I also have swagger-ui plugin setup. Using it, I can authenticate via /auth and then call one of my other services which require authentication without issue.
Now, from a secondary MVC application using the c# JsonServiceClient I can again successfully make a call to /auth and then secured services using the same client object. However, if I dispose of that client (after saving the unique sessionId to a cookie), then later create a new client, and either add the sessionId as a Cookie or via headers using x-ss-pid as documented, calling a services returns 401. If I call a non-secure service, but then try to access the unique user session, it returns a new session.
If I look at the request headers in that service, the cookie or Header is clearly set with the sessionId. The sessionId also exists in the sessionCache. The problem seems to be that the code which tries to get the session from the request isn't finding it.
To be more specific, it appears that ServiceExtensions.GetSessionId is looking at the HostContext and not the calling Request. I'm not sure why. Perhaps I misunderstand something along the way here.
If I directly try and fetch my expected session with the following code it's found without issue.
var req = base.Request;
var sessionId = req.GetHeader("X-" + SessionFeature.PermanentSessionId);
var sessionKey = SessionFeature.GetSessionKey(sessionId);
var session = (sessionKey != null ? Cache.Get<IAuthSession>(sessionKey) : null)?? SessionFeature.CreateNewSession(req, sessionId);
So, am I missing something obvious here? Or maybe not so obvious in creating my secondary client?
Sample code of client calls
Here is my authorization code. It's contained in a Controller class. This is just the relevant parts.
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
try
{
loginResult = client.Post(new Authenticate()
{
UserName = model.Email,
Password = model.Password,
RememberMe = model.RememberMe
});
Response.SetCookie(new HttpCookie(SessionFeature.PermanentSessionId, loginResult.SessionId));
return true;
}
}
Here is my secondary client setup and service call, contained in it's own controller class in another area of the MVC application
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
var cCookie = HttpContext.Request.Cookies.Get(SessionFeature.PermanentSessionId);
if (cCookie != null)
{
client.Headers.Add("X-" + SessionFeature.PermanentSessionId, cCookie.Value);
client.Headers.Add("X-" + SessionFeature.SessionOptionsKey, "perm");
}
response = client.Get(new SubscriptionStatusRequest());
}
Additional Update
During the Authenticate process the following function is called from HttpRequestExtensions with the name = SessionFeature.PermanentSessionId
public static class HttpRequestExtensions
{
/// <summary>
/// Gets string value from Items[name] then Cookies[name] if exists.
/// Useful when *first* setting the users response cookie in the request filter.
/// To access the value for this initial request you need to set it in Items[].
/// </summary>
/// <returns>string value or null if it doesn't exist</returns>
public static string GetItemOrCookie(this IRequest httpReq, string name)
{
object value;
if (httpReq.Items.TryGetValue(name, out value)) return value.ToString();
Cookie cookie;
if (httpReq.Cookies.TryGetValue(name, out cookie)) return cookie.Value;
return null;
}
Now what occurs is the httpReq.Items contains a SessionFeature.PermanentSessionId value, but I have no clue why and where this gets set. I don't even understand at this point what the Items container is on the IRequest. The code thus never gets to the functionality to check my cookies or headers
The Session wiki describes the different cookies used by ServiceStack Session.
If the client wants to use a Permanent SessionId (i.e. ss-pid), it also needs to send a ss-opt=perm Cookie to indicate it wants to use the permanent Session. This Cookie is automatically set when authenticating with the RememberMe=true option during Authentication.
There was an issue in the Session RequestFilter that was used to ensure Session Id's were attached to the current request weren't using the public IRequest.GetPermanentSessionId() API's which also looks for SessionIds in the HTTP Headers. This has been resolved with this commit which now lets you make Session requests using HTTP Headers, e.g:
//First Authenticate to setup an Authenticated Session with the Server
var client = new JsonServiceClient(BaseUrl);
var authResponse = client.Send(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = "user",
Password = "p#55word",
RememberMe = true,
});
//Use new Client instance without Session Cookies populated
var clientWithHeaders = new JsonServiceClient(BaseUrl);
clientWithHeaders.Headers["X-ss-pid"] = authResponse.SessionId;
clientWithHeaders.Headers["X-ss-opt"] = "perm";
var response = clientWithHeaders.Send(new AuthOnly()); //success
This fix is available from v4.0.37+ that's now available on MyGet.
However, if I dispose of that client (after saving the unique sessionId to a cookie)
If the client is disposed where is the cookie you are saving the sessionId located? This answer might provide some additional information.
then later create a new client, and either add the sessionId as a Cookie or via headers using x-ss-pid as documented, calling a services returns 401
If you store/save a valid sessionId as a string you should be able to supply it within a CookieContainer of a new client (given the sessionId is still authenticated). I know you said you tried adding the sessionId as a Cookie but I don't a see sample within your question using the CookieContainer so it should look something like...
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
var cCookieId = savedCookieId; //a string that I believe you saved from a successfully authenticated client that is now disposed
if (cCookieId != null)
{
var cookie = new Cookie(SessionFeature.PermanentSessionId, cCookieId);
//cookie.Domian = "somedomain.com" //you will probably need to supply this as well
client.CookieContainer.Add(cookie)
}
response = client.Get(new SubscriptionStatusRequest());
}
I've seen in some websites that user signed in into their accounts and then closed the browser.
After closed and re-opened the browser and their accounts are still signed in.
But some websites, cannot do like that.
I'm confused that it's considered session or cookie?
If I want my website to be signed in like that, do I have to set session.setMaxInactiveInterval() or cookie.setMaxAge()?
* This answer has serious flaws, see comments. *
Your question is about session tracking.
[PART 1] : SESSION OBJECT
HTTP-request are processed separately, so in order to keep information between each request (for instance, information about the user), a session object has to be created on server-side.
Some websites doesn't need a session at all. A website where users can't modify any content won't have to manage a session (for instance, an online CV). You won't need any cookie or session on such a website.
Create a session :
In a servlet, use the method request.getSession(true) from the HttpServletRequest object to create a new HttpSession object. Note that if you use request.getSession(false), null will be returned if the session has not already been created. Look at this answer for more details.
Set / Get attributes :
The purpose of a session is to keep information on server-side between each request. For instance, keeping the user's name :
session.setAttribute("name","MAGLEFF");
// Cast
String name = (String) session.getAttribute("name");
Destroy a session :
A session will be automatically destroyed if kept inactive too much time. Look at this answer for more details. But you can manually force the session to be destroyed, in the case of a logout action for example :
HttpSession session = request.getSession(true);
session.invalidate();
[PART 2] : So... join the dark side, we have COOKIES ?
Here comes the cookies.
JSESSIONID :
A JSESSIONID cookie is created on the user's computer each time a session is created with request.getSession(). Why? Because each session created on server side has an ID. You can't access another user's session, unless you don't have the right ID. This ID is kept in JSESSIONID cookie, and allow the user to find his information. Look at this answer for more details !
When does a JSESSIONID get deleted ?
JSESSIONID doesn't have an expiration date : it's a session cookie. As all session cookies, it will be deleted when the browser is closed. If you use the basic JSESSIONID mechanism, then the session will become unreachable after you close and re-open the browser, because the JSESSIONID cookie is deleted.
Note that the session is unreachable by the client, but is still running on server-side. Setting a MaxInactiveInterval allows the server to automatically invalidate the session when it has been inactive for too long.
Evil destruction of JSESSIONID
Just for fun, one day I found this code on a project. It was used to invalidate the session by deleting the JSESSIONID cookie with javascript :
<SCRIPT language="JavaScript" type="text/javascript">
function delete_cookie( check_name ) {
// first we'll split this cookie up into name/value pairs
// note: document.cookie only returns name=value, not the other components
var a_all_cookies = document.cookie.split( ';' );
var a_temp_cookie = '';
var cookie_name = '';
var cookie_value = '';
var b_cookie_found = false; // set boolean t/f default f
// var check_name = 'JSESSIONID';
var path = null;
for ( i = 0; i < a_all_cookies.length; i++ )
{
// now we'll split apart each name=value pair
a_temp_cookie = a_all_cookies[i].split( '=' );
// and trim left/right whitespace while we're at it
cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
// alert (cookie_name);
// if the extracted name matches passed check_name
if ( cookie_name.indexOf(check_name) > -1 )
{
b_cookie_found = true;
// we need to handle case where cookie has no value but exists (no = sign, that is):
if ( a_temp_cookie.length > 1 )
{
cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
document.cookie = cookie_name + "=" + cookie_value +
";path=/" +
";expires=Thu, 01-Jan-1970 00:00:01 GMT";
// alert("cookie deleted " + cookie_name);
}
}
a_temp_cookie = null;
cookie_name = '';
}
return true;
}
// DESTROY
delete_cookie("JSESSIONID");
</SCRIPT>
Give another look to this answer. With JavaScript, JSESSIONID can be read, modified, have it's session lost or hijacked.
[PART 3] : KEEPING A SESSION AFTER CLOSING YOUR BROWSER
After closed and re-opened the browser and their accounts are still
signed in.
But some websites, cannot do like that.
I'm confused that it's considered session or cookie??
It's cookie.
We saw that when the JSESSIONID session cookie has been deleted by the web browser, the session object on server-side is lost. There is no way to access it again without the right ID.
If I want my website to be signed in like that, do I have to set
session.setMaxInactiveInterval() or cookie.setMaxAge()?
We also saw that session.setMaxInactiveInterval() was to prevent from running a lost session indefinitely. JSESSIONID cookie cookie.setMaxAge() won't get us anywhere either.
Use a persistent cookie with the session Id :
I came to this solution after reading the following topics :
How to implement "Stay Logged In" when user login in to the web application by BalusC
http://simple.souther.us/not-so-simple.html by Ben Souther; ben#souther.us
The main idea is to register the user's session in a Map, put into the servlet context. Each time a session is created, it is added to the Map with the JSESSIONID value for key; A persistent cookie is also created to memorize the JSESSIONID value, in order to find the session after the JSESSIONID cookie has been destroyed.
When you close the web browser, JSESSIONID is destroyed. But all the HttpSession objects adress have been kept into a Map on server-side, and you can access the right session with the value saved into the persistent cookie.
First, add two listeners in your web.xml deployment descriptor.
<listener>
<listener-class>
fr.hbonjour.strutsapp.listeners.CustomServletContextListener
</listener-class>
</listener>
<listener>
<listener-class>
fr.hbonjour.strutsapp.listeners.CustomHttpSessionListener
</listener-class>
</listener>
The CustomServletContextListener creates a map at context initialization. This map will register all the sessions created by the user on this application.
/**
* Instanciates a HashMap for holding references to session objects, and
* binds it to context scope.
* Also instanciates the mock database (UserDB) and binds it to
* context scope.
* #author Ben Souther; ben#souther.us
* #since Sun May 8 18:57:10 EDT 2005
*/
public class CustomServletContextListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent event){
ServletContext context = event.getServletContext();
//
// instanciate a map to store references to all the active
// sessions and bind it to context scope.
//
HashMap activeUsers = new HashMap();
context.setAttribute("activeUsers", activeUsers);
}
/**
* Needed for the ServletContextListener interface.
*/
public void contextDestroyed(ServletContextEvent event){
// To overcome the problem with losing the session references
// during server restarts, put code here to serialize the
// activeUsers HashMap. Then put code in the contextInitialized
// method that reads and reloads it if it exists...
}
}
The CustomHttpSessionListener will put the session into the activeUsers map when it is created.
/**
* Listens for session events and adds or removes references to
* to the context scoped HashMap accordingly.
* #author Ben Souther; ben#souther.us
* #since Sun May 8 18:57:10 EDT 2005
*/
public class CustomHttpSessionListener implements HttpSessionListener{
public void init(ServletConfig config){
}
/**
* Adds sessions to the context scoped HashMap when they begin.
*/
public void sessionCreated(HttpSessionEvent event){
HttpSession session = event.getSession();
ServletContext context = session.getServletContext();
HashMap<String, HttpSession> activeUsers = (HashMap<String, HttpSession>) context.getAttribute("activeUsers");
activeUsers.put(session.getId(), session);
context.setAttribute("activeUsers", activeUsers);
}
/**
* Removes sessions from the context scoped HashMap when they expire
* or are invalidated.
*/
public void sessionDestroyed(HttpSessionEvent event){
HttpSession session = event.getSession();
ServletContext context = session.getServletContext();
HashMap<String, HttpSession> activeUsers = (HashMap<String, HttpSession>)context.getAttribute("activeUsers");
activeUsers.remove(session.getId());
}
}
Use a basic form to test a user authentification by name/password. This login.jsp form is meant for test only.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title><bean:message key="formulaire1Title" /></title>
</head>
<body>
<form action="login.go" method="get">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="submit" />
</form>
</body>
</html>
There we go. This java servlet is forwarding to a login page when the user is not in session, and to another page when he is. It is only meant for testing the persistent session!
public class Servlet2 extends AbstractServlet {
#Override
protected void doGet(HttpServletRequest pRequest,
HttpServletResponse pResponse) throws IOException, ServletException {
String username = (String) pRequest.getParameter("username");
String password = (String) pRequest.getParameter("password");
// Session Object
HttpSession l_session = null;
String l_sessionCookieId = getCookieValue(pRequest, "JSESSIONID");
String l_persistentCookieId = getCookieValue(pRequest, "MY_SESSION_COOKIE");
// If a session cookie has been created
if (l_sessionCookieId != null)
{
// If there isn't already a persistent session cookie
if (l_persistentCookieId == null)
{
addCookie(pResponse, "MY_SESSION_COOKIE", l_sessionCookieId, 1800);
}
}
// If a persistent session cookie has been created
if (l_persistentCookieId != null)
{
HashMap<String, HttpSession> l_activeUsers = (HashMap<String, HttpSession>) pRequest.getServletContext().getAttribute("activeUsers");
// Get the existing session
l_session = l_activeUsers.get(l_persistentCookieId);
}
// Otherwise a session has not been created
if (l_session == null)
{
// Create a new session
l_session = pRequest.getSession();
}
//If the user info is in session, move forward to another page
String forward = "/pages/displayUserInfo.jsp";
//Get the user
User user = (User) l_session.getAttribute("user");
//If there's no user
if (user == null)
{
// Put the user in session
if (username != null && password != null)
{
l_session.setAttribute("user", new User(username, password));
}
// Ask again for proper login
else
{
forward = "/pages/login.jsp";
}
}
//Forward
this.getServletContext().getRequestDispatcher(forward).forward( pRequest, pResponse );
}
The MY_SESSION_COOKIE cookie will save the value of the JSESSIONID cookie. When the JSESSIONID cookie is destroyed, the MY_SESSION_COOKIE is still there with the session ID.
JSESSIONID is gone with the web browser session, but we chose to use a persistent and simple cookie, along with a map of all active sessions put into the application context. The persistent cookie allow us to find the right session in the map.
Don't forget these useful methods made by BalusC to add/get/remove cookies :
/**
*
* #author BalusC
*/
public static String getCookieValue(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
/**
*
* #author BalusC
*/
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
/**
*
* #author BalusC
*/
public static void removeCookie(HttpServletResponse response, String name) {
addCookie(response, name, null, 0);
}
}
The last solution was tested with glassfish on localhost, with chrome for webbrowser, on windows. It only depends on a single cookie, and you don't need a database. But actually, I don't know what are the limits of such a mechanism. I only spent the night coming to this solution, without knowing if it will be a good or a bad one.
THANKS
I'm still learning, please tell me if there's any error in my answer. Thanks, #+
The correct answer has many flaws, see my comment there. The matter is actually easier. You will need a persistent datastore (such as a SQL database). You can use ServletContext as well, but the user will be logged out after server restart or application redeploy. Don't forget to properly synchronize, if you use a HashMap in ServletContext, as it might be accessed concurrently from more threads.
Don't hack with server's session and it's ID, it's not under your control and some servers change session ID if a request with JSESSIONID appears after the server expired the original session. Roll your own cookie.
Basically you need:
own cookie, that is not persistent, with a securely random value
a datastore
a javax.servlet.Filter to check login
The filter implementation might look like this:
public class LoginFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// Java 1.8 stream API used here
Cookie loginCookie = Arrays.stream(req.getCookies()).filter(c -> c.getName()
.equals("MY_SESSION_COOKIE")).findAny().orElse(null);
// if we don't have the user already in session, check our cookie MY_SESSION_COOKIE
if (req.getSession().getAttribute("currentUser") == null) {
// if the cookie is not present, add it
if (loginCookie == null) {
loginCookie = new Cookie("MY_SESSION_COOKIE", UUID.randomUUID().toString());
// Store that cookie only for our app. You can store it under "/",
// if you wish to cover all webapps on the server, but the same datastore
// needs to be available for all webapps.
loginCookie.setPath(req.getContextPath());
loginCookie.setMaxAge(DAYS.toSeconds(1)); // valid for one day, choose your value
resp.addCookie(loginCookie);
}
// if we have our cookie, check it
else {
String userId = datastore.getLoggedUserForToken(loginCookie.getValue());
// the datastore returned null, if it does not know the token, or
// if the token is expired
req.getSession().setAttribute("currentUser", userId);
}
}
else {
if (loginCookie != null)
datastore.updateTokenLastActivity(loginCookie.getValue());
}
// if we still don't have the userId, forward to login
if (req.getSession().getAttribute("currentUser") == null)
resp.sendRedirect("login.jsp");
// else return the requested resource
else
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
After the user logs in, you should add the value of MY_SEESSION_COOKIE to the datastore along with the userId and remove it upon logout. You must also store the expiration date to the datastore and check it before accepting the token, you must not rely on the browser respecting the maxAge property.
And don't forget to add some datastore cleanup to prevent outstanding cookies to hang around forever.
The above code was not tested in real life, there might be some quirks, but the basic idea should work. It's at least a lot better than the accepted solution.