Showing posts with label Spring Security. Show all posts
Showing posts with label Spring Security. Show all posts

Thursday, 6 December 2018

Spring WebSocket for Specific User Without Stomp

When I started to learn how to do web sockets with Spring, I was able to get lot of examples. However, almost all of them were using STOMP protocol. STOMP is important that it gives structure to web sockets. However, I still wanted to understand how the basic web socket works before moving on to STOMP.

In particular, this post will showcase an example web socket using Spring and sending messages to targeted logged-in user(s).

Most of the code for this example has been taken from http://www.devglan.com/spring-boot/spring-websocket-integration-example-without-stomp.
We add the targeted user messaging on top of this.

We first configure the Spring web security allowing users to login to the application using form login. We use in memory authentication with sample users for demo purposes. Plus, we use the standard technique by having an endpoint to get the login user (and on 401 it redirects to login).

Run the application as a Spring boot app and go to the url: http://localhost:8080/app.html. I have configured 2 users - user1/pass1 and user2/pass2. Login using one of these and click the connect button.

Once logged in, we have a landing page where we have a button which allows us to connect to the web socket (this would be done from plain Javascript and the script is put inside app.html itself). This script is simple, uses plain Javascript to connect to web socket. And, if a message is sent by the server to the connected client, we simply receive the message and add it to the '#greetings' div which is defined on the landing.html. The message is simply appended to the this div, so, we can see all the message as it is sent by the server.

On the server side, we use the Spring @EnableWebSocket annotation and register a web socket handler for the endpoint '/' (this is not usually the case, as a practice, people would have something like, 'directws' or something to indicate this separately - like we usually use the 'rest' for rest endpoints).

The web socket handler extends the Spring TextWebSocketHandler class and overrides the afterConnectionEstablished method. We create a BroadcastService and pass on the session. The BroadcastService stores the session in a list.

The scenario we have used is where a server needs to 'push' messages to connected clients (about a new event, or about an email which has arrived and so on). So, I have added a REST endpoint to help us to simulate this. I call it PushController which has endpoints like 'push/broadcast/message' which is for broadcast and 'push/targeted/{user}/{message}' which is for specific user. So, we can deploy the server and simulate the push by making a REST call to this endpoint from say Postman or ARC. These end points in turn call the methods of broadcast service that we talked about above.

Let's go back to the broadcast service and examine the methods. We have a broadcastMessage method which just loops over all the available web socket sessions (which was created when a client connected) and calls sendMessage on those - which will in turn sends this message to call connected clients. This is quite simple and works well for broadcast.

The other method is what we are interested about. The targetedMessage method takes the message as well as the user name - to which the message is targeted. So, now how do we send the message to the specific user?

The WebSocketSession class has a method getPrincipal which returns the principal i.e the user who has logged in. This actually returns the same old java.security.Principal instance - which is really cool as it neatly ties with the Principal object that we are acustomed to in Java Enterprise Applications. So, I now loop over the available web socket sessions and if the principal's name (user name) matches with that of user name passed into the method, we just call the sendMessage on that session - which ensures that the message is pushed only to the user.

The demo is not very efficient, but, I hope it conveys the basic idea. We can expand on this to other situations - like pushing messages to users of a specific role for example.

The complete code is available in my github repo.


Saturday, 18 November 2017

Spring Security Authentication and Authorization with Custom Schema

In my earlier post, I described how to do authentication and authorization with PicketLink using a custom schema. In this post, I use the same schema to perform the same with Spring Security and Spring Data JPA. Spring Security provides everything we need and we can configure authentication and authorization supported from a database using queries. In this post, I show an example of how to achieve the same using Spring Data JPA repositories.

Most of these are covered in the post: http://www.baeldung.com/spring-security-authentication-with-a-database.
I build on top of this by tweaking it to use the same schema as used in my earlier post where the password is kept in a separate table:



A simple one, with a table to store the user info, a separate related table to store password (this is useful because, when bringing back user from DB, we don't need to bring back the password related info). A master table for role and another table to associate the roles for a user.

I write corresponding JPA entities for these tables. Importantly, I don't have a relation from AppUser to UserPassword entity (it's the other way around) - simply because, as mentioned above, I don't want to bring back password information when I load user. Same applies in case of roles too. One of the basic concepts to remember about security is that, information should not be provided unless and until it is asked for (strictly on a need-to-know basis).

So, how do we encrypt the password and store the same in DB? Spring provides excellent support for encoders. So, we declare a bean in the config file, such as:

@Bean
public PasswordEncoder passwordEncoder() {
  return new BCryptPasswordEncoder();
}

Let us now look at how to create an user with password and associate a role. We can create the entities and persist with Spring Data JPA repository:

String role="ADMIN";
AppUser appUser = new AppUser();
appUser.setLoginName(email);
...
appUserRepository.save(appUser);

UserRole userRole = new UserRole();
userRole.setAppUser(appUser);
userRole.setRole(roleMasterRepository.findByName(role));
userRoleRepository.save(userRole);

UserPassword userPassword = new UserPassword();
userPassword.setAppUser(appUser);
userPassword.setPasswordHash(passwordEncoder.encode(password));
userPasswordRepository.save(userPassword);


The above block of code can be placed within a transaction and we can call this service from wherever user creation needs to be done.

For the user authentication to work, we need to wire a authentication provider which can be  a DaoAuthenticationProvider as we are storing the info in a database. We need to pass an service to this provider which implements org.springframework.security.core.userdetails.UserDetailsService. 

We also need to set the password encoder which is same as we saw above. The UserDetailsService is an interface, so we need to override the method loadUserByUsername() which should return a org.springframework.security.core.userdetails.UserDetails instance. 

So, we create a class which implements UserDetails interface. I have named this as UserLoginDto and I pass the AppUser object and the role of the user. Using this the overridden methods can return corresponding values. For example, the method isEnabled() can simply return appUser.isActive().

Most importantly, we have to override the getAuthorities() method which needs to return the list of roles assigned for the user. Since we have retrieved the roles for the user from the DB, we can use that to build the Set and return it 
(note: I have used only only role per user in this example, we can easily expand on that).

public class UserLoginDto implements UserDetails {
  public UserLoginDto(AppUser appUser, String passwordHash, AppRole appRole) {
    this.appUser = appUser;
    ...
   }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    Set<RoleGrantedAuthority> roleSet = new HashSet<>();
    roleSet.add(new RoleGrantedAuthority("ROLE_"+ appRole.toString()));
    return roleSet;
  }

  @Override
  public boolean isEnabled() {
    return appUser.isActive();
  }

  ...
}

So, when Spring is going to authenticate, it will call the loadUserByUsername on the service. We use Spring Data JPA repositories of AppUser, UserPassword and UserRole to fetch these values from the DB. We then build the UserLoginDto object and return it from the method. 
Note that, we need to fetch the password at this place as it is needed to authenticate the user. However, the AppUser object is free of this password and we can pass this around back to the UI separately. This should be enough for authentication. 

For URL based authorization, we can set it up in the security config,such as:
.antMatchers("/userpages/**").hasAnyRole(AppRole.USER.toString(), AppRole.ADMIN.toString())
.antMatchers("/adminpages/**").hasRole(AppRole.ADMIN.toString())

The complete source code for this post is available in my github repo.