Friday, December 31, 2021
Multi-factor Authentication with Spring Security and LDAP
Required Libraries
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.version}</version>
</dependency>
Spring Context
<!-- Activate Spring Annotation -->
<context:annotation-config />
<!-- Load property file properties -->
<context:property-placeholder location="classpath:*.properties" />
<!-- Spring Authentication Provider -->
<security:authentication-manager>
<security:authentication-provider ref="LDAPProvider" />
</security:authentication-manager>
<!-- Spring Authentication Provider bean definition -->
<beans:bean id="LDAPProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource"></beans:constructor-arg>
<beans:property name="userSearch" ref="userSearch"></beans:property>
</beans:bean>
</beans:constructor-arg>
<beans:constructor-arg>
<beans:bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<beans:constructor-arg ref="contextSource"/>
<beans:constructor-arg value="ou=groups"/>
<beans:property name="groupRoleAttribute" value="memberOf"/>
</bean>
</beans:constructor-arg>
<!-- You can write custom Auth populater here -->
</beans:bean>
<!-- Spring Authentication Provider bean implementation -->
<beans:bean id="userSearch" class="com.mycompany.service.CustomLdapUserSearch">
<beans:constructor-arg value=""></beans:constructor-arg>
<beans:constructor-arg value="(sAMAccountName={0})"></beans:constructor-arg>
<beans:constructor-arg ref="contextSource"></beans:constructor-arg>
<beans:property name="searchSubtree" value="true"></beans:property>
</beans:bean>
<!-- LDAP Spring context -->
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="${ldap.server}" />
<beans:property name="userDn" value="CN=<????>,OU=<????>,OU=<????>,DC=<???>,DC=<????>,DC=<???>" />
<beans:property name="password" value="<????>" />
</beans:bean>
<!-- Spring Security Auth failure message definitions -->
<beans:bean id="authenticationFailureHandlerException"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="exceptionMappings">
<beans:props>
<beans:prop key="org.springframework.security.authentication.BadCredentialsException">/?error=Invalid user name or password. Please re-enter.</beans:prop>
<beans:prop key="org.springframework.security.authentication.CredentialsExpiredException">/?error=Your credentials has been expired</beans:prop>
<beans:prop key="org.springframework.security.authentication.LockedException">/?error=Your account has been locked</beans:prop>
<beans:prop key="org.springframework.security.authentication.DisabledException">/?error=Your account has been disabled</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<beans:bean id="authenticationFailureHandler"
class="com.company.service.AuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/"></beans:property>
</beans:bean>
<!-- Spring Security Role definitions for each page -->
<security:http auto-config="true" use-expressions="true" access-denied-page="/" disable-url-rewriting="true">
<security:intercept-url pattern="/viewRePorts./**" access="hasAnyRole('VIEW_TRANSACTION_2FAYES','ADJUST_TRANSACTION_2FAYES')"/>
<security:intercept-url pattern="/viewRePorts" access="hasAnyRole('VIEW_TRANSACTION_2FAYES','ADJUST_TRANSACTION_2FAYES')"/>
<security:intercept-url pattern="/viewRePorts./**" access="hasAnyRole('VIEW_TRANSACTION_2FAYES','ADJUST_TRANSACTION_2FAYES')"/>
<!-- Spring Security Login Form -->
<!-- always-use-default-target="true" : This is to strictly follow the 2 factor validation flow -->
<!-- default-target-url="/otpVerification" : This is the 2 factor page just after the successful login -->
<security:form-login
always-use-default-target="true"
login-processing-url="/j_spring_security_check"
login-page="/"
default-target-url="/otpVerification"
authentication-failure-url="/?error=Error while login. Please try Again"
authentication-failure-handler-ref="authenticationFailureHandlerException"
username-parameter="j_username"
password-parameter="j_password" />
<security:logout invalidate-session="true" logout-success-url="/?error=You've been logged out successfully." />
<!-- Redirect to login page if session is invalid -->
<!-- Redirect to login page if session is expired -->
<security:session-management invalid-session-url="/">
<security:concurrency-control expired-url="/" />
</security:session-management>
</security:http>
HTML Form
<form name='f' action="<c:url value='j_spring_security_check' />" method='POST'>
<input type="text" class="form-control" autocomplete="off" type='text' name='j_username' id='j_username' placeholder="User Name">
<input type="password" class="form-control" autocomplete="off" type='password' name='j_password' id='j_password' placeholder="Password">
<button name="Submit" type="submit" value="Login" >Sign In</button>
</form>
Java Implementation ( Once 2 factor is success, then only set the roles )
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> updatedAuthorities = new ArrayList<>(auth.getAuthorities());
updatedAuthorities.add(new SimpleGrantedAuthority('VIEW_TRANSACTION_2FAYES'));
updatedAuthorities.add(new SimpleGrantedAuthority('ADJUST_TRANSACTION_2FAYES'));
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), updatedAuthorities);
SecurityContextHolder.getContext().setAuthentication(newAuth);
Subscribe to:
Posts (Atom)