From f50b0d204e0ea8c99f02ed90517e0773510c9f0a Mon Sep 17 00:00:00 2001 From: "fabio.formosa" Date: Sun, 26 Apr 2020 21:26:52 +0200 Subject: [PATCH] #6 refactoring completed --- quartz-manager-api/pom.xml | 33 +++--- .../configuration/WebSecurityConfigJWT.java | 107 ++++++++++++++---- ...{LoginConfig.java => LoginConfigurer.java} | 6 +- .../helpers/impl/FormLoginConfig.java | 34 +++--- .../impl/QuartzManagerHttpSecurity.java | 41 ++++--- .../UsernamePasswordFiterLoginConfig.java | 33 +++--- .../controllers/AuthenticationController.java | 2 +- .../security/JwtTokenHelper.java | 75 ++++++------ .../auth/AuthenticationFailureHandler.java | 6 +- .../auth/AuthenticationSuccessHandler.java | 20 ++-- .../auth/JwtAuthenticationSuccessHandler.java | 9 +- .../JwtAuthenticationSuccessHandlerImpl.java | 25 ++-- .../auth/JwtTokenAuthenticationFilter.java | 93 +++++++++++++++ .../auth/JwtTokenBasedAuthentication.java | 42 +++++++ .../security/auth/LogoutSuccess.java | 12 +- .../auth/TokenAuthenticationFilter.java | 87 -------------- .../auth/TokenBasedAuthentication.java | 42 ------- .../src/main/resources/application.yml | 2 +- .../src/app/services/api.service.ts | 9 ++ .../src/app/services/auth.service.ts | 30 ++++- .../src/app/views/login/login.component.ts | 1 - 21 files changed, 427 insertions(+), 282 deletions(-) rename quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/{LoginConfig.java => LoginConfigurer.java} (51%) create mode 100644 quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenAuthenticationFilter.java create mode 100644 quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenBasedAuthentication.java delete mode 100644 quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenAuthenticationFilter.java delete mode 100644 quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenBasedAuthentication.java diff --git a/quartz-manager-api/pom.xml b/quartz-manager-api/pom.xml index dbce52a..caccc3c 100644 --- a/quartz-manager-api/pom.xml +++ b/quartz-manager-api/pom.xml @@ -26,6 +26,7 @@ + org.springframework.boot spring-boot-starter-web @@ -58,6 +59,11 @@ org.springframework spring-tx + + org.springframework.boot + spring-boot-configuration-processor + true + org.springframework.boot spring-boot-starter-tomcat @@ -69,6 +75,7 @@ test + io.jsonwebtoken jjwt @@ -91,23 +98,31 @@ h2 runtime - org.codehaus.groovy groovy - net.sourceforge.nekohtml nekohtml - io.rest-assured spring-mock-mvc test + + org.projectlombok + lombok + provided + + + org.apache.commons + commons-lang3 + 3.10 + + org.quartz-scheduler quartz @@ -158,18 +173,6 @@ ${springfox.version} - - org.projectlombok - lombok - provided - - - - - org.springframework.boot - spring-boot-configuration-processor - true - diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java index 1c4c0a6..da42dea 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/WebSecurityConfigJWT.java @@ -1,6 +1,6 @@ package it.fabioformosa.quartzmanager.configuration; -import it.fabioformosa.quartzmanager.configuration.helpers.impl.QuartzManagerHttpSecurity; +import org.apache.commons.lang3.BooleanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -21,15 +21,25 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.HttpStatusEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfig; +import com.fasterxml.jackson.databind.ObjectMapper; + +import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfigurer; +import it.fabioformosa.quartzmanager.configuration.helpers.impl.FormLoginConfig; +import it.fabioformosa.quartzmanager.configuration.helpers.impl.QuartzManagerHttpSecurity; +import it.fabioformosa.quartzmanager.configuration.helpers.impl.UsernamePasswordFiterLoginConfig; import it.fabioformosa.quartzmanager.configuration.properties.InMemoryAccountProperties; +import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties; +import it.fabioformosa.quartzmanager.security.JwtTokenHelper; +import it.fabioformosa.quartzmanager.security.auth.AuthenticationFailureHandler; +import it.fabioformosa.quartzmanager.security.auth.AuthenticationSuccessHandler; +import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationSuccessHandler; +import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationSuccessHandlerImpl; +import it.fabioformosa.quartzmanager.security.auth.JwtTokenAuthenticationFilter; import it.fabioformosa.quartzmanager.security.auth.LogoutSuccess; -import it.fabioformosa.quartzmanager.security.auth.TokenAuthenticationFilter; /** * @@ -43,18 +53,32 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v2/api-docs", "/swagger-resources/**", "/webjars/**"}; - @Value("${quartz-manager.security.jwt.cookie-strategy.cookie}") - private String TOKEN_COOKIE; + private static final String LOGIN_PATH = "/api/login"; + private static final String LOGOUT_PATH = "/api/logout"; + + @Value("${server.servlet.context-path}") + private String contextPath; + + @Value("${app.name}") + private String APP_NAME; + + @Value("${quartz-manager.security.login-model.form-login-enabled}") + private Boolean formLoginEnabled; + @Value("${quartz-manager.security.login-model.userpwd-filter-enabled}") + private Boolean userpwdFilterEnabled; + + @Autowired + private JwtSecurityProperties jwtSecurityProps; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private UserDetailsService userDetailsService; // @Autowired // private CustomUserDetailsService jwtUserDetailsService; - @Autowired - private LogoutSuccess logoutSuccess; - - @Autowired - private LoginConfig loginConfig; - @Autowired private InMemoryAccountProperties inMemoryAccountProps; @@ -82,8 +106,7 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { .addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) // .authorizeRequests().anyRequest().authenticated(); - QuartzManagerHttpSecurity.from(http).login(authenticationManager()).logout().logoutRequestMatcher(new AntPathRequestMatcher("/api/logout")) - .logoutSuccessHandler(logoutSuccess).deleteCookies(TOKEN_COOKIE); + QuartzManagerHttpSecurity.from(http).loginConfig(loginConfigurer(), logoutConfigurer()).login(LOGIN_PATH, authenticationManager()).logout(LOGOUT_PATH); } @Override @@ -113,8 +136,49 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { } @Bean - public TokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception { - return new TokenAuthenticationFilter(); + public LoginConfigurer formLoginConfigurer() { + JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler(); + AuthenticationSuccessHandler authenticationSuccessHandler = new AuthenticationSuccessHandler(jwtAuthenticationSuccessHandler); + AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationFailureHandler(); + LoginConfigurer loginConfigurer = new FormLoginConfig(authenticationSuccessHandler, authenticationFailureHandler); + return loginConfigurer; + } + + @Bean + public JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler() { + JwtTokenHelper jwtTokenHelper = jwtTokenHelper(); + JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = new JwtAuthenticationSuccessHandlerImpl(contextPath, jwtSecurityProps, jwtTokenHelper, objectMapper); + return jwtAuthenticationSuccessHandler; + } + + @Bean + public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception { + return new JwtTokenAuthenticationFilter(jwtTokenHelper(), userDetailsService); + } + + @Bean + public JwtTokenHelper jwtTokenHelper() { + JwtTokenHelper jwtTokenHelper = new JwtTokenHelper(APP_NAME, jwtSecurityProps); + return jwtTokenHelper; + } + + @Bean + public LoginConfigurer loginConfigurer() { + if(BooleanUtils.isTrue(userpwdFilterEnabled)) + return userpwdFilterLoginConfigurer(); + if(BooleanUtils.isNotFalse(formLoginEnabled)) + return formLoginConfigurer(); + throw new RuntimeException("No login configurer enabled!"); + } + + @Bean + public LogoutSuccess logoutConfigurer() { + return new LogoutSuccess(objectMapper); + } + + @Bean + public AuthenticationEntryPoint restAuthEntryPoint() { + return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED); } // @Bean @@ -122,15 +186,16 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { // return new BCryptPasswordEncoder(); // } - @Bean - public AuthenticationEntryPoint restAuthEntryPoint() { - return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED); - } - @Bean @Override public UserDetailsService userDetailsServiceBean() throws Exception { return super.userDetailsServiceBean(); } + @Bean + public LoginConfigurer userpwdFilterLoginConfigurer() { + LoginConfigurer loginConfigurer = new UsernamePasswordFiterLoginConfig(jwtAuthenticationSuccessHandler()); + return loginConfigurer; + } + } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/LoginConfig.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/LoginConfigurer.java similarity index 51% rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/LoginConfig.java rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/LoginConfigurer.java index 0692c50..9b9fa53 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/LoginConfig.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/LoginConfigurer.java @@ -3,8 +3,10 @@ package it.fabioformosa.quartzmanager.configuration.helpers; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -public interface LoginConfig { +public interface LoginConfigurer { - HttpSecurity login(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception; + String cookieMustBeDeletedAtLogout(); + + HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception; } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/FormLoginConfig.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/FormLoginConfig.java index 3e70044..72516cf 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/FormLoginConfig.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/FormLoginConfig.java @@ -1,31 +1,37 @@ package it.fabioformosa.quartzmanager.configuration.helpers.impl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.stereotype.Component; -import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfig; +import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfigurer; import it.fabioformosa.quartzmanager.security.auth.AuthenticationFailureHandler; import it.fabioformosa.quartzmanager.security.auth.AuthenticationSuccessHandler; -@Component -@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true) -public class FormLoginConfig implements LoginConfig { +//@Component +//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true) +public class FormLoginConfig implements LoginConfigurer { - private static final String API_LOGIN = "/api/login"; + private final AuthenticationSuccessHandler authenticationSuccessHandler; - @Autowired - private AuthenticationSuccessHandler authenticationSuccessHandler; + private final AuthenticationFailureHandler authenticationFailureHandler; - @Autowired - private AuthenticationFailureHandler authenticationFailureHandler; + // @Autowired + public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler, + AuthenticationFailureHandler authenticationFailureHandler) { + super(); + this.authenticationSuccessHandler = authenticationSuccessHandler; + this.authenticationFailureHandler = authenticationFailureHandler; + } @Override - public HttpSecurity login( + public String cookieMustBeDeletedAtLogout() { + return authenticationSuccessHandler.cookieMustBeDeletedAtLogout(); + } + + @Override + public HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { - return http.formLogin().loginPage(API_LOGIN).successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and(); + return http.formLogin().loginPage(loginPath).successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and(); } } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/QuartzManagerHttpSecurity.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/QuartzManagerHttpSecurity.java index 065868d..e52c7ad 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/QuartzManagerHttpSecurity.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/QuartzManagerHttpSecurity.java @@ -1,39 +1,54 @@ package it.fabioformosa.quartzmanager.configuration.helpers.impl; -import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfig; -import org.springframework.context.ApplicationContext; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer; import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfigurer; +import it.fabioformosa.quartzmanager.security.auth.LogoutSuccess; public class QuartzManagerHttpSecurity extends SecurityConfigurerAdapter { - private ApplicationContext applicationContext; - private HttpSecurity httpSecurity; - - private final LoginConfig loginConfig; - public static QuartzManagerHttpSecurity from(HttpSecurity httpSecurity){ QuartzManagerHttpSecurity newInstance = new QuartzManagerHttpSecurity(httpSecurity); newInstance.setBuilder(httpSecurity); return newInstance; } + private HttpSecurity httpSecurity; + + private LoginConfigurer loginConfiger; + + private LogoutSuccess logoutSuccess; + public QuartzManagerHttpSecurity(HttpSecurity httpSecurity) { this.httpSecurity = httpSecurity; - this.applicationContext = httpSecurity.getSharedObject(ApplicationContext.class); - this.loginConfig = this.applicationContext.getBean(LoginConfig.class); + // applicationContext = httpSecurity.getSharedObject(ApplicationContext.class); + // loginConfiger = applicationContext.getBean(LoginConfigurer.class); + // logoutSuccess = applicationContext.getBean(LogoutSuccess.class); } - public QuartzManagerHttpSecurity login(AuthenticationManager authenticationManager) throws Exception { - httpSecurity = loginConfig.login(httpSecurity, authenticationManager); + public QuartzManagerHttpSecurity login(String loginPath, AuthenticationManager authenticationManager) throws Exception { + httpSecurity = loginConfiger.login(loginPath, httpSecurity, authenticationManager); return this; } - public LogoutConfigurer logout() throws Exception { - return httpSecurity.logout(); + public QuartzManagerHttpSecurity loginConfig(LoginConfigurer loginConfigurer, LogoutSuccess logoutSuccess) { + loginConfiger = loginConfigurer; + this.logoutSuccess = logoutSuccess; + return this; + } + + public LogoutConfigurer logout(String logoutPath) throws Exception { + LogoutConfigurer logoutConfigurer = httpSecurity.logout().logoutRequestMatcher(new AntPathRequestMatcher(logoutPath)) + .logoutSuccessHandler(logoutSuccess); + String cookie = loginConfiger.cookieMustBeDeletedAtLogout(); + if(cookie != null) + logoutConfigurer.deleteCookies(cookie); + return logoutConfigurer; } } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/UsernamePasswordFiterLoginConfig.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/UsernamePasswordFiterLoginConfig.java index 8305e42..f938822 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/UsernamePasswordFiterLoginConfig.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/UsernamePasswordFiterLoginConfig.java @@ -1,37 +1,42 @@ package it.fabioformosa.quartzmanager.configuration.helpers.impl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.security.web.util.matcher.RegexRequestMatcher; -import org.springframework.stereotype.Component; import org.springframework.web.filter.GenericFilterBean; -import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfig; +import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfigurer; import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationFilter; import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationSuccessHandler; -@Component -@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "userpwd-filter-enabled", havingValue = "true", matchIfMissing = false) -public class UsernamePasswordFiterLoginConfig implements LoginConfig { +//@Component +//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "userpwd-filter-enabled", havingValue = "true", matchIfMissing = false) +public class UsernamePasswordFiterLoginConfig implements LoginConfigurer { - private static final String API_LOGIN = "/api/login"; + // @Autowired + private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; - @Autowired - private JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; + public UsernamePasswordFiterLoginConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) { + super(); + this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler; + } - public GenericFilterBean authenticationProcessingFilter(AuthenticationManager authenticationManager) throws Exception { + public GenericFilterBean authenticationProcessingFilter(String loginPath, AuthenticationManager authenticationManager) throws Exception { JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtAuthenticationSuccessHandler); - authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(API_LOGIN, HttpMethod.POST.name(), false)); + authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(loginPath, HttpMethod.POST.name(), false)); return authenticationProcessingFilter; } @Override - public HttpSecurity login(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { - return http.addFilterAfter(authenticationProcessingFilter(authenticationManager), AbstractPreAuthenticatedProcessingFilter.class); + public String cookieMustBeDeletedAtLogout() { + return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout(); + } + + @Override + public HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { + return http.addFilterAfter(authenticationProcessingFilter(loginPath, authenticationManager), AbstractPreAuthenticatedProcessingFilter.class); } } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java index fec4e8a..46fe68b 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AuthenticationController.java @@ -59,7 +59,7 @@ public class AuthenticationController { @RequestMapping(value = "/refresh", method = RequestMethod.GET) public ResponseEntity refreshAuthenticationToken(HttpServletRequest request, HttpServletResponse response) { - String authToken = tokenHelper.getToken( request ); + String authToken = tokenHelper.retrieveToken( request ); if (authToken != null && tokenHelper.canTokenBeRefreshed(authToken)) { // TODO check user password last update String refreshedToken = tokenHelper.refreshToken(authToken); diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/JwtTokenHelper.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/JwtTokenHelper.java index a7d570e..df08bed 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/JwtTokenHelper.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/JwtTokenHelper.java @@ -10,9 +10,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.joda.time.DateTime; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; @@ -27,22 +24,27 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j -@Component +//@Component public class JwtTokenHelper { private static String base64EncodeSecretKey(String secretKey) { return Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8)); } - @Value("${app.name}") - private String APP_NAME; - - - @Autowired - private JwtSecurityProperties jwtSecurityProps; + // @Value("${app.name}") + private final String appName; + // @Autowired + private final JwtSecurityProperties jwtSecurityProps; private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512; + // @Autowired + public JwtTokenHelper(String appName, JwtSecurityProperties jwtSecurityProps) { + super(); + this.appName = appName; + this.jwtSecurityProps = jwtSecurityProps; + } + public Boolean canTokenBeRefreshed(String token) { try { final Date expirationDate = getClaimsFromToken(token).getExpiration(); @@ -63,30 +65,21 @@ public class JwtTokenHelper { } private String generateToken(Map claims) { - return Jwts.builder() - .setClaims(claims) - .setExpiration(generateExpirationDate()) - .signWith( SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())) - .compact(); + return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate()) + .signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())).compact(); } public String generateToken(String username) { - return Jwts.builder() - .setIssuer(APP_NAME) - .setSubject(username) - .setIssuedAt(generateCurrentDate()) + return Jwts.builder().setIssuer(appName).setSubject(username).setIssuedAt(generateCurrentDate()) .setExpiration(generateExpirationDate()) - .signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())) - .compact(); + .signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())).compact(); } private Claims getClaimsFromToken(String token) { Claims claims; try { - claims = Jwts.parser() - .setSigningKey(base64EncodeSecretKey(jwtSecurityProps.getSecret())) - .parseClaimsJws(token) - .getBody(); + claims = Jwts.parser().setSigningKey(base64EncodeSecretKey(jwtSecurityProps.getSecret())) + .parseClaimsJws(token).getBody(); } catch (Exception e) { claims = null; log.error("Error getting claims from jwt token due to " + e.getMessage(), e); @@ -98,9 +91,9 @@ public class JwtTokenHelper { * Find a specific HTTP cookie in a request. * * @param request - * The HTTP request object. + * The HTTP request object. * @param name - * The cookie name to look for. + * The cookie name to look for. * @return The cookie, or null if not found. */ public Cookie getCookieValueByName(HttpServletRequest request, String name) { @@ -116,18 +109,6 @@ public class JwtTokenHelper { return DateTime.now().getMillis(); } - public String getToken(HttpServletRequest request) { - Cookie authCookie = getCookieValueByName(request, jwtSecurityProps.getCookieStrategy().getCookie()); - if ( authCookie != null ) - return authCookie.getValue(); - - String authHeader = request.getHeader(jwtSecurityProps.getHeaderStrategy().getHeader()); - if ( authHeader != null && authHeader.startsWith("Bearer ")) - return authHeader.substring(7); - - return null; - } - public String getUsernameFromToken(String token) { String username; try { @@ -153,6 +134,22 @@ public class JwtTokenHelper { return refreshedToken; } + public String retrieveToken(HttpServletRequest request) { + if (jwtSecurityProps.getCookieStrategy().isEnabled() == true) { + Cookie authCookie = getCookieValueByName(request, jwtSecurityProps.getCookieStrategy().getCookie()); + if (authCookie != null) + return authCookie.getValue(); + } + + if (jwtSecurityProps.getHeaderStrategy().isEnabled()) { + String authHeader = request.getHeader(jwtSecurityProps.getHeaderStrategy().getHeader()); + if (authHeader != null && authHeader.startsWith("Bearer ")) + return authHeader.substring(7); + } + + return null; + } + public void setHeader(HttpServletResponse response, String token) { response.addHeader(jwtSecurityProps.getHeaderStrategy().getHeader(), "Bearer " + token); } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationFailureHandler.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationFailureHandler.java index e170b96..409eb45 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationFailureHandler.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationFailureHandler.java @@ -6,13 +6,11 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.stereotype.Component; -@Component -@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true) +//@Component +//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true) public class AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Override diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationSuccessHandler.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationSuccessHandler.java index 46a2e32..d800130 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationSuccessHandler.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/AuthenticationSuccessHandler.java @@ -6,18 +6,24 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.stereotype.Component; -@Component -@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true) +//@Component +//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true) public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - @Autowired - private JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; + private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; + + // @Autowired + public AuthenticationSuccessHandler(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) { + super(); + this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler; + } + + public String cookieMustBeDeletedAtLogout() { + return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout(); + } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandler.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandler.java index 372b1be..91356d7 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandler.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandler.java @@ -1,10 +1,13 @@ package it.fabioformosa.quartzmanager.security.auth; +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + import org.springframework.security.core.Authentication; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - public interface JwtAuthenticationSuccessHandler { void onSuccess(Authentication authentication, HttpServletResponse response) throws IOException; + + String cookieMustBeDeletedAtLogout(); } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandlerImpl.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandlerImpl.java index c342478..cffe01d 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandlerImpl.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationSuccessHandlerImpl.java @@ -8,7 +8,6 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.User; -import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; @@ -16,22 +15,34 @@ import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityPropert import it.fabioformosa.quartzmanager.security.JwtTokenHelper; import it.fabioformosa.quartzmanager.security.model.UserTokenState; -@Component +//@Component public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuccessHandler { - @Autowired - private JwtSecurityProperties jwtSecurityProps; + // @Autowired + private final JwtSecurityProperties jwtSecurityProps; private final JwtTokenHelper jwtTokenHelper; private final ObjectMapper objectMapper; + // @Value("${server.servlet.context-path}") + private final String contextPath; + @Autowired - public JwtAuthenticationSuccessHandlerImpl(JwtTokenHelper tokenHelper, ObjectMapper objectMapper) { - jwtTokenHelper = tokenHelper; + public JwtAuthenticationSuccessHandlerImpl(String contextPath, JwtSecurityProperties jwtSecurityProps, JwtTokenHelper jwtTokenHelper, ObjectMapper objectMapper) { + this.contextPath = contextPath; + this.jwtSecurityProps = jwtSecurityProps; + this.jwtTokenHelper = jwtTokenHelper; this.objectMapper = objectMapper; } + @Override + public String cookieMustBeDeletedAtLogout() { + if(!jwtSecurityProps.getCookieStrategy().isEnabled()) + return null; + return jwtSecurityProps.getCookieStrategy().getCookie(); + } + @Override public void onSuccess(Authentication authentication, HttpServletResponse response) throws IOException { User user = (User) authentication.getPrincipal(); @@ -42,7 +53,7 @@ public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuc Cookie authCookie = new Cookie(jwtSecurityProps.getCookieStrategy().getCookie(), jwtToken); authCookie.setHttpOnly(true); authCookie.setMaxAge((int) jwtSecurityProps.getExpirationInSec()); - authCookie.setPath("/quartz-manager"); + authCookie.setPath(contextPath); response.addCookie(authCookie); } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenAuthenticationFilter.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenAuthenticationFilter.java new file mode 100644 index 0000000..17f8686 --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenAuthenticationFilter.java @@ -0,0 +1,93 @@ +package it.fabioformosa.quartzmanager.security.auth; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; +import org.springframework.web.filter.OncePerRequestFilter; + +import it.fabioformosa.quartzmanager.security.JwtTokenHelper; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class JwtTokenAuthenticationFilter extends OncePerRequestFilter { + + public static final String ROOT_MATCHER = "/"; + public static final String FAVICON_MATCHER = "/favicon.ico"; + public static final String HTML_MATCHER = "/**/*.html"; + public static final String CSS_MATCHER = "/**/*.css"; + public static final String JS_MATCHER = "/**/*.js"; + public static final String IMG_MATCHER = "/images/*"; + public static final String LOGIN_MATCHER = "/api/login"; + public static final String LOGOUT_MATCHER = "/api/logout"; + + // private final Log logger = LogFactory.getLog(this.getClass()); + + // @Autowired + private final JwtTokenHelper jwtTokenHelper; + + // @Autowired + private final UserDetailsService userDetailsService; + + private List pathsToSkip = Arrays.asList( + ROOT_MATCHER, + HTML_MATCHER, + FAVICON_MATCHER, + CSS_MATCHER, + JS_MATCHER, + IMG_MATCHER, + LOGIN_MATCHER, + LOGOUT_MATCHER + ); + + + public JwtTokenAuthenticationFilter(JwtTokenHelper jwtTokenHelper, UserDetailsService userDetailsService) { + super(); + this.jwtTokenHelper = jwtTokenHelper; + this.userDetailsService = userDetailsService; + } + + @Override + public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + + String jwtToken = jwtTokenHelper.retrieveToken(request); + if (jwtToken != null && !skipPathRequest(request, pathsToSkip)) + try { + String username = jwtTokenHelper.getUsernameFromToken(jwtToken); + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + // create authentication + JwtTokenBasedAuthentication authentication = new JwtTokenBasedAuthentication(userDetails); + authentication.setToken(jwtToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (Exception e) { + SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); + log.error("Switched to Anonimous Authentication, " + + "because an error occurred setting authentication in security context holder due to " + e.getMessage(), e); + } + else + SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); + + chain.doFilter(request, response); + } + + private boolean skipPathRequest(HttpServletRequest request, List pathsToSkip ) { + Assert.notNull(pathsToSkip, "path cannot be null."); + List m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); + OrRequestMatcher matchers = new OrRequestMatcher(m); + return matchers.matches(request); + } + +} \ No newline at end of file diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenBasedAuthentication.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenBasedAuthentication.java new file mode 100644 index 0000000..2938674 --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtTokenBasedAuthentication.java @@ -0,0 +1,42 @@ +package it.fabioformosa.quartzmanager.security.auth; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.userdetails.UserDetails; + + +public class JwtTokenBasedAuthentication extends AbstractAuthenticationToken { + + private static final long serialVersionUID = 1L; + + private String token; + private final UserDetails principle; + + public JwtTokenBasedAuthentication(UserDetails principle) { + super(principle.getAuthorities()); + this.principle = principle; + } + + @Override + public Object getCredentials() { + return token; + } + + @Override + public UserDetails getPrincipal() { + return principle; + } + + public String getToken() { + return token; + } + + @Override + public boolean isAuthenticated() { + return true; + } + + public void setToken( String token ) { + this.token = token; + } + +} diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/LogoutSuccess.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/LogoutSuccess.java index c52e09f..f185289 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/LogoutSuccess.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/LogoutSuccess.java @@ -8,18 +8,20 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; -import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; -@Component public class LogoutSuccess implements LogoutSuccessHandler { - @Autowired - private ObjectMapper objectMapper; + private final ObjectMapper objectMapper; + + // @Autowired + public LogoutSuccess(ObjectMapper objectMapper) { + super(); + this.objectMapper = objectMapper; + } @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication) diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenAuthenticationFilter.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenAuthenticationFilter.java deleted file mode 100644 index 70a1aaf..0000000 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenAuthenticationFilter.java +++ /dev/null @@ -1,87 +0,0 @@ -package it.fabioformosa.quartzmanager.security.auth; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.OrRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; -import org.springframework.web.filter.OncePerRequestFilter; - -import it.fabioformosa.quartzmanager.security.JwtTokenHelper; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class TokenAuthenticationFilter extends OncePerRequestFilter { - - public static final String ROOT_MATCHER = "/"; - public static final String FAVICON_MATCHER = "/favicon.ico"; - public static final String HTML_MATCHER = "/**/*.html"; - public static final String CSS_MATCHER = "/**/*.css"; - public static final String JS_MATCHER = "/**/*.js"; - public static final String IMG_MATCHER = "/images/*"; - public static final String LOGIN_MATCHER = "/auth/login"; - public static final String LOGOUT_MATCHER = "/auth/logout"; - - // private final Log logger = LogFactory.getLog(this.getClass()); - - @Autowired - private JwtTokenHelper tokenHelper; - - @Autowired - private UserDetailsService userDetailsService; - - private List pathsToSkip = Arrays.asList( - ROOT_MATCHER, - HTML_MATCHER, - FAVICON_MATCHER, - CSS_MATCHER, - JS_MATCHER, - IMG_MATCHER, - LOGIN_MATCHER, - LOGOUT_MATCHER - ); - - @Override - public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - - String authToken = tokenHelper.getToken(request); - if (authToken != null && !skipPathRequest(request, pathsToSkip)) - try { - String username = tokenHelper.getUsernameFromToken(authToken); - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - // create authentication - TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails); - authentication.setToken(authToken); - SecurityContextHolder.getContext().setAuthentication(authentication); - } catch (Exception e) { - SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); - log.error("Switched to Anonimous Authentication, " - + "because an error occurred setting authentication in security context holder due to " + e.getMessage(), e); - } - else - SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); - - chain.doFilter(request, response); - } - - private boolean skipPathRequest(HttpServletRequest request, List pathsToSkip ) { - Assert.notNull(pathsToSkip, "path cannot be null."); - List m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); - OrRequestMatcher matchers = new OrRequestMatcher(m); - return matchers.matches(request); - } - -} \ No newline at end of file diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenBasedAuthentication.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenBasedAuthentication.java deleted file mode 100644 index dcf3d18..0000000 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/TokenBasedAuthentication.java +++ /dev/null @@ -1,42 +0,0 @@ -package it.fabioformosa.quartzmanager.security.auth; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.userdetails.UserDetails; - - -public class TokenBasedAuthentication extends AbstractAuthenticationToken { - - private static final long serialVersionUID = 1L; - - private String token; - private final UserDetails principle; - - public TokenBasedAuthentication( UserDetails principle ) { - super( principle.getAuthorities() ); - this.principle = principle; - } - - @Override - public Object getCredentials() { - return token; - } - - @Override - public UserDetails getPrincipal() { - return principle; - } - - public String getToken() { - return token; - } - - @Override - public boolean isAuthenticated() { - return true; - } - - public void setToken( String token ) { - this.token = token; - } - -} diff --git a/quartz-manager-api/src/main/resources/application.yml b/quartz-manager-api/src/main/resources/application.yml index 53892f1..ae82d02 100644 --- a/quartz-manager-api/src/main/resources/application.yml +++ b/quartz-manager-api/src/main/resources/application.yml @@ -39,7 +39,7 @@ quartz-manager: enabled: true header: "Authorization" cookie-strategy: - enabled: true + enabled: false cookie: AUTH-TOKEN jobClass: it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob accounts: diff --git a/quartz-manager-frontend/src/app/services/api.service.ts b/quartz-manager-frontend/src/app/services/api.service.ts index 0fc7d7b..201e901 100644 --- a/quartz-manager-frontend/src/app/services/api.service.ts +++ b/quartz-manager-frontend/src/app/services/api.service.ts @@ -22,8 +22,14 @@ export class ApiService { 'Content-Type': 'application/json' }); + private jwtToken: string; + constructor( private http: HttpClient) { } + setToken(token: string) { + this.jwtToken = token; + } + get(path: string, args?: any): Observable { const options = { headers: this.headers, @@ -56,6 +62,9 @@ export class ApiService { withCredentials: true }); + if(this.jwtToken) + req.headers.append('Authorization', `Bearer ${this.jwtToken}`); + return this.http.request(req) .pipe( filter(response => response instanceof HttpResponse), diff --git a/quartz-manager-frontend/src/app/services/auth.service.ts b/quartz-manager-frontend/src/app/services/auth.service.ts index 3af4dda..e79decf 100644 --- a/quartz-manager-frontend/src/app/services/auth.service.ts +++ b/quartz-manager-frontend/src/app/services/auth.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; -import { HttpHeaders } from '@angular/common/http'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; import { ApiService } from './api.service'; import { UserService } from './user.service'; import { ConfigService } from './config.service'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; @Injectable() export class AuthService { @@ -15,16 +15,33 @@ export class AuthService { private config: ConfigService, ) { } + private static extractTokenFromHttpResponse(res: HttpResponse): string { + let authorization: string = null; + let headers: HttpHeaders = res.headers; + if (headers.has('Authorization')) + authorization = headers.get('Authorization'); + if(authorization.startsWith('Bearer ')) + authorization = authorization.substring(7); + return authorization; + } + login(user) { const loginHeaders = new HttpHeaders({ 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' }); const body = `username=${user.username}&password=${user.password}`; - return this.apiService.post(this.config.login_url, body, loginHeaders).pipe(map(() => { - console.log("Login success"); - this.userService.getMyInfo().subscribe(); - })); + return this.apiService.post(this.config.login_url, body, loginHeaders) + .pipe( + tap((resp: HttpResponse) => { + let jwtToken = AuthService.extractTokenFromHttpResponse(resp); + this.apiService.setToken(jwtToken); + }), + map(() => { + console.log("Login success"); + this.userService.getMyInfo().subscribe(); + }) + ); } signup(user){ @@ -48,4 +65,5 @@ export class AuthService { return this.apiService.post(this.config.change_password_url, passwordChanger); } + } diff --git a/quartz-manager-frontend/src/app/views/login/login.component.ts b/quartz-manager-frontend/src/app/views/login/login.component.ts index 5962dd3..201b259 100644 --- a/quartz-manager-frontend/src/app/views/login/login.component.ts +++ b/quartz-manager-frontend/src/app/views/login/login.component.ts @@ -82,7 +82,6 @@ export class LoginComponent implements OnInit, OnDestroy { this.authService.login(this.form.value) .pipe(delay(1000)) .subscribe(data => { - this.userService.getMyInfo().subscribe(); this.router.navigate([this.returnUrl]); }, error => {