diff --git a/quartz-manager-api/pom.xml b/quartz-manager-api/pom.xml index 72afb43..dbce52a 100644 --- a/quartz-manager-api/pom.xml +++ b/quartz-manager-api/pom.xml @@ -164,7 +164,12 @@ 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 77bb44f..16bedb9 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 @@ -4,7 +4,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; @@ -14,16 +17,17 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.factory.PasswordEncoderFactories; 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.security.auth.AuthenticationFailureHandler; -import it.fabioformosa.quartzmanager.security.auth.AuthenticationSuccessHandler; +import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfig; +import it.fabioformosa.quartzmanager.configuration.properties.InMemoryAccountProperties; import it.fabioformosa.quartzmanager.security.auth.LogoutSuccess; -import it.fabioformosa.quartzmanager.security.auth.RestAuthenticationEntryPoint; import it.fabioformosa.quartzmanager.security.auth.TokenAuthenticationFilter; /** @@ -37,91 +41,96 @@ import it.fabioformosa.quartzmanager.security.auth.TokenAuthenticationFilter; @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { - @Value("${jwt.cookie}") - private String TOKEN_COOKIE; + private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v2/api-docs", "/swagger-resources/**", "/webjars/**"}; - // @Autowired - // private CustomUserDetailsService jwtUserDetailsService; + @Value("${quartz-manager.security.jwt.cookie}") + private String TOKEN_COOKIE; - @Autowired - private RestAuthenticationEntryPoint restAuthenticationEntryPoint; + // @Autowired + // private CustomUserDetailsService jwtUserDetailsService; - @Autowired - private LogoutSuccess logoutSuccess; + @Autowired + private LogoutSuccess logoutSuccess; - @Autowired - private AuthenticationSuccessHandler authenticationSuccessHandler; + @Autowired + private LoginConfig loginConfigurer; - @Autowired - private AuthenticationFailureHandler authenticationFailureHandler; + @Autowired + private InMemoryAccountProperties inMemoryAccountProps; - @Value("${quartz-manager.account.user}") - private String adminUser; + // @Bean + // @Override + // public AuthenticationManager authenticationManagerBean() throws Exception { + // return super.authenticationManagerBean(); + // } - @Value("${quartz-manager.account.pwd}") - private String adminPwd; + @Override + public void configure(AuthenticationManagerBuilder authenticationManagerBuilder)throws Exception { + configureInMemoryAuthentication(authenticationManagerBuilder); + // authenticationManagerBuilder.userDetailsService(jwtUserDetailsService) + // .passwordEncoder(passwordEncoder()); + } - // @Bean - // @Override - // public AuthenticationManager authenticationManagerBean() throws Exception { - // return super.authenticationManagerBean(); - // } + @Override + protected void configure(HttpSecurity http) throws Exception { + // http.csrf().ignoringAntMatchers("/api/login", "/api/signup") // + // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // + http.csrf().disable() // + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // + .exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() + .addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) + .authorizeRequests().anyRequest().authenticated(); - @Override - public void configure(WebSecurity web) throws Exception { - web.ignoring().antMatchers("/css/**", // - "/js/**", // - "/img/**", // - "/lib/**", // - "/swagger-resources/**", "/swagger-ui.html","/v2/api-docs", // - "/webjars/**"); - } + loginConfigurer.configureLoginHandler(http, authenticationManager()).logout().logoutRequestMatcher(new AntPathRequestMatcher("/api/logout")) + .logoutSuccessHandler(logoutSuccess).deleteCookies(TOKEN_COOKIE); - @Autowired - public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) - throws Exception { - // authenticationManagerBuilder.userDetailsService(jwtUserDetailsService) - // .passwordEncoder(passwordEncoder()); - PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); - authenticationManagerBuilder.inMemoryAuthentication().withUser(adminUser).password(encoder.encode(adminPwd)).roles("ADMIN"); - } + } - @Bean - public TokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception { - return new TokenAuthenticationFilter(); - } + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring()// + .antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI) // + .antMatchers(HttpMethod.GET,"/css/**", "/js/**", "/img/**", "/lib/**"); + } - @Bean - @Override - public UserDetailsService userDetailsServiceBean() throws Exception { - return super.userDetailsServiceBean(); - } + private void configureInMemoryAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); + if(inMemoryAccountProps.isEnabled() && inMemoryAccountProps.getUsers() != null && !inMemoryAccountProps.getUsers().isEmpty()) { + InMemoryUserDetailsManagerConfigurer inMemoryAuth = authenticationManagerBuilder.inMemoryAuthentication(); + inMemoryAccountProps.getUsers() + .forEach(u -> inMemoryAuth + .withUser(u.getName()) + .password(encoder.encode(u.getPassword())) + .roles(u.getRoles().toArray(new String[0]))); + } + } - // @Bean - // public PasswordEncoder passwordEncoder() { - // return new BCryptPasswordEncoder(); - // } + @Bean + CorsConfigurationSource corsConfigurationSource() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); + return source; + } - @Override - protected void configure(HttpSecurity http) throws Exception { - // http.csrf().ignoringAntMatchers("/api/login", "/api/signup") // - // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // - http.csrf().disable() // - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // - .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).and() - .addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) - .authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/api/login") - .successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler) - .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/api/logout")) - .logoutSuccessHandler(logoutSuccess).deleteCookies(TOKEN_COOKIE); + @Bean + public TokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception { + return new TokenAuthenticationFilter(); + } - } + // @Bean + // public PasswordEncoder passwordEncoder() { + // return new BCryptPasswordEncoder(); + // } - @Bean - CorsConfigurationSource corsConfigurationSource() { - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); - return source; - } + @Bean + public AuthenticationEntryPoint restAuthEntryPoint() { + return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED); + } + + @Bean + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return super.userDetailsServiceBean(); + } } 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/LoginConfig.java new file mode 100644 index 0000000..be5b2fd --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/LoginConfig.java @@ -0,0 +1,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 { + + HttpSecurity configureLoginHandler(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 new file mode 100644 index 0000000..e683754 --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/FormLoginConfig.java @@ -0,0 +1,31 @@ +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.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 { + + private static final String API_LOGIN = "/api/login"; + + @Autowired + private AuthenticationSuccessHandler authenticationSuccessHandler; + + @Autowired + private AuthenticationFailureHandler authenticationFailureHandler; + + @Override + public HttpSecurity configureLoginHandler( + HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { + return http.formLogin().loginPage(API_LOGIN).successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and(); + } + +} 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 new file mode 100644 index 0000000..4d26310 --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/helpers/impl/UsernamePasswordFiterLoginConfig.java @@ -0,0 +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.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.security.JwtTokenHelper; +import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationFilter; + +@Component +@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "userpwd-filter-enabled", havingValue = "true", matchIfMissing = false) +public class UsernamePasswordFiterLoginConfig implements LoginConfig { + + private static final String API_LOGIN = "/api/login"; + + @Autowired + private JwtTokenHelper jwtTokenHelper; + + public GenericFilterBean authenticationProcessingFilter(AuthenticationManager authenticationManager) throws Exception { + JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtTokenHelper); + authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(API_LOGIN, HttpMethod.POST.name(), false)); + return authenticationProcessingFilter; + } + + @Override + public HttpSecurity configureLoginHandler(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { + return http.addFilterAfter(authenticationProcessingFilter(authenticationManager), AbstractPreAuthenticatedProcessingFilter.class); + } + +} diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/InMemoryAccountProperties.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/InMemoryAccountProperties.java new file mode 100644 index 0000000..a6905ba --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/InMemoryAccountProperties.java @@ -0,0 +1,24 @@ +package it.fabioformosa.quartzmanager.configuration.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +@ConfigurationProperties(prefix = "quartz-manager.accounts.in-memory") +@Getter @Setter +public class InMemoryAccountProperties { + private boolean enabled; + private List users; + + @Getter @Setter + public static class User { + private String name; + private String password; + private List roles = new ArrayList<>(); + } +} diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/JwtSecurityProperties.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/JwtSecurityProperties.java new file mode 100644 index 0000000..d2e9f49 --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/configuration/properties/JwtSecurityProperties.java @@ -0,0 +1,17 @@ +package it.fabioformosa.quartzmanager.configuration.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "quartz-manager.security.jwt") +@Getter @Setter +public class JwtSecurityProperties { + private boolean enabled; + private String secret; + private long expirationInSec; + private String header; + private String cookie; +} \ No newline at end of file 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 61f64a4..74f25d8 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 @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import it.fabioformosa.quartzmanager.security.TokenHelper; +import it.fabioformosa.quartzmanager.security.JwtTokenHelper; import it.fabioformosa.quartzmanager.security.model.UserTokenState; import it.fabioformosa.quartzmanager.security.service.impl.CustomUserDetailsService; @@ -39,12 +39,12 @@ public class AuthenticationController { private CustomUserDetailsService userDetailsService; @Autowired - TokenHelper tokenHelper; + JwtTokenHelper tokenHelper; - @Value("${jwt.expires_in_sec}") + @Value("${quartz-manager.security.jwt.expiration-in-sec}") private int EXPIRES_IN_SEC; - @Value("${jwt.cookie}") + @Value("${quartz-manager.security.jwt.cookie}") private String TOKEN_COOKIE; @RequestMapping(value = "/changePassword", method = RequestMethod.POST) diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/TokenHelper.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/JwtTokenHelper.java similarity index 69% rename from quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/TokenHelper.java rename to quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/JwtTokenHelper.java index 6576838..e3b2b82 100644 --- a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/TokenHelper.java +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/JwtTokenHelper.java @@ -1,22 +1,26 @@ package it.fabioformosa.quartzmanager.security; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.Date; import java.util.Map; import javax.servlet.http.Cookie; 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; import io.jsonwebtoken.SignatureAlgorithm; +import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties; import lombok.extern.slf4j.Slf4j; /** - * JWT Temporary disabled * * @author Fabio.Formosa * @@ -24,25 +28,32 @@ import lombok.extern.slf4j.Slf4j; @Slf4j @Component -public class TokenHelper { +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; - @Value("${jwt.secret}") - private String SECRET; - - @Value("${jwt.expires_in_sec}") - private int EXPIRES_IN_SEC; - - @Value("${jwt.header}") - private String AUTH_HEADER; - - @Value("${jwt.cookie}") - private String AUTH_COOKIE; + // @Value("${jwt.secret}") + // private String SECRET; + // + // @Value("${jwt.expires_in_sec}") + // private int EXPIRES_IN_SEC; + // + // @Value("${jwt.header}") + // private String AUTH_HEADER; // @Autowired // UserDetailsService userDetailsService; + // + // @Value("${jwt.cookie}") + // private String AUTH_COOKIE; + + @Autowired + private JwtSecurityProperties jwtSecurityProps; private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512; @@ -62,14 +73,14 @@ public class TokenHelper { } private Date generateExpirationDate() { - return new Date(getCurrentTimeMillis() + EXPIRES_IN_SEC * 1000); + return new Date(getCurrentTimeMillis() + jwtSecurityProps.getExpirationInSec() * 1000); } - String generateToken(Map claims) { + private String generateToken(Map claims) { return Jwts.builder() .setClaims(claims) .setExpiration(generateExpirationDate()) - .signWith( SIGNATURE_ALGORITHM, SECRET ) + .signWith( SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())) .compact(); } @@ -79,7 +90,7 @@ public class TokenHelper { .setSubject(username) .setIssuedAt(generateCurrentDate()) .setExpiration(generateExpirationDate()) - .signWith(SIGNATURE_ALGORITHM, SECRET) + .signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())) .compact(); } @@ -87,7 +98,7 @@ public class TokenHelper { Claims claims; try { claims = Jwts.parser() - .setSigningKey(SECRET) + .setSigningKey(base64EncodeSecretKey(jwtSecurityProps.getSecret())) .parseClaimsJws(token) .getBody(); } catch (Exception e) { @@ -119,12 +130,12 @@ public class TokenHelper { return DateTime.now().getMillis(); } - public String getToken( HttpServletRequest request ) { - Cookie authCookie = getCookieValueByName( request, AUTH_COOKIE ); + public String getToken(HttpServletRequest request) { + Cookie authCookie = getCookieValueByName(request, jwtSecurityProps.getCookie()); if ( authCookie != null ) return authCookie.getValue(); - String authHeader = request.getHeader(AUTH_HEADER); + String authHeader = request.getHeader(jwtSecurityProps.getHeader()); if ( authHeader != null && authHeader.startsWith("Bearer ")) return authHeader.substring(7); @@ -155,4 +166,8 @@ public class TokenHelper { } return refreshedToken; } + + public void setHeader(HttpServletResponse response, String token) { + response.addHeader(jwtSecurityProps.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 a57f6e1..e170b96 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,17 +6,19 @@ 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) public class AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException exception) throws IOException, ServletException { + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException, ServletException { - super.onAuthenticationFailure(request, response, exception); - } + super.onAuthenticationFailure(request, response, exception); + } } \ No newline at end of file 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 9f3bd77..df6d282 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 @@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.User; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; @@ -16,54 +17,52 @@ import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; -import it.fabioformosa.quartzmanager.security.TokenHelper; +import it.fabioformosa.quartzmanager.security.JwtTokenHelper; import it.fabioformosa.quartzmanager.security.model.UserTokenState; @Component +@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true) public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - @Value("${jwt.expires_in_sec}") - private int EXPIRES_IN_SEC; + @Value("${quartz-manager.security.jwt.expiration-in-sec}") + private int EXPIRES_IN_SEC; - @Value("${jwt.cookie}") - private String TOKEN_COOKIE; + @Value("${quartz-manager.security.jwt.cookie}") + private String TOKEN_COOKIE; - @Autowired - TokenHelper tokenHelper; - // - @Autowired - ObjectMapper objectMapper; + @Autowired + JwtTokenHelper tokenHelper; + // + @Autowired + ObjectMapper objectMapper; - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication ) throws IOException, ServletException { - clearAuthenticationAttributes(request); - User user = (User)authentication.getPrincipal(); + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication ) throws IOException, ServletException { + clearAuthenticationAttributes(request); + User user = (User) authentication.getPrincipal(); - String jws = tokenHelper.generateToken( user.getUsername() ); + String jws = tokenHelper.generateToken(user.getUsername()); - Cookie authCookie = new Cookie( TOKEN_COOKIE, jws ); + Cookie authCookie = new Cookie( TOKEN_COOKIE, jws ); + authCookie.setHttpOnly(true); + authCookie.setMaxAge(EXPIRES_IN_SEC); + authCookie.setPath( "/quartz-manager" ); + response.addCookie(authCookie); - authCookie.setHttpOnly( true ); + // JWT is also in the response + UserTokenState userTokenState = new UserTokenState(jws, EXPIRES_IN_SEC); + String jwtResponse = objectMapper.writeValueAsString( userTokenState ); + response.setContentType("application/json"); + response.getWriter().write( jwtResponse ); - authCookie.setMaxAge( EXPIRES_IN_SEC ); + } - authCookie.setPath( "/quartz-manager" ); - response.addCookie( authCookie ); - - // JWT is also in the response - UserTokenState userTokenState = new UserTokenState(jws, EXPIRES_IN_SEC); - String jwtResponse = objectMapper.writeValueAsString( userTokenState ); - response.setContentType("application/json"); - response.getWriter().write( jwtResponse ); - - } - - // @Override - // public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - // Authentication authentication ) throws IOException, ServletException { - // // clearAuthenticationAttributes(request); - // response.setContentType("application/json"); - // response.getWriter().write( objectMapper.writeValueAsString("OK")); - // } + // @Override + // public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + // Authentication authentication ) throws IOException, ServletException { + // // clearAuthenticationAttributes(request); + // response.setContentType("application/json"); + // response.getWriter().write( objectMapper.writeValueAsString("OK")); + // } } diff --git a/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationFilter.java b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationFilter.java new file mode 100644 index 0000000..a8ec266 --- /dev/null +++ b/quartz-manager-api/src/main/java/it/fabioformosa/quartzmanager/security/auth/JwtAuthenticationFilter.java @@ -0,0 +1,32 @@ +package it.fabioformosa.quartzmanager.security.auth; + +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import it.fabioformosa.quartzmanager.security.JwtTokenHelper; + +public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + private final JwtTokenHelper jwtTokenHelper; + + public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtTokenHelper jwtTokenHelper) { + this.jwtTokenHelper = jwtTokenHelper; + setAuthenticationManager(authenticationManager); + } + + @Override + protected void successfulAuthentication(HttpServletRequest req, + HttpServletResponse res, + FilterChain chain, + Authentication auth) { + UserDetails user = (UserDetails) auth.getPrincipal(); + String token = jwtTokenHelper.generateToken(user.getUsername()); + jwtTokenHelper.setHeader(res, token); + } +} \ No newline at end of file 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 index b0856ad..70a1aaf 100644 --- 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 @@ -20,7 +20,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; -import it.fabioformosa.quartzmanager.security.TokenHelper; +import it.fabioformosa.quartzmanager.security.JwtTokenHelper; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -38,7 +38,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { // private final Log logger = LogFactory.getLog(this.getClass()); @Autowired - private TokenHelper tokenHelper; + private JwtTokenHelper tokenHelper; @Autowired private UserDetailsService userDetailsService; diff --git a/quartz-manager-api/src/main/resources/application.yml b/quartz-manager-api/src/main/resources/application.yml index c4c71ce..bfa8435 100644 --- a/quartz-manager-api/src/main/resources/application.yml +++ b/quartz-manager-api/src/main/resources/application.yml @@ -19,12 +19,6 @@ job: frequency: 4000 repeatCount: 19 -jwt: - header: Authorization - expires_in_sec: 600 # 10 minutes - secret: queenvictoria - cookie: AUTH-TOKEN - logging: level: org.springframework.web: WARN @@ -33,8 +27,22 @@ logging: it.fabioformosa: DEBUG quartz-manager: + security: + login-model: + form-login-enabled: true + userpwd-filter-enabled : false + jwt: + enabled: true + secret: "bibidibobidiboo" + expiration-in-sec: 28800 # 8 hours + header: "Authorization" + cookie: AUTH-TOKEN jobClass: it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob - account: - user: admin - pwd: admin - \ No newline at end of file + accounts: + in-memory: + enabled: true + users: + - name: admin + password: admin + roles: + - ADMIN \ No newline at end of file