diff --git a/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/CustomAuthenticationManager.java b/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/CustomAuthenticationManager.java new file mode 100644 index 0000000000..0a7dd66b24 --- /dev/null +++ b/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/CustomAuthenticationManager.java @@ -0,0 +1,126 @@ +package com.baeldung.jhipster5.security; + +import com.baeldung.jhipster5.domain.User; +import com.baeldung.jhipster5.security.dto.LoginRequest; +import com.baeldung.jhipster5.security.dto.LoginResponse; +import com.baeldung.jhipster5.service.UserService; +import com.baeldung.jhipster5.service.dto.UserDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.Collection; +import java.util.Collections; +import java.util.stream.Collectors; + +@Component +public class CustomAuthenticationManager implements AuthenticationManager { + + private final static Logger LOG = LoggerFactory.getLogger(CustomAuthenticationManager.class); + + private final String REMOTE_LOGIN_URL = "https://example.com/login"; + + private final RestTemplate restTemplate = new RestTemplate(); + + @Autowired + private UserService userService; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + + LoginRequest loginRequest = new LoginRequest(); + loginRequest.setUsername(authentication.getPrincipal().toString()); + loginRequest.setPassword(authentication.getCredentials().toString()); + + try + { + ResponseEntity response = + restTemplate.postForEntity( + REMOTE_LOGIN_URL, + loginRequest, + LoginResponse.class); + + if(response.getStatusCode().is2xxSuccessful()) + { + // + // Need to create a new local user if this is the first time logging in; this + // is required so they can be issued JWTs. We can use this flow to also keep + // our local use entry up to date with data from the remote service if needed + // (for example, if the first and last name might change, this is where we would + // update the local user entry) + // + + User user = userService.getUserWithAuthoritiesByLogin(authentication.getPrincipal().toString()) + .orElseGet(() -> userService.createUser(createUserDTO(response.getBody(), authentication))); + return createAuthentication(authentication, user); + } + else + { + throw new BadCredentialsException("Invalid username or password"); + } + } + catch (Exception e) + { + LOG.warn("Failed to authenticate", e); + throw new AuthenticationServiceException("Failed to login", e); + } + } + + /** + * Creates a new authentication with basic roles + * @param auth Contains auth details that will be copied into the new one. + * @param user User object representing who is logging in + * @return Authentication + */ + private Authentication createAuthentication(Authentication auth, User user) { + + // + // Honor any roles the user already has set; default is just USER role + // but could be modified after account creation + // + + Collection authorities = user + .getAuthorities() + .stream() + .map(a -> new SimpleGrantedAuthority(a.getName())) + .collect(Collectors.toSet()); + + UsernamePasswordAuthenticationToken token + = new UsernamePasswordAuthenticationToken( + user.getId(), + auth.getCredentials().toString(), + authorities); + + return token; + } + + /** + * Creates a new UserDTO with basic info. + * @param loginResponse Response from peloton login API + * @param authentication Contains user login info (namely username and password) + * @return UserDTO + */ + private UserDTO createUserDTO(LoginResponse loginResponse, Authentication authentication) { + + UserDTO dto = new UserDTO(); + + dto.setActivated(true); + dto.setEmail(loginResponse.getEmail()); + dto.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + dto.setFirstName(loginResponse.getFirstName()); + dto.setLastName(loginResponse.getLastName()); + + return dto; + } +} diff --git a/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/dto/LoginRequest.java b/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/dto/LoginRequest.java new file mode 100644 index 0000000000..f45c23fa39 --- /dev/null +++ b/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/dto/LoginRequest.java @@ -0,0 +1,30 @@ +package com.baeldung.jhipster5.security.dto; + +/** + * Simple DTO representing a login request to a remote service. + */ +public class LoginRequest { + + private String username; + + private String password; + + public LoginRequest() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/dto/LoginResponse.java b/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/dto/LoginResponse.java new file mode 100644 index 0000000000..ad1fe37a2f --- /dev/null +++ b/jhipster-5/bookstore-monolith/src/main/java/com/baeldung/jhipster5/security/dto/LoginResponse.java @@ -0,0 +1,50 @@ +package com.baeldung.jhipster5.security.dto; + +/** + * Simple DTO representing the response of logging in using a remote service. + */ +public class LoginResponse { + + private String username; + + private String firstName; + + private String lastName; + + private String email; + + public LoginResponse() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/jhipster-5/bookstore-monolith/src/main/webapp/app/account/account.route.ts b/jhipster-5/bookstore-monolith/src/main/webapp/app/account/account.route.ts index f849342e69..cba5d40716 100644 --- a/jhipster-5/bookstore-monolith/src/main/webapp/app/account/account.route.ts +++ b/jhipster-5/bookstore-monolith/src/main/webapp/app/account/account.route.ts @@ -1,8 +1,8 @@ import { Routes } from '@angular/router'; -import { activateRoute, passwordRoute, passwordResetFinishRoute, passwordResetInitRoute, registerRoute, settingsRoute } from './'; +import { settingsRoute } from './'; -const ACCOUNT_ROUTES = [activateRoute, passwordRoute, passwordResetFinishRoute, passwordResetInitRoute, registerRoute, settingsRoute]; +const ACCOUNT_ROUTES = [settingsRoute]; export const accountState: Routes = [ { diff --git a/jhipster-5/bookstore-monolith/src/main/webapp/app/layouts/navbar/navbar.component.html b/jhipster-5/bookstore-monolith/src/main/webapp/app/layouts/navbar/navbar.component.html index e58d234c22..4fab5c76ac 100644 --- a/jhipster-5/bookstore-monolith/src/main/webapp/app/layouts/navbar/navbar.component.html +++ b/jhipster-5/bookstore-monolith/src/main/webapp/app/layouts/navbar/navbar.component.html @@ -114,12 +114,6 @@ Settings -
  • - - - Password - -
  • @@ -132,12 +126,6 @@ Sign in
  • -
  • - - - Register - -
  • diff --git a/jhipster-5/bookstore-monolith/src/main/webapp/app/shared/login/login.component.html b/jhipster-5/bookstore-monolith/src/main/webapp/app/shared/login/login.component.html index 60d593bd4b..7eb35364b4 100644 --- a/jhipster-5/bookstore-monolith/src/main/webapp/app/shared/login/login.component.html +++ b/jhipster-5/bookstore-monolith/src/main/webapp/app/shared/login/login.component.html @@ -30,14 +30,6 @@ -

    -
    - Did you forget your password? -
    -
    - You don't have an account yet? - Register a new account -
    diff --git a/jhipster-5/bookstore-monolith/src/test/java/com/baeldung/jhipster5/security/MockAuthenticationManager.java b/jhipster-5/bookstore-monolith/src/test/java/com/baeldung/jhipster5/security/MockAuthenticationManager.java new file mode 100644 index 0000000000..bdcdba7644 --- /dev/null +++ b/jhipster-5/bookstore-monolith/src/test/java/com/baeldung/jhipster5/security/MockAuthenticationManager.java @@ -0,0 +1,54 @@ +package com.baeldung.jhipster5.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Collections; + +/** + * AuthenticationManager used solely by unit tests. + */ +@Component +@Primary +public class MockAuthenticationManager implements AuthenticationManager +{ + private final static Collection ROLES = + Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")); + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException + { + + UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName()); + + if(userDetails == null || !passwordEncoder.matches(authentication.getCredentials().toString(), userDetails.getPassword())) + { + throw new BadCredentialsException("Invalid username/password"); + } + + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( + authentication.getPrincipal().toString(), + authentication.getCredentials().toString(), + ROLES); + + return token; + } +}