How to see who's logged in from server.xml file? - java

I have configured the server.xml file in a Java Spring application to authenticate users when logging in from database tables and roles. I'm wondering how in Java code can I check who's logged in to the application?
I know that in a jsp file I can just use this following syntax to show the name:
${pageContext.request.userPrincipal.name} .

In your Spring MVC Controller, just add the following statement:
String loggedUser = request.getUserPrincipal().getName();
where request is the object of HttpRequest type, made available to you by Spring on demand.

There is very beautiful article for this is given at http://www.baeldung.com/spring-security-track-logged-in-users
You can leverage the HttpSessionBindingListener to update the list of logged in users whenever user information is added to the session or removed from the session based on user logs into the system or logs out from the system.
It will listen to events of type HttpSessionBindingEvent, which are triggered whenever a value is set or removed, or, in other words, bound or unbound, to the HTTP session.
#Component
public class LoggedUser implements HttpSessionBindingListener {
private String username;
private ActiveUserStore activeUserStore;
public LoggedUser(String username, ActiveUserStore activeUserStore) {
this.username = username;
this.activeUserStore = activeUserStore;
}
public LoggedUser() {}
#Override
public void valueBound(HttpSessionBindingEvent event) {
List<String> users = activeUserStore.getUsers();
LoggedUser user = (LoggedUser) event.getValue();
if (!users.contains(user.getUsername())) {
users.add(user.getUsername());
}
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
List<String> users = activeUserStore.getUsers();
LoggedUser user = (LoggedUser) event.getValue();
if (users.contains(user.getUsername())) {
users.remove(user.getUsername());
}
}
// standard getter and setter
}
You can go through the whole code here
You can also retrieve the current logged in user from Spring security
Go through this artical
Or through Request also
request.getUserPrincipal().getName();

You can write a method to get current logged in user as you might need this various places like below :
public User getCurrentLoggedInUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Object principal = auth.getPrincipal();
if (principal instanceof User) {
return ((User) principal);
}
}
}

Related

How to configure Spring Boot OAuth2.0 server with custom UserDetailsService and password validation?

I am trying to configure a Spring Boot OAuth2.0 server on my existing schema. To authenticate a user with my schema, I need to retrieve an encrypted password from my users table and pass it to an external service for validation.
I was able to define a custom UserDetailsService as below, but the default behavior authenticates based on a comparison of the password in the returned UserDetails object and the password from the request.
How can I customize this behavior so that I can use my custom UserDetailsService, but offload the password validation to the external service?
#Component("userDetailsService")
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private DataSource dataSource;
#Override
public UserDetails loadUserByUsername(final String login) {
String sql = "select encrypted_password from users where login_name = ?";
String encryptedPassword = null;
try {
Connection con = dataSource.getConnection();
PreparedStatement statement = con.prepareStatement(sql));
statement.setString(1, login);
try (ResultSet results = statement.executeQuery()) {
if (results.next()) {
encryptedPassword = results.getLong("encrypted_password");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return new User(login, encryptedPassword, Arrays.asList(new SimpleGrantedAuthority("simpleAuth")));
}
}
I probably don't have enough details to go off from your question, but I will make some assumptions and give you the best answer I can.
Assumption: User has to login using some sort of credential that you are then doing a loadUserByUsername(loginCreds).
If you don't want to use that specific UserDetails object, you can create an CustomAuthenticationFilter that is configured in your WebSecurity class by using http.addFilterBefore(new CustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter)
From there, you can customize your entire authentication flow. You can choose to create a CustomAuthenticationToken that in turn gets authenticated via CustomAuthenticationProvider. Within this provider, you can do your password check with your 3rd party validation. If it passes, you can load your user object as you have there.

JHipster Using LDAP Authentification

I've started to use JHipster weeks ago and everything went find since now. I want to have a LDAP authentification with at the same time the default authentification of JHipster.
I followed this https://jhipster.github.io/tips/016_tip_ldap_authentication.html and it doesn't work as planned.
Actually my configuration is connecting well to my LDAP server and i know by viewing logs that the login search into the LDAP server and compare the password.
The problem is the login fail with the error :
UT005023: Exception handling request to /api/authentication
org.springframework.security.core.userdetails.UsernameNotFoundException: User nseys was not found in the database
at com.mycompany.myapp.security.PersistentTokenRememberMeServices.lambda$onLoginSuccess$1(PersistentTokenRememberMeServices.java:116)
at java.util.Optional.orElseThrow(Optional.java:290)
at com.mycompany.myapp.security.PersistentTokenRememberMeServices.onLoginSuccess(PersistentTokenRememberMeServices.java:116)
at org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices.loginSuccess(AbstractRememberMeServices.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
...
The thing is I want JHipster to automatically create the user in database when it doesn't exist in there with a mapping of parameters (but only when it's a LDAP user) and just connect if it's already done.
I've searched Spring-security solution aswell but the implementations are too far away from the initial files created by JHipster and I don't want to destroy all this.
Well I tried something that work, I don't know if this is how I should have done, but since I've found nothing about that, and it's not documented alot, I'll stick with that solution unless I find a better solution.
// PersistentTokenRememberMeServices.java
protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication
successfulAuthentication) {
String login = successfulAuthentication.getName();
log.debug("Creating new persistent login for user {}", login);
PersistentToken t = new PersistentToken();
t.setSeries(RandomUtil.generateSeriesData());
t.setTokenValue(RandomUtil.generateTokenData());
t.setTokenDate(LocalDate.now());
t.setIpAddress(request.getRemoteAddr());
t.setUserAgent(request.getHeader("User-Agent"));
PersistentToken token = userRepository.findOneByLogin(login).map(u -> {
t.setUser(u);
return t;
}).orElse(null);
if (token == null) {
if (successfulAuthentication.getPrincipal() instanceof LdapUserDetails) {
User ldapUser = new User();
ldapUser.setLogin(login);
ldapUser.setPassword(RandomStringUtils.random(60)); // We use LDAP password, but the password need to be set
ldapUser.setActivated(true);
CustomLdapUserDetails customLdapUserDetails = (CustomLdapUserDetails) successfulAuthentication.getPrincipal();
ldapUser.setEmail(customLdapUserDetails.getEmail());
ldapUser.setFirstName(customLdapUserDetails.getFirstName());
ldapUser.setLastName(customLdapUserDetails.getLastName());
Set<Authority> authorities = new HashSet<>();
authorities.add(this.authorityRepository.findOneByName("ROLE_USER"));
ldapUser.setAuthorities(authorities);
ldapUser.setLangKey("fr");
userRepository.save(ldapUser);
t.setUser(ldapUser);
token = t;
} else {
throw new UsernameNotFoundException("User " + login + " was not found in the database");
}
}
...
}
And I added a contextMapper to get the attributes in the LDAP server
// SecurityConfiguration.java
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new LdapUserDetailsMapper() {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
UserDetails details = super.mapUserFromContext(ctx, username, authorities);
return new CustomLdapUserDetails((LdapUserDetails) details, ctx);
}
};
}
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(applicationProperties.getLdap().getUrl());
contextSource.setBase(applicationProperties.getLdap().getBase());
contextSource.setUserDn(applicationProperties.getLdap().getUserDn());
contextSource.setPassword(applicationProperties.getLdap().getPassword());
contextSource.afterPropertiesSet(); //needed otherwise you will have a NullPointerException in spring
auth.ldapAuthentication()
.userDetailsContextMapper(userDetailsContextMapper())
.userSearchBase(applicationProperties.getLdap().getSearchBase()) //don't add the base
.userSearchFilter(applicationProperties.getLdap().getSearchFilter())
.contextSource(contextSource)
;
}

Creating static methods for handling common servlet request and transaction operations

Consider these static methods outside of any servlet. All of them retrieve a hibernate session from a servlet filter.
public static User findUser(HttpServletRequest request) {
Session session = (Session)request.getAttribute("session"); // org.hibernate.Session
User user = (User)session.createQuery("from User u where u.email like :email and u.password like :password").setParameter("email", request.getParameter("email")).setParameter("password", hash(request.getParameter("password"))).uniqueResult();
return user;
}
public static User userExist(HttpServletRequest request) {
Session session = (Session)request.getAttribute("session"); // org.hibernate.Session
User user = (User)session.createQuery("from User u where u.email like :email").setParameter("email", request.getParameter("email")).uniqueResult();
return user;
}
public static User loadUser(HttpServletRequest request) {
Session session = (Session)request.getAttribute("session"); // org.hibernate.Session
User user = session.load(User.class, (long)request.getSession().getAttribute("userid"));
return user;
}
public static boolean existReservations(HttpServletRequest request, boolean checkCurrent) {
Date checkInDate = getDate(request.getParameter("checkInDate"));
Date checkOutDate = getDate(request.getParameter("checkOutDate"));
List<Reservation> res = loadUser(request).getReservations();
for (Reservation r : res) {
if (checkInDate.compareTo(r.getCheckOutDate()) <= 0 && checkOutDate.compareTo(r.getCheckInDate()) >= 0) {
if (checkCurrent && r.getReservationID() != Long.parseLong(request.getParameter("id"))) {
continue;
}
return true;
}
}
return false;
}
I will have to call these methods lots of times so they are in a Utils class. Now, I am absolutely sure that I will always call them if:
I'm in a servlet.
A hibernate session is open
A HTTPSession and a "userid" attribute is available.
In case of reservation methods, a parameter "id" is available
Now I found that doing this made my servlet code shorter. I personally think there are no problems doing this, I'm not modifying any attributes or parameters in HTTPServletRequest. I've seen many question asking if this is possible. But is this bad practice or I'm doing just fine? Are there cases where doing this approach make bad things happen?

How do I add both Facebook Login and Email registration to my Google Cloud Endpoints App (Java)?

So I have an app which uses Google App Engine and Google Cloud Endpoints as it's backend in Java. I'm currently working on User authentication and here is what I'm trying to do:
When user first opens the app, they'll have option to either "Login through Facebook" or signup using their email address. Then this data would be stored in a user object and after registration would direct them to the app homepage. It will be saved in their preferences so that they don't need to login every time they open the app (if ever).
Now I heard you can use a custom authenticator for Facebook, but there's not much documentation regarding this. How can I get the email registration and Facebook Login options to be implemented with Google Cloud Endpoint's Authenticator? Or should I make a different approach?
Thanks.
My approach is using the Facebook login method (Facebook SDK for Android). The Facebook authentication process returns (on success) an object from which I can get the user's email then I save it in my Endpoints class using Datastore API. To check if user already logged in I chose the SharedPreferences approach with GSON library to parse objects into JSON String and save them in the prefs.
Links and my sample codes below :
Regarding the Authenticator I found this SO answer
More info about Facebook login method
Saving custom objects in SharedPreferences
Getting user's email through Facebook auth
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
if (isSessionCalled == false) {
Log.i(TAG, "Logged in...");
System.out.println("Token=" + session.getAccessToken());
new Request(
session,
"/me",
null,
HttpMethod.GET,
new Request.Callback() {
public void onCompleted(Response response) {
if (response != null) {
GraphObject object = response.getGraphObject();
String email = (String) object.getProperty("email");
Log.i(TAG, "user email : " + email);
String firstName = (String) object.getProperty("first_name");
String lastName = (String) object.getProperty("last_name");
mUserTask = new UserAsyncTask();
mUserTask.execute(email);
}
}
}
).executeAsync();
isSessionCalled = true;
}
else {
Log.w(TAG, "session called twice");
}
}
else if (state.isClosed()) {
Log.i(TAG, "Logged out...");
}
}
Storing the user in my backend :
#ApiMethod(name = "storeUserModel")
public UserModel storeUserModel(UserModel userModel) throws UserAlreadyExistsException, UserNotFoundException {
logger.info("inside storeUser");
String email = userModel.getEmail();
UserModel checkUser = getUserModel(email);
logger.info("after getUserModel with email " + email);
if (checkUser == null) {
logger.info("inside checkUser is NULL");
DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
Transaction txn = datastoreService.beginTransaction();
try {
Entity userEntity = new Entity(UserModel.class.getSimpleName(), email);
userEntity.setProperty("nickname", userModel.getNickname());
// TODO save the pheromones with the key of userEntity
datastoreService.put(userEntity);
txn.commit();
storePheromoneList(userModel.getPheromoneList(), userEntity.getKey(), datastoreService);
} finally {
if (txn.isActive()) {
logger.severe("rolled back with email : " + email);
txn.rollback();
}
}
}
else {
throw new UserAlreadyExistsException();
}
return userModel;
}
A class that triggers calls to my backend
public class EndpointsServer implements Server {
private static final String TAG = "EndpointsServer";
final UserModelApi userEndpointsApi;
public EndpointsServer() {
UserModelApi.Builder builder = new UserModelApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
userEndpointsApi = builder.build();
}
#Override
public User getUser(String email) {
User user = null;
try {
Log.d(TAG, "in getUser with email " +email);
// get user from db
UserModel userModel = userEndpointsApi.getUserModel(email).execute();
if (userModel != null) {
Log.d(TAG, "user != null with email " + email);
user = new User(userModel);
}
} catch (IOException e) {
e.printStackTrace();
}
return user;
}
}
Storing user on successful login :
String userString = gson.toJson(user, User.class);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(USER_KEY, userString);
editor.commit();
There's more to it like another client side class to build the api call to the backend and lots of other details. I can post it if you want.
I can't speak on Java but I started with Python by looking at this repo on Github:
https://github.com/loudnate/appengine-endpoints-auth-example
This shows you an example on how to write a custom authenticator with Facebook Login. Writing your own authentication I think you should be able to find some examples. The only thing you need to do after is to use the same User entity.
And I suggest you do some reading on how OAUTH 2.0 works so you don't get too confused on the task you need to do.
Basically:
On your client side, whether web or android, get a facebook access token, sends it to your endpoint service. Exchange for a access token of your own. At the same time, create your User object in datastore and associate the access token.
Then all your subsequent request should use this access token to get access to your endpoint backend. (Do a user check on your endpoint API method.)

How can I save information for current user in Spring & Hibernate?

I have a User table and a UserInfo table which keeps the personal information of the user. What I am trying to do is adding personel information after log in. There is OneToOne relationship between tables with userId column so I defined it like this :
UserInfo class :
public class UserInfo {
//...
#OneToOne
#JoinColumn(name="user_id")
private User user;
// ...
}
User class :
public class User implements UserDetails {
// ...
#OneToOne(mappedBy="user", cascade = CascadeType.ALL, fetch=FetchType.LAZY,optional=true)
private UserInfo userInfo;
...
}
After I log in, I want to add some user information to the db for current user. But I don't know how can i save user information in controller? Should I save User object like below? I've searched about it and there is a way to get current user's information:
(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
But when I try to use it in the controller it just returns null. How can I do such operations (adding pers. info) for logged in user?
#RequestMapping(value = "/show", method = RequestMethod.POST)
public ModelAndView newUser(ModelMap model, Principal principal) {
ModelAndView result = new ModelAndView("home");
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserInfo userinfo = new UserInfo();
userinfo.setPlacesVisit(userinfo.getPlacesVisit());
user.setUserInfo(userinfo);
userService.save(user);
String message = "Userinfo was successfully added.";
result.addObject("message", message);
return result;
}
instead of saving current object update current object. First get current user object in controller after that set UserInfo then update user object in database.
#RequestMapping(value = "/show", method = RequestMethod.POST)
public ModelAndView newUser(ModelMap model, Principal principal) {
String userName=principal.getName(); // username or email using user login
User user=userService.getUserByName(userName); // retrieve current user information
UserInfo userinfo = new UserInfo(); // create userinfor object
userinfo.setPlacesVisit(userinfo.getPlacesVisit());
user.setUserInfo(userinfo); // set userinfo to user
userService.update(user); // update user
String message = "Userinfo was successfully added.";
result.addObject("message", message);
return result;
}
You can retrieve to user with the Principal parameter :
public ModelAndView newUser(ModelMap model, Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
}
SecurityContextHolder is a part of Spring Security. If you want to use this component, you have to set up Spring Security for the log in of your application to be able to retrieve, in your REST controller, the current user (javax.security.Principal)
Spring Security Documentation
You can use session to save session attributes. In spring, you can access session using
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession();
You can save your logged user in session:
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
request.getSession().setAttribute("key", user);
and if you want get currently logged user, just:
(User) user = ((HttpServletRequest) request).getSession().getAttribute("key")
This example is in JSF, but I am sure that is also possible without JSF

Categories