I am successfully using Google Cloud Endpoints. Now for custom user auth, I want to use HTTPSession. The problem is, the initial session is not being reused in future calls, and new session are created (I can see from datastore admin that the session all exists, _AH_SESSION entity). As instructed in the docs, i have enabled it in appengine-web.xml:
<sessions-enabled>true</sessions-enabled>
I made some sample code to narrow it down:
#Api(name = "loginService", version = "v0.1.5")
public class LoginService {
private static final Logger log = Logger.getLogger(LoginService.class.getName());
#ApiMethod(name = "login", path= "login")
public LoginResponse login(HttpServletRequest req)
{
HttpSession session = req.getSession(false);
if(session == null){
log.warning("creating new session");
session = req.getSession(true);
}
LoginResponse resp = new LoginResponse();
resp.statusCode = 200;
resp.statusMessage = "SessionId:" + session.getId();
return resp;
}
#ApiMethod(name = "show", path= "show")
public LoginResponse show(HttpServletRequest req)
{
//session should exist when calling LOGIN first
HttpSession session = req.getSession(false); //NULLPOINTER since session from login is not being reused/found!
LoginResponse resp = new LoginResponse();
resp.statusCode = 200;
resp.statusMessage = "SessionId:" + session.getId();
return resp;
}
public class LoginResponse implements Serializable{
public int statusCode;
public String statusMessage;
}
}`
So first, I call the login method, this creates a new session and prints me the session id. Then in the next request (both using Postman - which should track sessions - in Chrome as the API explorer) i call the 'show' endpoint, and there the previous session does not exist anymore, hence the nullpointer exception.
In the comments on this post, user mikO says endpoints don't keep the session. Is this the reason? I don't really understand the reason behind it. When I just deploy a 'regular' servlet on appengine, it DOES work using Postman or just browsing.. (testing by calling the getter twice, i see that the previous session is being picked up), so it seems that the comment in that post could be right, but i really don't understand why. Working code without endpoints:
public class LoginServlet extends HttpServlet {
private static final Logger log = Logger.getLogger(LoginServlet.class.getName());
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession s = request.getSession(false);
if (s == null) {
log.warning("creating new session");
s = request.getSession(true);
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>" + s.getId() + "</h1>");
}
}
Thanks!
Related
I have the following service subscription in my component:
this.userService.loginUser(this.user).subscribe(users => {
if (users == null) {
console.log('users is null');
this.isValid = !this.isValid;
} else {
this.userService.subscribers.next(users);
localStorage.setItem('user', JSON.stringify(users));
console.log(`User, ${this.user.username}, successfully logged in!`);
console.log(localStorage.getItem('user'));
this.router.navigate(['dashboard']);
}
});
The service method looks like this:
public loginUser(user: User) {
console.log(`Attempting to login user: ${user.username}`);
const json = JSON.stringify(user);
this.currentUser = user;
return this.http.post<User>(API_URL + 'login', json, HTTP_OPTIONS);
}
The post request goes to this java servlet method:
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("login attempt");
if (req.getInputStream() != null) {
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(req.getInputStream(), User.class);
user = userService.loginUser(user);
if (user != null) {
user.setPassword("");
HttpSession session = req.getSession();
session.setAttribute("user", user);
}
}
}
On the java end, a username and password match is found and the session attribute is correctly set. However, on the Angular end, 'users' is null, as evidenced by the console.log('users is null') in the first code block. So my question is, why is this null? Can someone please help me understand the mechanics of this? I'm having a very hard time grasping how certain aspects of Angular even work...
So the problem was that in the servlet I didn't have a response. At the end I needed
PrintWriter out = resp.getWriter();
out.write(mapper.writeValueAsString(user));
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 need to retrieve users profile from Http Session. Since I need the same profile in couple different methods within the class is there a way that all the method access the same session? So I dont have to put the http request multiple times. Below is the example of session and methods.
In the below methods you will see im putting the http request in both methods since they are both in same class it would help if I dont create seprate sessions.
Http Request:
HttpSession session = request.getSession();
EntRProfile profile = (EntRProfile) session.getAttribute("profile");
Sample methods:
private Integer getClaimCount(HttpServletRequest request, String searchCriteria) throws Exception {
HttpSession session = request.getSession();
EntRProfile profile = (EntRProfile) session.getAttribute("profile");
Integer claimsCount = claimTDAO.getCountOfClaimsWithCurrentStatusNot(profile, "INI", searchCriteria);
request.getSession().setAttribute("claimsPaginationCount", claimsCount);
return claimsCount;
}
2nd Method
#RequestMapping(value = "/claims_history", method = RequestMethod.GET)
public String getClaims(HttpServletRequest request, #RequestParam(value = "claimsSearch", required = false) String searchCriteria, boolean keepOffset) throws Exception {
HttpSession session = request.getSession();
EntRProfile profile = (EntRProfile) session.getAttribute("profile");
request.getSession().setAttribute("claimsList", claimsList);
request.getSession().setAttribute("claimsSearchCriteria", searchCriteria);
Integer count = claimTDAO.getCountOfClaimsWithCurrentStatusNot(profile, "INI");
request.setAttribute("claimsHistoryCount", count);
return "claims_history";
}
You can create a private method in your controller:
private EntRProfile getProfile(HttpServletRequest request) {
HttpSession session = request.getSession();
EntRProfile profile = (EntRProfile) session.getAttribute("profile");
return profile;
}
Then you can get the profile by calling:
EntRProfile profile = getProfile(request);
I have a servlet where I need to declare a session which can be acceptable form doGet and doPost both how I should do this?
I have done
#WebServlet(name = "LoginLogout", urlPatterns = {"/LoginLogout.do"})public class LoginLogout extends HttpServlet {//For Session
HttpSession session = request.getSession(true);
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String status = request.getParameter("status");
System.out.println(status);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
String loginId = request.getParameter("login_id");
String password = request.getParameter("password");
System.out.println(loginId);
//Inserting value to the Pogo named "newLoginPogo"
loginData newLoginPogo = new loginData();
newLoginPogo.setLoginId(loginId);
newLoginPogo.setPassword(password);
//Creating a obj of ModelLogin to send the loginId and Password via a method which is in ModelLogin class
ModelLogin loginBis = new ModelLogin();
loginData userData = loginBis.checkUser(newLoginPogo);
String userExist = userData.getUserExist();
System.out.println(userExist);
if ("yes".equals(userExist)) {
System.out.println("In while loop of Servlet");
String firstName = userData.getFirstName();
String userId = userData.getUserId();
boolean IsSu = userData.getIsSu();
//conveting boolean to string
String superuser = new Boolean(IsSu).toString();
//Creating a session
session.setAttribute("firstName", firstName);
session.setAttribute(userId, "userId");
session.setAttribute(superuser, "IsSu");
//==============================================================================================================
//If user does exist show the Success Message and forward Dashboard
//==============================================================================================================
//Session for success message
String succmsg = "Login Successful";
session.setAttribute("succmsg", succmsg);
getServletConfig().getServletContext().getRequestDispatcher("/WEB-INF/ViewPages/dashboard/dashboard.jsp").forward(request, response);
} //==============================================================================================================
//If user does not exist show the Error Message
//==============================================================================================================
else if ("no".equals(userExist)) {
//Session for success message
System.out.println("inside NO");
String emsg = "Login Error";
session.setAttribute("errmsg", emsg);
getServletConfig().getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
} else {
}
/*
//===============================================================================================================
//code for Logout
//===============================================================================================================
String status = request.getParameter("status");
if ("logout".equals(status)) {
//clearing the session
session.invalidate();
//forwarding to index page
getServletConfig().getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
}
*/
} finally {
}
}}
But it says
Can Not find Symbol
in this line HttpSession session = request.getSession(true);
You don't need to have session variable in servlet as field. In general - this is kind of common mistake. There will be only one onstance of servlet serving lots of requests, and unless you declare it as single-threaded - the requests would be handled concurrently.
HttpSession will be pre-exist for you in doGet and doPost via request object. Servlet container will guarantee this. So simply obtain reference to the session in doGet/doPost and do whatever you want.
What you desire is one of the roles of HTTP session.
You can look at it as a conversation between the client and the server.
As long as the "conversation" (HTTP session) is open and alive, you can set variables on the HTTP session, and access them from different requests that will sent on the same session.
Look at this as some sort of "shared memory" that exists during the "conversation time".
You can find many examples on how to do that over the internet.
Here is an example for session tracking.
I'm attempting to write tests for a very long and kludgy "getPost" method in a webapp I'm working on for my job. I'm using JUnit, HtmlUnit, and Jetty's ServletTester to approximate sending a request to a servlet and receiving a response. I've managed to get it mostly working, but I'm having a problem. I'm trying to test the login functionality. If the user logs in successfully, the server should send some JSON back to the client with the user's information. If the user is already logged in, the server should send back "result": "failure" and an error message.
My problem comes when I try to test the second requirement. I can log in successfully, and get the correct data back. However, when I try to send the request again, it returns 404: not found. I tried breaking the code up into different tests, but I have to be able to call login twice in order to test the second requirement. Later tests in the JUnit file run just fine, and the servlet is staying connected the same time. I tried making a second, identical request, but that also failed. I've searched the internet to no avail. In short, I'm stumped.
Here's what I'm working with (unnecessary code has been edited out):
//In MyFunServlet class:
private final static String USERID_ATTRIBUTENAME = "userid";
void doPost(HttpServletRequest request, HttpServletResponse response) {
String action = request.getParameter("opt");
final HttpSession session = request.getSession();
if(action != null){
Long userId = (Long)session.getAttribute(USERID_ATTRIBUTENAME);
if(userId != null){
//do stuffz
} else {
if(action.equals("login")) {
User user = LoginUser(request, response);
try{
JSONObject json = new JSONObject();
if(request.getAttribute("result") == "success"){
json.put("result", "success");
json.put("id", user.getId());
json.put("name", user.getName());
} else {
json.put("result", "failure");
json.put("message", request.getAttribute("message"));
}
SendJSONResponse(json, request, response);
}catch(Exception e){
}
} else {
System.out.print("Unknown opt: " + action);
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
}
}
}
private void LoginUser(HttpServletRequest request, HttpServletResponse response){
final HttpSession session = request.getSession();
User user = null;
Long userId = (Long)session.getAttribute(USERID_ATTRIBUTENAME);
if(userId != null){
request.setAttribute("result", "failure");
request.setAttribute("message", "The user is already logged in.");
} else {
final String email = request.getParameter("accountEmail");
final String password = request.getParameter("accountPassword");
if(email != null) {
user = helperClass.magicallyGetUserByEmail(email);
if(user != null){
if(user.getPassword().equals(password)){
session.setAttribute(USERID_ATTRIBUTENAME, user.getId();
request.setAttribute("result", "success");
}
}
} else {
request.setAttribute("result", "failure");
}
}
return user;
}
private void SendJSONResponse(JSONObject json, HttpServletRequest request,
HttpServletResponse response) {
String contentStr = json.toString();
response.setContentType("application/json");
response.setStatus( HttpServletResponse.SC_OK);
response.setContentLength(contentStr.length());
response.getWriter().print(contentStr);
response.flushBuffer();
}
For reference purposes, this file is 1084 lines long. The doPost method is about 900 of those. Disclaimer: this is not my code. I did not write it. I only have to test it.
Now for the test:
//In MyFunServletTest.java:
//using JUnit 4
public class MyFunServletTest {
static ServletTester tester;
static String baseUrl;
WebClient webClient = new WebClient();
User user;
WebRequest loginRequest;
#BeforeClass
public static void initClass(){
tester = new ServletTester;
tester.setContextPath("/");
tester.addServlet(MyFunServlet.class, "/fun.service");
baseUrl = tester.createSocketConnector(true);
tester.start();
}
#AfterClass
public static void cleanClass() {
tester.stop();
}
#Before
public void preTest(){
//taking values from our magical test user
user = new User();
user.setEmail("blah#blah.com");
user.setPassword("secure");
loginRequest = new WebRequest(baseUrl + "/fun.service", HttpMethod.POST);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new NameValuePair("opt","login"));
params.add(new NameValuePair("accountEmail", user.getEmail());
params.add(new NameValuePair("accountPassword", user.getPassword());
loginRequest.setRequestParameters(params);
}
#Test
public void testLogin() {
WebResponse response = webClient.loadWebResponse(loginRequest);
JSONObject responseJSON = new JSONObject(response.getContentAsString());
//this test passes
assertEquals("success", responseJSON.getString("result"));
response = webClient.loadWebResponse(loginRequest);
//this test fails
assertTrue(404 != response.getStatusCode());
//this then causes an error, as response.getContentAsString() is null.
esponseJSON = new JSONObject(response.getContentAsString());
}
}
Help? Where am I missing something?
Thanks.
Without the ability to run the test myself, I can only offer some approaches:
Try creating two JSONObject objects to store the two responses separately, and compare the two (either print them or using the debugger), see if anything looks odd there.
If that doesn't tell you anything, create two separate identical request instances and use each.
Then try tracing through the call to loadWebResponse to see exactly what URL is being requested (cranking up the log level might tell you this, too).
If the 404 is correct, then the second request is somehow being mangled, but the question would be WHERE.