Showing posts with label spring-session. Show all posts
Showing posts with label spring-session. Show all posts

Saturday, September 8, 2018

Using multiple OAuth2 clients in single browser session using Spring boot

Leave a Comment

We have Multi tenant WebApp designed using Spring Boot + Spring Security. This app is used to manage certain resources in Azure. User login into our WebApp using OAuth2.0 and can access Azure resources through our app.

Now we need to allow multiple users to login into our app in single browser session. So basically user (user 1) will use credentials1 to login to access resources allowed by these credentials. Then user will use credentials2 (basically another users credentials lets call it user2) to login into same browser page. There will be two active users in same session. User should be able to switch between these accounts.

Once user login into our app, we instantiate RestTemplate (using credentials entered) to access Azure resources.

Either we can have single JSession id mapped to multiple RestTemplate or multiple JSession ID (within single JSession cookie) to mapped to individual RestTemplate. We can have request parameter indicating which RestTemplate to use.

We have used SpringSecurity to get access token. This access token is then used in RestTemplate and used for accessing Azure resources.

1 Answers

Answers 1

"Now we need to allow multiple users to login into our app in single browser session"

Is this approach secure, at all? I mean, having two users using the same browser and sharing information isn't recommended.

"Either we can have single JSession id mapped to multiple RestTemplate or multiple JSession ID (within single JSession cookie) to mapped to individual RestTemplate"

I never saw this kind of approach. Get Google as an example -- you can switch profiles, but need to log in.

If you really need to do it, there's an out of the box solution for Chrome, Firefox and Opera called SessionBox, that enables session switch within the same browser. Otherwise, two common solutions are:

  • Use two different browsers (e.g. Chrome and Firefox)
  • Use incognito mode
Read More

Wednesday, January 24, 2018

Spring session replication with mongodb not working as expected

Leave a Comment

I am replicating session using mongodb

below is the configuration I am using

@Configuration @EnableMongoHttpSession public class MongoSessionReplication {  @Bean public AbstractMongoSessionConverter mongoSessionConverter() {     List<Module> securityModules = SecurityJackson2Modules.getModules(getClass().getClassLoader());     return new JacksonMongoSessionConverter(securityModules); } @Bean public MongoTemplate mongoTemplate(@Qualifier("replicaSet") Datastore replicaSet){    MongoTemplate mongoTemplate = new MongoTemplate(replicaSet.getMongo(),replicaSet.getDB().getName());    return mongoTemplate; }  } 

Now everything is working fine except the Principal object that spring security creates after loggin in. I have custom implementation of UserDetails

public class PortalUser extends User {      private String primaryEmailId;      private String redirectUrl;      public PortalUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {         super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);     }     public PortalUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {         super(username, password, true, true, true, true, authorities);     }      public String getPrimaryEmailId() {         return primaryEmailId;     }      public void setPrimaryEmailId(String primaryEmailId) {         this.primaryEmailId = primaryEmailId;     }      public String getRedirectUrl() {         return redirectUrl;     }      public void setRedirectUrl(String redirectUrl) {         this.redirectUrl = redirectUrl;     } } 

Below is UserDetailsService

@Service public class PortalUserDetailService implements UserDetailsService {      @Autowired     private SSOServiceAPI ssoServiceAPI;      @Autowired     private UserProfileService userProfileService;      @Override     public UserDetails loadUserByUsername(String hexId) throws UsernameNotFoundException {         UserProfile userProfile = userProfileService.getUserProfileByUserId(hexId);         List<GrantedAuthority> grantedAuthority = new ArrayList<GrantedAuthority>();         if(userProfile!=null) {             grantedAuthority.add(new SimpleGrantedAuthority(userProfile.getSsmRoles().name()));         } else {             grantedAuthority.add(new SimpleGrantedAuthority("USER"));         }         SSOUsers ssoUser = ssoServiceAPI.findSSOUser(hexId, false);         PortalUser portalUser = new PortalUser(hexId, hexId, true, true, true, true, grantedAuthority);         portalUser.setPrimaryEmailId(ssoUser.getPrimaryUserId());         return portalUser;      } } 

Controller

public String getAllProducts(@RequestParam(value = "callback", required = true) String callback, Principal principal, HttpServletRequest request) {      String hexId = principal.getName();     String primaryEmailId = ((PortalUser) ((UsernamePasswordAuthenticationToken) principal).getPrincipal()).getPrimaryEmailId(); //----->> this line fails  } 

Above highlighted typecasting failed as it returns instance of UserDetails instead of my custom PortalUser. But this isn't a case when I disable spring-session replication..

1 Answers

Answers 1

You need to extend Spring's Security UserDetails, not User. update PortUser to the below:

import org.springframework.security.core.userdetails.UserDetails;  public class PortalUser  extends UserDetails,Serializable { 
Read More

Tuesday, April 19, 2016

Spring Session Data Redis - Get Valid Sessions, Current User from Redis Store

Leave a Comment

My question is, in distributed web application is it possible to get the valid sessions from Redis Store using RedisOperationSessionRepository. (I mean I don't want to write explicit code for putting it into Redis store and then later read it, I want to understand if framework or spring-data-redis library provides that).

I am aware that Spring Redis is able to restore sessions and server restarts also preserve the login if the session is still valid (as it is backed by Redis)

One of the functionality I am looking for is to get all the possible log in users currently in the application. I am aware of SessionRegistryImpl, and this method. but what I noticed that this method is not backed by Redis and after server restarts, log in users are not returned.

 public List<Object> getAllPrincipals() {     return new ArrayList<Object>(principals.keySet()); } 

One of the functionality I can try is from Spring Session 1.1.0, Spring session find by username.

  1. http://docs.spring.io/spring-session/docs/1.1.0.M1/reference/html5/guides/findbyusername.html
  2. https://spring.io/blog/2015/11/17/spring-session-1-1-0-m1-released

I tried and it indeed returns me valid session result, but the problem is I still need to know all the current valid user names that are using this application. (I don't know how to get them using Redis Store, again I can store in Redis and get them, but I want to know if there is better approach).

This is the piece of code, this is the way I can get current user from one of the many users that are currently using the system, if I know the session id.

    final Session session = redisOperationsSessionRepository.getSession(sessionid);      final Object obj = session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);     if (obj instanceof SecurityContext) {         final SecurityContext context = (SecurityContext) obj;         final Authentication authentication = context.getAuthentication();         if (authentication != null) {             final Object principal = authentication.getPrincipal();             if (principal != null && principal instanceof CurrentUser) {                 return (CurrentUser) principal;             }         }     } 

Now I can use above logic to get all the current user, but again I should all the valid session ids, which I don't know how to get from Redis store.

New Update : https://github.com/spring-projects/spring-session/issues/255 Here in this link, I can probably get all the session ids and look for active sessions in RedisOperationSessionRepository, but might result in performance issues.

I am not sure if I made myself clear, but can't we tell something to Redis using spring session api, just give me all the valid sessions and their current user that are currently log in. (based on last accessed time or something like that).

Thank you

1 Answers

Answers 1

Redis is basically a key-value store and does not provide such querying features.

However, you should be able to list all session using a KEYS request, then filter them on a last-activity basis, but with drawbacks mentioned in the github issue you have mentioned.

You should probably consider logging users activities in a datastore that supports relational querying, keeping Redis as a fast session store.

Read More

Monday, April 4, 2016

How to get exclusive access to some session entry?

Leave a Comment

Due to remote invocation nature of REST services, they are in constant situation to run into race condition with each other. One of the everyday resources to race for is session. In order to be practical, you need to be able to put a lock over the resource at the beginning of your process and lift it up whenever you are done with it.

Now my question is, does Spring Session have any feature to deal with race condition over the session entries?

Or any other library / framework in Java!!!

2 Answers

Answers 1

If you're using Spring Controllers, then you can use

RequestMappingHandlerAdapter.setSynchronizeOnSession-boolean-

This will make every Controller method synchronized in presence of a session.

HttpSession.setAttribute is thread safe. However getAttribute followed by setAttribute has to be manually made tread safe.

synchronized(session) {     session.setAttribute("foo", "bar");     session.getAttribute("foo"); } 

Same can be done in case of spring session beans.

synchronized(session) {     //do something with the session bean } 

#Edit

In case of multiple containers with normal spring session beans you would have to use sticky sessions. That would ensure that one session state is stored on one container and that container is accessed every single time the same session is requested. This has to be done on the load balancer with the help of something like BigIP cookies. Rest would would work the same way as for a single session there exists a single container, so locking session would suffice.

If you would like to use session sharing across instances there are supports on the containers like Tomcat and Jetty

These approaches use a back-end database or some other persistence mechanism to store state.

For the same purpose you can try using Spring Session. Which is trivial to configure with the Redis. Since Redis is single threaded it ensures that one instance of a entry is accessed atomically.

Above approaches are non invasive. Both the database and Redis based approaches support transactions.

However if you want more control over the distributed state and locking you can try using the distributed data grids like Hazelcast and Gemfire.

I have personally worked with the Hazelcast and it does provide methods to lock entries made in the map.

Answers 2

As a previous answer stated. If you are using Spring Session and you are concerned for thread safety on concurrent access of a Session, you should set:

RequestMappingHandlerAdapter.setSynchronizeOnSession(true); 

One example can be found here EnableSynchronizeOnSessionPostProcessor :

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;  public class EnableSynchronizeOnSessionPostProcessor implements BeanPostProcessor {     private static final Logger logger = LoggerFactory         .getLogger(EnableSynchronizeOnSessionPostProcessor.class);      @Override     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {         // NO-OP         return bean;     }      @Override     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {         if (bean instanceof RequestMappingHandlerAdapter) {             RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;             logger.info("enable synchronizeOnSession => {}", adapter);             adapter.setSynchronizeOnSession(true);         }         return bean;     } } 

Sticky Sessions and Session Replication

With regards to a clustered application and Sessions, there is a very good post here on SO, that discusses this topic: Sticky Sessions and Session Replication

In my experience, you would want both Sticky Session and Session replication. You use sticky session to eliminate the concurrent Session access across nodes, because sticky session will pin a session to a single node and each subsequent request for the same session will always be directed to that node. This eliminates the cross-node session access concern.

Replicated sessions are helpful mainly in case a node goes down. By replicating sessions, when a node goes down, future requests for existing sessions will be directed to another node that will have a copy of the original session and makes the fail over transparent to the user.

There are many frameworks that support session replication. The one I use for large projects is the open-source Hazelcast.

In response to your comments made on @11thdimension post:

I think you are in a bit of a challenging area. Basically, you want to enforce all session operations to be atomic across nodes in a cluster. This leads me to lean towards a common session store across nodes, where access is synchronized (or something similar).

Multiple Session store / replication frameworks surely support an external store concept and I am sure Reddis does. I am most familiar with Hazelcast and will use that as an example.

Hazelcast allows to configure the session persistence to use a common database. If you look at Map Persistence section, it shows an example and a description of options.

The description for the concept states:

Hazelcast allows you to load and store the distributed map entries from/to a persistent data store such as a relational database. To do this, you can use Hazelcast's MapStore and MapLoader interfaces.

Data store needs to be a centralized system that is accessible from all Hazelcast Nodes. Persistence to local file system is not supporte

Hazelcast supports read-through, write-through, and write-behind persistence modes which are explained in below subsections.

The interesting mode is write-through:

Write-Through

MapStore can be configured to be write-through by setting the write-delay-seconds property to 0. This means the entries will be put to the data store synchronously.

In this mode, when the map.put(key,value) call returns:

MapStore.store(key,value) is successfully called so the entry is persisted. In-Memory entry is updated. In-Memory backup copies are successfully created on other JVMs (if backup-count is greater than 0). The same behavior goes for a map.remove(key) call. The only difference is that MapStore.delete(key) is called when the entry will be deleted.

I think, using this concept, plus setting up your database tables for the store properly to lock entries on insert/update/deletes, you can accomplish what you want.

Good Luck!

Read More