DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Angular JWT Autorefresh With Spring Boot
  • Spring OAuth Server: Token Claim Customization
  • How to Use JWT Securely
  • Authentication With Remote LDAP Server in Spring Web MVC

Trending

  • Automating Data Pipelines: Generating PySpark and SQL Jobs With LLMs in Cloudera
  • The Role of Functional Programming in Modern Software Development
  • Teradata Performance and Skew Prevention Tips
  • A Guide to Container Runtimes
  1. DZone
  2. Coding
  3. Frameworks
  4. Using Angular and Reactive Spring With JWT Tokens

Using Angular and Reactive Spring With JWT Tokens

Want to learn more about using Angular and the reactive Spring Boot framework? Check out this tutorial on how to use Angular and Spring with JWT tokens.

By 
Sven Loesekann user avatar
Sven Loesekann
·
Jul. 26, 18 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
14.0K Views

Join the DZone community and get the full member experience.

Join For Free

The project AngularAndSpring provides an example of using Angular and reactive Spring secured with JWT tokens. The AngularAndSpring project can use the embedded Mongo DB for easy, local testing. For the topic of reactive programming in Angular and Spring, here is an article for more details.

What Do Angular and Spring Do?

This is an application that shows the quotes of cryptocurrencies with four types of exchanges. The currencies have detailed pages with daily/weekly/monthly charts and a report with the data on the chart. After the login the orderbook page can be used, the orderbooks can be seen. The orderbook data is requested from the exchanges, and, because of that, the page needs to be secured.

JWT Tokens

JWT Tokens are used because they can secure the access to REST interfaces without a session between the browser and the server. The token is base64-encoded and signed by the server. The browser logs in with a username and password and gets a token as a response. The token is then stored in the browser and, then, is sent in the HTTP header in all secured requests to the server. The server can check the signature and the expiration and, then, provide access to the REST API, making the API stateless and secure.

Configuration

The security configuration of Spring Boot is in the WebSecurityConfig:

@Configuration
@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private MyAuthenticationProvider authProvider;

  @Autowired
  private JwtTokenProvider jwtTokenProvider;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
     http.httpBasic();
     http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
     http.authorizeRequests().anyRequest().permitAll().anyRequest().anonymous();
     http.antMatcher("/**/orderbook").authorizeRequests().anyRequest().authenticated();
     http.csrf().disable();
     http.apply(new JwtTokenFilterConfigurer(jwtTokenProvider));
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authProvider);
  }

}


Line 14 sets the session creation policy to stateless for the JWT Tokens.

Line 15 sets the default permissions to anonymous to provide access without tokens.

Line 16 sets the permissions for the orderbook REST API to be authenticated to use the tokens.

Line 18 sets the JwtTokenFilterConfigurer with the JwtTokenProvider.

JWT Token Providers

The JwtTokenProviders class creates and processes the JWT Tokens:

@Component
public class JwtTokenProvider {

  @Value("${security.jwt.token.secret-key}")
  private String secretKey;

  @Value("${security.jwt.token.expire-length}")
  private long validityInMilliseconds; // 24h

  @Autowired
  private ReactiveMongoOperations operations;

  public String createToken(String username, List<Role> roles) {
    Claims claims = Jwts.claims().setSubject(username);
    claims.put("auth", roles.stream().map(s -> new SimpleGrantedAuthority(s.getAuthority()))
    .filter(Objects::nonNull).collect(Collectors.toList()));

    Date now = new Date();
    Date validity = new Date(now.getTime() + validityInMilliseconds);
    String encodedSecretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());

    return Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(validity)
    .signWith(SignatureAlgorithm.HS256, encodedSecretKey).compact();
  }

  public Optional<Jws<Claims>> getClaims(Optional<String> token) {
    if (!token.isPresent()) {
      return Optional.empty();
    }
    String encodedSecretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    return Optional.of(Jwts.parser().setSigningKey(encodedSecretKey).parseClaimsJws(token.get()));
  }

  public Authentication getAuthentication(String token) {
    Query query = new Query();
    query.addCriteria(Criteria.where("userId").is(getUsername(token)));
    MyUser user = operations.findOne(query, MyUser.class).block();

    return new UsernamePasswordAuthenticationToken(user, "", user.getAuthorities());
  }

  public String getUsername(String token) {
    String encodedSecretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    return Jwts.parser().setSigningKey(encodedSecretKey).parseClaimsJws(token).getBody().getSubject();
  }

  public String resolveToken(HttpServletRequest req) {
    String bearerToken = req.getHeader("Authorization");
    if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
      return bearerToken.substring(7, bearerToken.length());
    }
    return null;
  }

  public boolean validateToken(String token) {
    String encodedSecretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    try {
      Jwts.parser().setSigningKey(encodedSecretKey).parseClaimsJws(token);
      return true;
    } catch (JwtException | IllegalArgumentException e) {
      throw new RuntimeException("Expired or invalid JWT token");
    }
  } 

}


Lines 13-24 create a JWT Token for the username and roles provided. The claims of the token are the roles of Spring Boot. The expiration is set by the validityInMilliseconds that is set in the application.properties. The key to signing the JWT Token is set by the secretKey that is also set in the application.properties and needs to be base64-encoded.

Lines 26-32 encode the secretKeyand read the claims of the token.

Lines 34-40 create a UsernamePasswordToken based on the values of the user in the Mongo DB for the authentication in the filter chain.

Lines  42-45 read the username out of the JWT Token.

Lines 47-53 read the JWT Token out of the HTTP header and returns the token string.

Lines 55-63 validate the token with the encoded secretKey. That makes sure that the token has not been tampered with.

Login Process

The login is done with the MyUserController that provides the login API:

@PostMapping("/login")
public Mono<MyUser> postUserLogin(@RequestBody MyUser myUser,HttpServletRequest request)
  throws NoSuchAlgorithmException, InvalidKeySpecException {
  Query query = new Query();
  query.addCriteria(Criteria.where("userId").is(myUser.getUserId()));
  return this.operations.findOne(query, MyUser.class).switchIfEmpty(Mono.just(new MyUser()))
  .map(user1 -> loginHelp(user1, myUser.getPassword()));
}

private MyUser loginHelp(MyUser user, String passwd) {
  if (user.getUserId() != null) {
    String encryptedPassword;
    try {
      encryptedPassword = this.passwordEncryption.getEncryptedPassword(passwd, user.getSalt());
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
      return new MyUser();
    }
    if (user.getPassword().equals(encryptedPassword)) {
      String jwtToken = this.jwtTokenProvider.createToken(user.getUserId(), Arrays.asList(Role.USERS));
      user.setToken(jwtToken);
      user.setPassword("XXX");
      return user;
    }
  }
  return new MyUser();
}


In Lines 1-8, the API call for the login is provided. The user has to post a MyUser that has userId, salt, password. Then, the user object for the userId is retrieved from the Mongo DB and mapped to the new  MyUser object.

In Lines 10-26, the password that the user provided is encrypted and checked against the password in the DB. If the password is ok the Jwt token is created and added to the MyUser object for the user and the password is removed. Then the user gets the MyUser object with the Jwt Token back.

Angular Login Service

The login service myuser.service takes care of the Angular side of the login:

@Injectable()
export class MyuserService {
  private _reqOptionsArgs= { headers: new HttpHeaders().set( 'Content-Type', 'application/json' ) };
  private _utils = new Utils();
  private myUserUrl = "/myuser";

  constructor(private http: HttpClient, private pl: PlatformLocation ) { 
  }

  postLogin(user: MyUser): Observable<MyUser> {
      return this.http.post<MyUser>(this.myUserUrl+'/login', user, this._reqOptionsArgs).pipe(map(res => {
          let retval = <MyUser>res;
          localStorage.setItem("salt", retval.salt);  
          localStorage.setItem("token", retval.token);
          return retval;
      }),catchError(this._utils.handleError<MyUser>('postLogin')));      
  }


In Lines 10-16, the user object with the username and password is sent to the server and the response is processed. The salt and the token of the response is put in LocalStorage to be used for the orderbook requests.

Angular Orderbook Request

The request for one of the orderbooks is done in bitfinex.service:

 getOrderbook(currencypair: string): Observable<OrderbookBf> {
      let reqOptions = {headers: this._utils.createTokenHeader()};
      return this.http.get<OrderbookBf>(this._bitfinex+'/'+currencypair+'/orderbook/', reqOptions).pipe(catchError(this._utils.handleError<OrderbookBf>('getOrderbook')));
  }


Here, a normal get request is sent to the server with a custom header in thereqOptions.

The following token is added in the utils:

export class Utils {

    get token():string {
        return !localStorage.getItem("token") ? null : localStorage.getItem("token");
    }

    public createTokenHeader(): HttpHeaders {
        let reqOptions = new HttpHeaders().set( 'Content-Type', 'application/json' )
        if(this.token) {
            reqOptions = new HttpHeaders().set( 'Content-Type', 'application/json' ).set('Authorization', 'Bearer ' + this.token);
        }
        return reqOptions;
    }


In Lines 3-5, a getter is created that provides the token out of the LocalStorage or null.

In Lines 7-13, the HTTP header is built. If the token exists in LocalStorage,the authorization with the bearer and token are added.

Summary

The project AngularAndSpring shows that Angular, Spring Boot, Spring Security and JWT Tokens can work together. The backend is stateless and makes it horizontally scalable. The anonymous part of the application uses the reactive features of Spring Boot 2. The frontend uses Angular with Material and has charts, animations, and supports multiple languages. It can be started with the in-memory Mongo DB and now will be tested standalone.

Spring Framework JWT (JSON Web Token) AngularJS

Opinions expressed by DZone contributors are their own.

Related

  • Angular JWT Autorefresh With Spring Boot
  • Spring OAuth Server: Token Claim Customization
  • How to Use JWT Securely
  • Authentication With Remote LDAP Server in Spring Web MVC

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!