Spring Security - Build Multi-Tenant Application

The problem
We are evolving our application from single-purpose to multi-tenant application.

The solution
We use LDAP to authenticate user and define different LDAP Group for different roles in different environment for different sub-application.

In login page, user selects what sub-applications to login. The application will call LDAP to do authentication, which will return what what groups user belongs to. Then the application will check the group-mapping to decide whether user can access this application and what roles user should have.

We also store the sub-application name in the session, so it can be used later.


We store supported Applications - the application name and the mapping of application's ldap groups in database.

Check Spring Security: Integrate In-Memory Authentication for Test Automation for why we add test users in dev lines and how to do it.


Talk is cheap. Show me the code.
@Component
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
 @Autowired
 private Environment environment;
 @Autowired
 private IConfigService configService;

 @Autowired
 private ApplicationProfile applicationProfile;
  // these test users are cross all applications in dev lines
 private final Set$lt;String$gt; testUsers = new HashSet$lt;$gt;();

 @PostConstruct
 public void postConstruct() {
  if (applicationProfile.isDev()) {
   addTestUser("spring.security.test.user.adminOnly.name");
   addTestUser("spring.security.test.user.provisionerOnly.name");
   addTestUser("spring.security.test.user.adminProvisioner.name");
  }
 }

 protected void addTestUser(final String testUserProperty) {
  final String testUser = environment.getProperty(testUserProperty);
  if (StringUtils.isNotBlank(testUser)) {
   testUsers.add(testUser);
  }
 }

 @Autowired
 @Override
 public void setAuthenticationManager(final AuthenticationManager authenticationManager) {
  super.setAuthenticationManager(authenticationManager);
 }

 @Override
 public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
   throws AuthenticationException {
  final String applicationName = request.getParameter(Util.APPLICATION_NAME);

  if (StringUtils.isEmpty(applicationName)) {
   throw new AuthenticationServiceException(
     MessageFormat.format("Not supported application: {0}", applicationName));
  }

  final Map$lt;String, SupportedAppSecurityConfig$gt; supportedApps = configService.getMySimpleConfig()
    .extractSupportedApplications();
  if (!supportedApps.containsKey(applicationName)) {
   throw new AuthenticationServiceException(
     MessageFormat.format("Not supported application: {0}", applicationName));
  }

  final Authentication auth = super.attemptAuthentication(request, response);

  if (auth.isAuthenticated()) {
   request.getSession(true).setAttribute(Util.APPLICATION_NAME, applicationName);
   if (testUsers.contains(auth.getName())) {
    return auth;
   }
   return checkAuthorizationAndMappingGroup(supportedApps, applicationName, auth);
  }
  return auth;
 }

 protected Authentication checkAuthorizationAndMappingGroup(
   final Map$lt;String, SupportedAppSecurityConfig$gt; supportedApps, final String applicationName,
   final Authentication auth) {
  // mapping group
  final SupportedAppSecurityConfig application = supportedApps.get(applicationName);

  final List$lt;GrantedAuthority$gt; newAuthorities = new ArrayList$lt;$gt;();

  boolean isAdmin = false, isProvisioner = false;
  for (final GrantedAuthority authority : auth.getAuthorities()) {
   if (authority.getAuthority().equals(application.getAdminLadpGroup())) {
    isAdmin = true;
   }
   if (authority.getAuthority().equals(application.getProvisionLdapGroup())) {
    isProvisioner = true;
   }
  }

  if (!isAdmin && !isProvisioner) {
   throw new AuthenticationServiceException(MessageFormat
     .format("User {0} does not have expected authority, having: {1}", auth.getName(), newAuthorities));
  }

  if (isAdmin) {
   newAuthorities.add(new SimpleGrantedAuthority(Util.ADMIN_GROUP));
  }
  if (isProvisioner) {
   newAuthorities.add(new SimpleGrantedAuthority(Util.PROVISION_GROUP));
  }

  final Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(),
    auth.getCredentials(), newAuthorities);
  return newAuth;
 }
}

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
      @Autowired
      private MyUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter;
      @Override
      protected void configure(final HttpSecurity http) throws Exception {
          http.authorizeRequests()
          .antMatchers("/* ignored*/").permitAll()
          .antMatchers("/* ignored*/").access(Util.ROLE_PROVISIONER_OR_ADMIN)
          .antMatchers("/* ignored*/").access(Util.ROLE_ADMIN)
          .and().formLogin().loginPage("/login").failureUrl("/loginerror")
          .loginProcessingUrl("/j_spring_security_check").passwordParameter("j_password")
          .usernameParameter("j_username").defaultSuccessUrl("/index.html").and().logout()
          .logoutUrl("/j_spring_security_logout").logoutSuccessUrl("/loggedout")
          .deleteCookies("JSESSIONID", "SESSION")
          .and().sessionManagement().sessionFixation().migrateSession().maximumSessions(1)
          .and().and().addFilter(usernamePasswordAuthenticationFilter);
      }
      // check http://lifelongprogrammer.blogspot.com/2016/04/spring-security-integrate-in-memory.html
      // for implementation
      @Bean @Override
      public AuthenticationManager authenticationManagerBean() throws Exception {}
}

public class SupportedAppSecurityConfig implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private String adminLadpGroup;
    private String provisionLdapGroup;
}
Post a Comment

Labels

Java (159) Lucene-Solr (110) All (60) Interview (59) J2SE (53) Algorithm (37) Eclipse (35) Soft Skills (35) Code Example (31) Linux (26) JavaScript (23) Spring (22) Windows (22) Web Development (20) Tools (19) Nutch2 (18) Bugs (17) Debug (15) Defects (14) Text Mining (14) J2EE (13) Network (13) PowerShell (11) Chrome (9) Continuous Integration (9) How to (9) Learning code (9) Performance (9) UIMA (9) html (9) Design (8) Dynamic Languages (8) Http Client (8) Maven (8) Security (8) Trouble Shooting (8) bat (8) blogger (8) Big Data (7) Google (7) Guava (7) JSON (7) Problem Solving (7) ANT (6) Coding Skills (6) Database (6) Scala (6) Shell (6) css (6) Algorithm Series (5) Cache (5) IDE (5) Lesson Learned (5) Miscs (5) Programmer Skills (5) System Design (5) Tips (5) adsense (5) xml (5) AIX (4) Code Quality (4) GAE (4) Git (4) Good Programming Practices (4) Jackson (4) Memory Usage (4) OpenNLP (4) Project Managment (4) Python (4) Spark (4) Testing (4) ads (4) regular-expression (4) Android (3) Apache Spark (3) Become a Better You (3) Concurrency (3) Eclipse RCP (3) English (3) Firefox (3) Happy Hacking (3) IBM (3) J2SE Knowledge Series (3) JAX-RS (3) Jetty (3) Restful Web Service (3) Script (3) regex (3) seo (3) .Net (2) Android Studio (2) Apache (2) Apache Procrun (2) Architecture (2) Batch (2) Build (2) Building Scalable Web Sites (2) C# (2) C/C++ (2) CSV (2) Career (2) Cassandra (2) Distributed (2) Fiddler (2) Google Drive (2) Gson (2) Html Parser (2) Http (2) Image Tools (2) JQuery (2) Jersey (2) LDAP (2) Life (2) Logging (2) Software Issues (2) Storage (2) Text Search (2) xml parser (2) AOP (1) Application Design (1) AspectJ (1) Bit Operation (1) Chrome DevTools (1) Cloud (1) Codility (1) Data Mining (1) Data Structure (1) ExceptionUtils (1) Exif (1) Feature Request (1) FindBugs (1) Greasemonkey (1) HTML5 (1) Httpd (1) I18N (1) IBM Java Thread Dump Analyzer (1) JDK Source Code (1) JDK8 (1) JMX (1) Lazy Developer (1) Mac (1) Machine Learning (1) Mobile (1) My Plan for 2010 (1) Netbeans (1) Notes (1) Operating System (1) Perl (1) Problems (1) Product Architecture (1) Programming Life (1) Quality (1) Redhat (1) Redis (1) Review (1) RxJava (1) Solutions logs (1) Team Management (1) Thread Dump Analyzer (1) Troubleshooting (1) Visualization (1) boilerpipe (1) htm (1) ongoing (1) procrun (1) rss (1)

Popular Posts