Given that,
I have a
USER_ACTIVITY_LOG table
that contains
USER_ID, SESSION_ID, ACTIVITY_CODE(Login/LogOut/TimeOut) &
ACTIVITY_TIME columns
in that. It updates all activities performed on the application in the table on basis of UserId & SessionId.
Problem Statement :
I want to restrict logins to maximum 2 users with the same credentials on my application.
Say at a time 2 users are logged into the application with the same credentials (eg: admin/admin) and now 3rd user is trying to log into application with the same credentials. In such case the oldest logged in user session should be invalidated.
I have to query USER_ACTIVITY_LOG table on every user login and check number of users logged in with the same userId and are not logged out.
If I get count of 2 users that are still logged in, I simply want to invalidate the session of the oldest user on basis of just SESSION_ID.
Is that possible ?
My project is on Java 8, Jboss 6.4, J2EE, Struts 2 & Oracle.
This is the fix I found out and I am using it as of now. No matter it will impact the performance a bit.
public void expireSessionWithId(String sessionID)
{
try {
MBeanServer server = java.lang.management.ManagementFactory.getPlatformMBeanServer();
ObjectName objectName=new ObjectName("jboss.web:type=Manager,path=/test,host=default-host");
// declare signature of the parameter
String[] sig = { "java.lang.String"};
// declare parameter
Object[] opArgs1 = { sessionID };
// call the method
String value = (String) server.invoke(objectName, "expireSession",
opArgs1, sig);
System.out.println(value);
} catch (MalformedObjectNameException e) {
//handle the exception
} catch (InstanceNotFoundException e) {
//handle the exception
} catch (ReflectionException e) {
//handle the exception
} catch (MBeanException e) {
//handle the exception
}
}
Related
We've migrated from adal4j to msal4j in our java web applications.
All works well but the big difference is that when the user is already logged (maybe in other applications but same browser session) we always see the "select user" page and the user is not logged automatically and redirected to redirect uri as before with adal4j.
This is how we redirect to autentication page:
private static void redirectToAuthorizationEndpoint(IdentityContextAdapter contextAdapter) throws IOException {
final IdentityContextData context = contextAdapter.getContext();
final String state = UUID.randomUUID().toString();
final String nonce = UUID.randomUUID().toString();
context.setStateAndNonce(state, nonce);
contextAdapter.setContext(context);
final ConfidentialClientApplication client = getConfidentialClientInstance();
AuthorizationRequestUrlParameters parameters = AuthorizationRequestUrlParameters
.builder(props.getProperty("aad.redirectURI"), Collections.singleton(props.getProperty("aad.scopes"))).responseMode(ResponseMode.QUERY)
.prompt(Prompt.SELECT_ACCOUNT).state(state).nonce(nonce).build();
final String authorizeUrl = client.getAuthorizationRequestUrl(parameters).toString();
contextAdapter.redirectUser(authorizeUrl);
}
I've tried to remove .prompt(Prompt.SELECT_ACCOUNT)
but I receive an error
Any ideas?
• You might be getting the option for selecting the user account after switching to MSAL4J in your browser even after the SSO is enabled because either clearing the token cache is enabled in your code or MsalInteractionRequiredException option is thrown and specified accordingly due to which the application asks for a token interactively.
Thus, please check which accounts information is stored in the cache as below: -
ConfidentialClientApplication pca = new ConfidentialClientApplication.Builder(
labResponse.getAppId()).
authority(TestConstants.ORGANIZATIONS_AUTHORITY).
build();
Set<IAccount> accounts = pca.getAccounts().join(); ’
Then, from the above information, if you want to remove the accounts whose prompts you don’t want to see during the user account selection such that the default account should get selected and signed in automatically, execute the below code by modifying the required information: -
Set<IAccount> accounts = pca.getAccounts().join();
IAccount accountToBeRemoved = accounts.stream().filter(
x -> x.username().equalsIgnoreCase(
UPN_OF_USER_TO_BE_REMOVED)).findFirst().orElse(null);
pca.removeAccount(accountToBeRemoved).join();
• And for the MsalInteractiveRequiredException class in the code, kindly refer to the below official documentation link for the AcquireTokenSilently and other reasons responsible for the behaviour. Also, refer to the sample code given below for your reference regarding the same: -
https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-error-handling-java#msalinteractionrequiredexception
IAuthenticationResult result;
try {
ConfidentialClientApplication application =
ConfidentialClientApplication
.builder("clientId")
.b2cAuthority("authority")
.build();
SilentParameters parameters = SilentParameters
.builder(Collections.singleton("scope"))
.build();
result = application.acquireTokenSilently(parameters).join();
}
catch (Exception ex){
if(ex instanceof MsalInteractionRequiredException){
// AcquireToken by either AuthorizationCodeParameters or DeviceCodeParameters
} else{
// Log and handle exception accordingly
}
}
I am using the latest Java Realm local database backing a mobile app.
I have a Profile class and a Contact class in the app. Each Profile object can be associated with one or more Contact objects.
Upon startup, I want to reconcile against a similar Profile/Contact list on my website to insure that the mobile app is using the latest/greatest definition of the Profile/Contact relationship.
After validating the Profile during login, I then query the website to get the list of Contact email addresses associated with the Profile. I now need to do the following:
For each Contact email address in the list from the website, make sure that the local Realm Contact object shows as "connected" to the Profile object.
For each Contact object NOT in the list from the website, make sure that the local Realm Contact object shows as "not connected" to the Profile object.
So what I'm doing is after login, executing an AsyncTask that:
try {
realm.beginTransaction();
RealmResults<Contact> contactResults;
contactResults = realm.where(Contact.class).findAll();
if ( contactResults.size() == 0 ) {
// Nothing to do
return false;
}
// First set everything as not connected, then set only those passed in as connected
contactResults.setBoolean(IS_CONNECTED, false);
// Now update just the email addresses passed in as connected
contactResults = realm.where(Contact.class).in(EMAIL_ADDRESS, emailList.toArray(new String[0])).findAll();
if ( contactResults.size() > 0) {
contactResults.setBoolean(IS_CONNECTED, true);
}
//TODO - what if an item passed in is not yet in Contact list?
success = true;
} catch (Exception e) {
Timber.d("Exception resetting contacts status: %s", e.getMessage());
} finally {
if (success) {
realm.commitTransaction();
} else {
realm.cancelTransaction();
}
}
How I've done the first two pieces of work seem ok to me - as this app won't be scaling to thousands of "friends", resetting the contact to FALSE for everyone then to TRUE for the current set retrieved from the website is doable.
However, the third piece has me stumped - is there a single call I can make to identify which, if any, of the email addresses in the passed array aren't actually in my Contact object RealmResults and therefore need to be added as new objects?
Seems inefficient to loop through that list and check each one, one at a time, particularly since I've already performed two queries.
The only solution I've come up with so far is this:
try {
realm.beginTransaction();
fullContactResults = realm.where(Contact.class).findAll();
if ( fullContactResults.size() > 0 ) {
// First set everything as not connected, then set only those passed in as connected
fullContactResults.setBoolean(IS_CONNECTED, false);
}
// Now for each item in the list we were passed in find it and update it, or add it.
for ( String data : contactData ) {
Contact c = null;
String[] contactStruct = data.split(BODY_COMPONENTS_SEPARATOR);
RealmResults<Contact> partialContactResults = realm.where(Contact.class)
.equalTo(EMAIL_ADDRESS, contactStruct[CONNECTION_EMAIL_ELE])
.findAll();
if (partialContactResults.size() > 0 ) {
c = partialContactResults.first();
} else {
// Need to add the contact
c = realm.createObject(Contact.class, UUID.randomUUID().toString());
}
c.setConnected(true);
c.setDisplayName(contactStruct[CONNECTION_FIRSTNAME_ELE] + " " + contactStruct[CONNECTION_LASTNAME_ELE]);
c.setEmailAddress(contactStruct[CONNECTION_EMAIL_ELE]);
}
success = true;
} catch (Exception e) {
Timber.d("Exception resetting contacts status: %s", e.getMessage());
} finally {
if (success) {
realm.commitTransaction();
} else {
realm.cancelTransaction();
}
}
Which does a loop and either updates or inserts.
Any better way?
I have a login servlet that processes post request with username and password parameters, and does something like this:
Instant pageGenStart = Instant.now();
String username = req.getParameter("username");
String password = req.getParameter("password");
resp.setContentType("text/html;charset=utf-8");
User user = null;
try {
user = userService.getByUsername(username);
} catch (SQLException e) {
e.printStackTrace();
}
if (user == null && !password.equals("")) {
try {
user = new User(username, password);
user.setId(userService.addNewUser(username, password));
charactersService.addNewCharacter(user);
sessionsService.add(req.getSession().getId(), user.getId());
} catch (SQLException e) {
e.printStackTrace();
}
resp.setContentType("text/html;charset=utf-8");
resp.setStatus(HttpServletResponse.SC_OK);
Duration time = Duration.between(pageGenStart, Instant.now());
resp.sendRedirect("/main");
}
If user is not found in db, create new user and redirect him to main page. Normally i would just put this "time" variable into page, but i redirect my response to other servlet where doGet method is called. How do i let other servlet know how long login servlet took to proccess post request?
You can pass value from one servlet to another in many ways like :
Storing values in session ( You have to take care of session management)
Creating class having static ConcurrentHashMap and storing time gap per user session and fetching it using session ID. (same problem need to take care when to clear cache).
Use already defined lib ( best option as you need not to worry about session management and cache clear).
I perfer EH-cache for these kind of perposes you can see a good example from the Link.
After a successful login I'm calling:
ParseUser currentUser = ParseUser.getCurrentUser();
currentUser.isAuthenticated()
Now if I switch to another app via the home button or multitasking and return to my app the currentUser is still authenticated.
But if I force close the app and then reopen it the currentUser is not authenticated. Therefore it seems that I can't access any objects from the network which have the default Access Control List (ACL) added to them via:
Parse.enableLocalDatastore(this);
ParseACL.setDefaultACL(new ParseACL(), true);
Update with sample code:
// Pinning
ParseObject gameScore = new ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.put("cheatMode", false);
gameScore.pinInBackground("NEW_GAMESCORES", null);
// Syncing Local Changes
ParseQuery<ParseObject> localQueryNewScores = ParseQuery
.getQuery("GameScore");
localQueryNewScores.fromPin("NEW_GAMESCORES");
localQueryNewScores.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> scores, ParseException e) {
Log.d("score", "New scores = " + scores.size());
for (ParseObject score : scores) {
score.saveInBackground();
score.unpinInBackground("NEW_GAMESCORES", null);
score.pinInBackground("GAMESCORES", null);
}
}
});
// Syncing Network Changes
ParseQuery<ParseObject> networkQueryScores = ParseQuery
.getQuery("GameScore");
// Query for new results from the network.
networkQueryScores.findInBackground(new FindCallback<ParseObject>() {
public void done(final List<ParseObject> scores, ParseException e) {
Log.d("score", "Network scores = " + scores.size());
// Remove the previously cached results.
ParseObject.unpinAllInBackground("GAMESCORES",
new DeleteCallback() {
public void done(ParseException e) {
// Cache the new results.
ParseObject.pinAllInBackground("GAMESCORES",
scores);
}
});
}
});
// Querying the Local Datastore
ParseQuery<ParseObject> localQueryScores = ParseQuery
.getQuery("GameScore");
localQueryScores.fromPin("GAMESCORES");
localQueryScores.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> scores, ParseException e) {
Log.d("score", "Local scores = " + scores.size());
}
});
Log output just after I've ran the code several times:
New scores = 2
Local scores = 0
Network scores = 0
New scores = 0
Local scores = 0
Network scores = 2
New scores = 2
Local scores = 2
Network scores = 2
New scores = 1
Local scores = 2
Network scores = 4
Log output just after I've force closed the app:
New scores = 0
Local scores = 4
Network scores = 0
New scores = 2
Local scores = 0
Network scores = 0
As you can see at Network scores = 0 after the force close I am unable to query any results from the network where I // Query for new results from the network to update the pinned objects in the Local Datastore with new results from the network. This happens even though I am constantly connected to the internet after the first login.
But as I need to sync back changes from the network to the Local Datastore I'm depending on this query.
So how can I still query the network for objects that are stored with ACL added to the currentUser, after I force close the app?
Update 2
I found others with the same problem which has been reported here:
developers.facebook.com/bugs/702967266408226
It seems to be a bug in the new Parse Android SDK 1.5. I will update this post as soon as it's clear that my problem was related to the reported bug.
Maybe save password to file, and if u reopen app after force close, log in with this saved password ?
You could save the username and password in a SharedPreferences and log in again when the user re opens your app.
I think you could just check to see whether getCurrentUser is null or not instead of using isAuthenticated.
boolean isLoggedIn = ParseUser.getCurrentUser() != null;
isAuthenticated states:
Whether the ParseUser has been authenticated on this device. This will
be true if the ParseUser was obtained via a logIn or signUp method.
Only an authenticated ParseUser can be saved (with altered attributes)
and deleted.
getCurrentUser is always the user that has signed up or logged in. So if it is not null, the user is logged in. isAuthenticated is just to see if the user object was retrieved from a login/sign up or not. For example, if you queried for other ParseUser objects, they would not be authenticated.
Also notice that when you do a logOut on the current user, getCurrentUser will be null.
I also noticed that if you were not using the Local Datastore, currentUser.isAuthenticated() would always return true even when force closing the application.
It seems that when you are using the Local Datastore, where the current user is retrieved locally, the object is no longer seen as "authenticated" as in the source is not from a login or sign up.
Parse Android Changelog
v1.5.1 — May 30, 2014
Fixed various bugs with Local Datastore.
The problem was indeed a Local Datastore bug which has been fixed today with the release of the new Parse Android SDK version 1.5.1.
I am new to this forum. I have a doubt about JSP/servlet in my application
I have developed an application in which user may search some data based on some criteria and he will get data from database(through Hibernate to servlet and to JSP). When some data is displayed on screen based on search he/she may try to copy the URL and forward to anyone or If he try to open in different browser it is showing an empty page.
eg: if i try to paste the link given bellow it is showing blank page
example link
but i need to display the data how this can be achieved.
Edited: After clicking on job search in menu bar as mentioned in comments the page will redirect to a servlet
if(action.equals("searchjob")){
String requireskills=request.getParameter("txt_requireSkills");
String location=request.getParameter("txt_locationName");
session.setAttribute("location",location);
String minexp1=request.getParameter("dd_minimum");
String maxexp1=request.getParameter("dd_maximum");
jobsearchDAO = new JobSearchDAOImpl();
List<JobPostInfo> data=jobsearchDAO.jobsearchlist(requireskills,location,minexp1,maxexp1);
if(data!=null && data.size()!=0){
//save data
if(!(session.getAttribute("LoginObject")==null)){
JobSeeker jobSeeker=(JobSeeker)session.getAttribute("LoginObject");
JobSearchCriteria jobsearchcriteria= new JobSearchCriteria();
jobsearchDAO=new JobSearchDAOImpl();
jobsearchcriteria.setKeyWords(requireskills);
jobsearchcriteria.setLocation(location);
JobSeeker jobseeker=(JobSeeker)session.getAttribute("jobseeker");
//
// jobsearchcriteria.setJobSeeker(jobseeker.getJobSeekerSn());
jobsearchcriteria.setJscTs(new Date());
int value=jobsearchDAO.savesearch(jobsearchcriteria);
System.out.println("savesearch value------>"+value);
}
session.setAttribute("jobsearchlist", data);
// session.setAttribute("success","Search Criteria is saved to database.");
response.sendRedirect("jobsearchresult.jsp");
}else
{
session.setAttribute("error","No Records found");
response.sendRedirect("jobsearch.jsp");
}
}
This is the code in DAOIMPL
public List<JobPostInfo> jobsearchlist(String requireskills,String location,String minexp1,String maxexp1) throws Exception{
long minexp;
long maxexp;
try{
session =getSession();
//Criteria Query
Criteria query=session.createCriteria(JobPostInfo.class,"jpost");
// if(minexp1.equals("0") && (maxexp1.equals("") || maxexp1==null)){
if((minexp1.equals("-1") || minexp1=="-1") && maxexp1==null){
}
else if(minexp1.equals("0")){
minexp=Long.parseLong(minexp1);
long min=1;
query.add(Restrictions.lt("jpost.experienceMin",min));
}else if(!(minexp1.equals("") || minexp1==null) && maxexp1.equals("-1")) {
minexp=Long.parseLong(minexp1);
query.add(Restrictions.ge("jpost.experienceMin",minexp));
}else if(!(minexp1==null && maxexp1==null)){
minexp=Long.parseLong(minexp1);
maxexp=Long.parseLong(maxexp1);
query.add(Restrictions.and(Restrictions.ge("jpost.experienceMin",minexp),Restrictions.le("jpost.experienceMax",maxexp)));
}
//For Location
if(!(location==null|| location.equals(""))){
query.createAlias("jpost.location","location");
query.add(Restrictions.like("location.locationName",location).ignoreCase());
}
//For Keyword
if(!(requireskills==null || requireskills.equals(""))){
query.add(Restrictions.like("jpost.requiredSkills","%"+requireskills+"%").ignoreCase());
}//requireskills
List<JobPostInfo> list = query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
if(list.size()==0){
return null;
}else{
return list;
}
}catch(HibernateException e){
e.printStackTrace();
}finally {
close(session);
}
return null;
}
I solved my problem. It's a very basic mistake and I hope this will help others:
response.sendRedirect("jobsearchresult.jsp") is replaced by request.getRequestDispatcher("studentinformation.jsp").forward(request, response)
or include-method. The second thing is, the session is created and initialized with the servlet. When I copy the link in a different browser, a certain block of the servlet will be executed. Example:
action.equals("searchjob")
So at the time the session is not available yet, I initialize it in every block like separating declaration and initialization.