I have multi-tenant environment, one database with separate schema for each tenant.
On user login I load default tenant and everything is working great. I create method switchTenant that should switch current tenant (default one) with selected one. I have partialy working solution, where I save that information in the HttpSession. Everything is working, with some side effects, like: on tomcat restart users need to relog, or android users on application shutdown lose their session.
Was trying to find better solution to replace HttpSession with storing current tenant information in SecurityContext. And of course I failed, thats why I ask you ppl for help.
Here is a relevant code that I`m trying to make to work:
TenantInterceptor (everything is fine here)
...
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
String schema = SecurityUtils.getCurrentSchema().orElse("");
if (schema.equals("")) {
TenantContext.setCurrentTenant("public");
} else {
TenantContext.setCurrentTenant(schema);
}
return true;
}
...
SecurityUtils helper class (everything is fine here also)
...
public static Optional<String> getCurrentSchema() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication()).map(authentication -> {
if (authentication.getPrincipal() instanceof TenantUser) {
TenantUser springSecurityUser = (TenantUser) authentication.getPrincipal();
return springSecurityUser.getCurrentSchema();
}
return null;
});
}
...
Service class (that I need fix for)
...
public void switchTenant(Tenant tenant) {
Optional<TenantDTO> tenantExist = this.findAllUserTenants().stream()
.filter(t -> t.getName().equals(tenant.getName())).findFirst();
if (tenantExist.isPresent()) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Authentication newAuth = null;
if (auth.getClass() == UsernamePasswordAuthenticationToken.class) {
TenantUser principal = (TenantUser) auth.getPrincipal();
principal.setCurrentSchema(tenant.getSchema());
newAuth = new UsernamePasswordAuthenticationToken(principal, auth.getCredentials(), auth.getAuthorities());
}
SecurityContextHolder.getContext().setAuthentication(newAuth);
...
TenantUser class
public class TenantUser extends org.springframework.security.core.userdetails.User {
private String userId;
private String currentSchema;
public TenantUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
...
No matter what, this line of code: SecurityContextHolder.getContext().setAuthentication(newAuth); always get me old authentication not the one with updated current tenant schema
Thanks for help.
Approach was bad. Because I use jwt token for authentication, best way is to store current tenant schema into token. On tenant switch, I did refresh token with new current schema in it and sent it back to client.