Secure your Angular 7 application with Spring Security and enable LDAP authentication for added protection

Having a significant issue, I've been stuck for days trying to figure out how to make my login app functional. I need it to send the username and password to Spring Boot for validation. Although I have the basic setup in place using Spring Boot's loginForm, I'm quite new to programming with Spring Boot as I was previously working with NodeJS. My aim is to authenticate users via LDAP in the backend using Spring Security instead of Pure Java (which would be simpler). This involves handling HTTP Requests (Endpoints).

Here's what I currently have:

// WebSecurityConfiguration

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .anyRequest().fullyAuthenticated()
            .and()
            .formLogin()
            .loginPage("/test").permitAll()
            .usernameParameter("_username_")
            .passwordParameter("_password_")
            .and()
            .csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
            .ldapAuthentication()
            .userDnPatterns("uid={0},ou=people")
            .groupSearchBase("ou=groups")
            .contextSource()
            .url("ldap://localhost:8389/dc=springframework,dc=org")
            .and()
            .passwordCompare()
            .passwordEncoder(new LdapShaPasswordEncoder())
            .passwordAttribute("userPassword");

}

@Bean
public DefaultSpringSecurityContextSource contextSource() {
    return new DefaultSpringSecurityContextSource(Arrays.asList("ldap://localhost:8389/"), "dc=springframework,cd=org");
}

I opted to use Gradle due to company standards, so here's the build.gradle:

// build.gradle

    plugins {
    id 'org.springframework.boot' version '2.1.2.RELEASE'
    id 'java'
}

apply plugin: 'io.spring.dependency-management'


group = 'net.company'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-ldap'
    // other dependencies...
}

Below is my current app.component.ts where I imported HttpClientModule and added it to imports:

// app.component.ts

import {Component} from '@angular/core';
import {HttpClient} from '@angular/common/http';

// rest of the code...

View Login App Look

// login.component.html

    <div id="container"
     fxLayout="column"
     fxLayoutAlign="start center">
  <div class="spacer" fxFlex="10%"></div>
  <div id="login" fxFlex="25%"
       fxLayout="column"
       fxLayoutAlign="start center">
    <h1 id="loginTitle" fxFlex="35%">LOGIN</h1>
    // form structure...
  </div>
  // more template code...
</div>

For those requiring information on LDAP configurations, refer to application.properties:

// application.properties

spring.ldap.embedded.ldif=classpath:test-server.ldif
spring.ldap.embedded.base-dn=dc=springframework,dc=org
spring.ldap.embedded.port=8389
server.servlet.context-path=/
server.port=8082

If more details or files are needed, feel free to reach out. I'll update this post once a solution is found for better understanding by beginners.

Answer №1

To solve your issue, consider implementing a custom filter. I suggest reviewing the Spring Security Documentation, specifically point 9.

For HTTP and REST operations, you'll need a JWT library to manage tokens between Angular and your Spring backend.

You can customize the

UsernamePasswordAuthenticationFilter
by implementing the attemptAuthentication and successfulAuthentication methods.

Firstly, verify if the submitted username and password are correct:

 try {
      LoginDto loginDto = new ObjectMapper().readValue(request.getInputStream(), LoginDto.class);
      if (getAuthenticationManager() == null) logger.warn("Authentication manager is null!");
      return getAuthenticationManager()
          .authenticate(
              new UsernamePasswordAuthenticationToken(
                  loginDto.getEmail(), loginDto.getPassword(), new ArrayList<>()));
    } catch (IOException e) {
      logger.warn(e.getMessage());
    }
    throw new AuthenticationServiceException("Credentials are invalid");

Next, generate a token for your Angular client for future authentication with your backend:

 String userName = authResult.getName();
    try {
      User user = userService.findUserByUserName(userName);

      // Add logic for handling user account lockout

      List<String> authorities = new ArrayList<>();
      user.getAuthorities().forEach(e -> authorities.add(e.getAuthority()));
      Map<String, Object> claims = new LinkedHashMap<>();

      // Include user details in the token

      String token =
          Jwts.builder()
              .setSubject(user.getId())
              .addClaims(claims)
              .setExpiration(
                  new Date(System.currentTimeMillis() + Long.parseLong(jwtConfig.getExpiration())))
              .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
              .compact();
      response.setHeader("Access-Control-Expose-Headers", "Authorization");
      response.setHeader("Authorization", "Bearer " + token);

      userService.successfulLogin(user);
      userService.resetLoginAttempts(user);

    } catch (NotFoundException e) {
      logger.warn(e.getMessage());
    }
  }

Don't forget to add this filter to your http configuration:

        Collections.singletonList(daoAuthenticationProvider()));
    AuthenticationFilter filter =  new AuthenticationFilter(userService,manager, mapper, jwtConfig);
    filter.setFilterProcessesUrl("/login");

    http.authorizeRequests().antMatchers("/**").permitAll()
        .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().addFilter(filter);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Attempting to utilize FirefoxProfile via GeckoDriver with Selenium on Mac OS X resulted in an unexpected error message stating "unknown error" with further details indicating that the connection was refused

I encountered a connection refused error while attempting to set up a Firefox driver. System.setProperty("webdriver.gecko.driver", "path to gecko driver"); FirefoxOptions options = new FirefoxOptions(); options.setLogLevel(FirefoxDriverLogLevel.FATAL); op ...

End the Angular RxJS stopwatch after a specified number of seconds

I am currently enhancing my scoreboard timer component by incorporating a stopwatch feature. The current setup involves modifying the countdown clock that was previously implemented. The stopwatch is designed to run continuously, but I would like to impl ...

Tips for effectively passing an array to props in Vue when leveraging Typescript and the class component decorator

I'm currently struggling to understand the proper method of passing an array as a prop to a component in Vue, using Typescript and the class component library. Following the official template, I attempted the following approach: <script lang="ts"& ...

Encountering an issue while creating an Ionic v6 Cordova Android Angular13 application, the error message "Project target does not exist" is displayed, indicating an unhandled exception

I am currently in the process of developing an ionic v6 app using the latest versions of cordova and angular13. However, I have encountered an error when running ionic cordova build android https://i.sstatic.net/i8QFO.png For more information on reproduc ...

What is the best way to create a write function that is not located in the main function?

Can you help me with a coding issue I am facing? Here is the code snippet: import java.io.*; import java.util.ArrayList; import java.util.Scanner; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.io.IOException ...

Guide to utilizing props conditionally in a Material UI TextField component

I am looking to build a comprehensive component using Material UI TextField along with Formik While I have managed all other props, I am facing difficulty in dealing with value and onChange, I have tried using 2 functions for onChange, but find it cumber ...

Angular 9 date input and selection

I am currently troubleshooting why the attribute value on an input type date is not working with Angular 9. <input type="date" [max]="dateConfig.max" [min]="dateConfig.min" name="date" [value]="dateConfig.val ...

Exploring Java Swing GUI Elements: A Guide to Marathon OSS Inspection

Currently, I am delving into the Marathon Open Source Java Driver to automate my Java Swing GUI application. I have included the necessary maven dependency in my pom.xml file and obtained the marathon-5.4.0.0 zip archive. After extracting the contents of t ...

Can a custom directive be set up for two-way binding with ngModel in Angular?

My situation is quite unique. When I eliminate all other variables, it comes down to this. Imagine I have the following input field <input type="text" [customDirective] [(ngModel)]="myValue" > The purpose of the customDirective is to analyze the u ...

Centralizing Tab Components with Angular 4's Material 2

I'm struggling to figure out how to center the Angular Material Tab Component. Here is a link to where I am looking for help: https://material.angular.io/components/tabs/overview I believe there must be a way to do it within the documentation, but i ...

What methods can be used to test included content in Angular?

When testing an Angular component that includes transclusion slots utilizing <ng-content>, it becomes challenging to verify if the transcluded content is correctly placed within the component. For instance: // base-button.component.ts @Component({ ...

Replace Angular Material Component with a new custom component in Angular

In order to customize the Angular Material select component (https://material.angular.io/components/select/overview) to have an input field transformed into an icon button, I attempted to override its styling. While this approach worked well for me, I came ...

Ways to display or conceal a component based on the current component being used

I am a newcomer in Angular, and I believe finding the solution to my problem will be a great learning experience. In my default component, the MainComponent is loaded in my <router-outlet>. At this stage, both the menu and the footer are displayed. H ...

TypeScript: Unable to retrieve values or keys from a Map data structure

Issue arises when attempting to access map keys or values using a method, resulting in the following error: ERROR TypeError: factors.values is not a function or its return value is not iterable These model interfaces are used for object types: export ...

Discovering the Absent Elements within a specified sequence array list using java

1. I have an Integer array list with a specified starting element and limit, for example [5,6,9,10]. 2. My task is to iterate through the list and identify any missing elements along with their positions. For instance, based on the given example, the outp ...

Transforming "datetime-local" into java.sql.Timestamp

I am working on a JSP page that has a form containing an input field with type "datetime-local". The data from this form is then passed to a servlet using the following code: String resetTimeString = request.getParameter(RequestParameterName.RESET_TIME); ...

Error in Typescript regarding inheritance and the usage of static methods in classes

I encountered an issue with TypeScript while compiling the code snippet below: class A { public static then() { return this; } } class B extends A { public static shouldWorks() { return 42; } } console.log(B.then().shouldWorks()); When I comp ...

Issues encountered with Navbar Links functionality while running on localhost in .jsp files

After adding the Bootstrap navigation bar to my other pages, I am unable to navigate to different links once I click on any link. I have included a screenshot of the file structure below: View Folder Structure You can find my navbar.jsp code in this link ...

RecyclerView appears to be failing to display its contents

Working with a RecyclerView in my app, I populate the items using JSON response. public class UserProfile extends AppCompatActivity { private UserData userData; private ProfileNewsFeedAdapter adapter; private ArrayList<ProgramModel> list; private R ...

Encountering the error "TS(2604): JSX element type 'App' does not have any construct or call signatures" while trying to export an array of JSX Elements

I have a function that returns an array of JSX Elements. When I pass this to ReactDOM.render, I encounter the error mentioned above. wrappers.tsx const FooterWithStore:React.FC = () => ( <Provider store={store}> <FooterLangWrapper ...