#6 added javadocs and moved packages

This commit is contained in:
fabio.formosa
2020-05-02 16:48:45 +02:00
parent 166244a67b
commit b8b88a6879
27 changed files with 305 additions and 321 deletions

View File

@@ -1,89 +0,0 @@
package it.fabioformosa.quartzmanager.configuration;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import it.fabioformosa.quartzmanager.security.ComboEntryPoint;
import it.fabioformosa.quartzmanager.security.auth.AuthenticationFailureHandler;
import it.fabioformosa.quartzmanager.security.auth.AuthenticationSuccessHandler;
@Deprecated
//@Configuration
//@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class DeprecatedWebSecurityConfig extends WebSecurityConfigurerAdapter {
// @Configuration
@Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //
.antMatcher("/notifications").authorizeRequests().anyRequest().hasAnyRole("ADMIN").and()
.httpBasic();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/","/v2/api-docs",
"/swagger-resources/**",
"/swagger-ui.html",
"/webjars/**",
"/csrf");
}
}
// @Configuration
@Order(2)
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private ComboEntryPoint comboEntryPoint;
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().ignoringAntMatchers("/api/login", "/api/signup").and() //
http.cors().and().csrf().disable().exceptionHandling().authenticationEntryPoint(comboEntryPoint)
.and()//
.authorizeRequests().anyRequest().authenticated().and()//
.formLogin().loginPage("/api/login").successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler).and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/api/logout"))
.logoutSuccessUrl("/manager");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**", "/webjars/**");
}
}
@Value("${quartz-manager.account.user}")
private String adminUser;
@Value("${quartz-manager.account.pwd}")
private String adminPwd;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication().withUser(adminUser).password(encoder.encode(adminPwd)).roles("ADMIN");
}
}

View File

@@ -27,19 +27,19 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
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.helpers.LoginConfigurer;
import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationFailureHandler;
import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationSuccessHandler;
import it.fabioformosa.quartzmanager.security.helpers.impl.FormLoginConfig;
import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandler;
import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandlerImpl;
import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenAuthenticationFilter;
import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenHelper;
import it.fabioformosa.quartzmanager.security.helpers.impl.QuartzManagerHttpSecurity;
import it.fabioformosa.quartzmanager.security.helpers.impl.JwtUsernamePasswordFiterLoginConfig;
import it.fabioformosa.quartzmanager.security.helpers.impl.LogoutSuccess;
/**
*
@@ -76,37 +76,29 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
// @Autowired
// private CustomUserDetailsService jwtUserDetailsService;
@Autowired
private InMemoryAccountProperties inMemoryAccountProps;
// @Bean
// @Override
// public AuthenticationManager authenticationManagerBean() throws Exception {
// return super.authenticationManagerBean();
// }
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder)throws Exception {
configureInMemoryAuthentication(authenticationManagerBuilder);
// authenticationManagerBuilder.userDetailsService(jwtUserDetailsService)
// .passwordEncoder(passwordEncoder());
}
@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();
QuartzManagerHttpSecurity.from(http).loginConfig(loginConfigurer(), logoutConfigurer()).login(LOGIN_PATH, authenticationManager()).logout(LOGOUT_PATH);
QuartzManagerHttpSecurity.from(http).withLoginConfigurer(loginConfigurer(), logoutConfigurer()) //
.login(LOGIN_PATH, authenticationManager()).logout(LOGOUT_PATH);
// temporary disabled csfr
// http.csrf().ignoringAntMatchers("/api/login", "/api/signup") //
// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //
}
@Override
@@ -158,8 +150,7 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter {
@Bean
public JwtTokenHelper jwtTokenHelper() {
JwtTokenHelper jwtTokenHelper = new JwtTokenHelper(APP_NAME, jwtSecurityProps);
return jwtTokenHelper;
return new JwtTokenHelper(APP_NAME, jwtSecurityProps);
}
@Bean
@@ -181,11 +172,6 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter {
return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
}
// @Bean
// public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
// }
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
@@ -194,8 +180,13 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter {
@Bean
public LoginConfigurer userpwdFilterLoginConfigurer() {
LoginConfigurer loginConfigurer = new UsernamePasswordFiterLoginConfig(jwtAuthenticationSuccessHandler());
LoginConfigurer loginConfigurer = new JwtUsernamePasswordFiterLoginConfig(jwtAuthenticationSuccessHandler());
return loginConfigurer;
}
// @Bean
// public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
// }
}

View File

@@ -1,12 +0,0 @@
package it.fabioformosa.quartzmanager.configuration.helpers;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
public interface LoginConfigurer {
String cookieMustBeDeletedAtLogout();
HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception;
}

View File

@@ -1,37 +0,0 @@
package it.fabioformosa.quartzmanager.configuration.helpers.impl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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 LoginConfigurer {
private final AuthenticationSuccessHandler authenticationSuccessHandler;
private final AuthenticationFailureHandler authenticationFailureHandler;
// @Autowired
public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler,
AuthenticationFailureHandler authenticationFailureHandler) {
super();
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authenticationFailureHandler = authenticationFailureHandler;
}
@Override
public String cookieMustBeDeletedAtLogout() {
return authenticationSuccessHandler.cookieMustBeDeletedAtLogout();
}
@Override
public HttpSecurity login(String loginPath,
HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
return http.formLogin().loginPage(loginPath).successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and();
}
}

View File

@@ -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.JwtTokenHelper;
import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenHelper;
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
import it.fabioformosa.quartzmanager.security.service.impl.CustomUserDetailsService;

View File

@@ -1,93 +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.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<String> 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<String> pathsToSkip ) {
Assert.notNull(pathsToSkip, "path cannot be null.");
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
OrRequestMatcher matchers = new OrRequestMatcher(m);
return matchers.matches(request);
}
}

View File

@@ -0,0 +1,21 @@
package it.fabioformosa.quartzmanager.security.helpers;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
/**
* It configures filters to authenticate credentials sent by client or to set authenticationSuccessHandler
*
* Implement this interface for a login strategy
*
*/
public interface LoginConfigurer {
/**
* If the authentication is based on cookie, it returns the name of cookie to be erased at the logout
*/
String cookieMustBeDeletedAtLogout();
HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception;
}

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import org.springframework.security.authentication.AbstractAuthenticationToken;

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
@@ -9,8 +9,6 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
//@Component
//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true)
public class AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
@@ -29,7 +29,7 @@ public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccess
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication ) throws IOException, ServletException {
clearAuthenticationAttributes(request);
jwtAuthenticationSuccessHandler.onSuccess(authentication, response);
jwtAuthenticationSuccessHandler.onLoginSuccess(authentication, response);
}
}

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;

View File

@@ -0,0 +1,75 @@
package it.fabioformosa.quartzmanager.security.helpers.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
/**
* It delegates form to @FormLoginConfigurer of the httpSecurity.
*
*/
public class FormLoginConfig implements LoginConfigurer {
private static final Logger log = LoggerFactory.getLogger(FormLoginConfig.class);
private final AuthenticationSuccessHandler authenticationSuccessHandler;
private final AuthenticationFailureHandler authenticationFailureHandler;
public FormLoginConfig() {
super();
authenticationSuccessHandler = null;
authenticationFailureHandler = null;
}
public FormLoginConfig(AuthenticationFailureHandler authenticationFailureHandler) {
super();
authenticationSuccessHandler = null;
this.authenticationFailureHandler = authenticationFailureHandler;
}
public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler) {
super();
this.authenticationSuccessHandler = authenticationSuccessHandler;
authenticationFailureHandler = null;
}
public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler,
AuthenticationFailureHandler authenticationFailureHandler) {
super();
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authenticationFailureHandler = authenticationFailureHandler;
}
@Override
public String cookieMustBeDeletedAtLogout() {
return authenticationSuccessHandler.cookieMustBeDeletedAtLogout();
}
@Override
public HttpSecurity login(String loginPath,
HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
log.debug("Configuring login through FormLoginConfigurer...");
FormLoginConfigurer<HttpSecurity> login = http.formLogin().loginPage(loginPath);
if(authenticationSuccessHandler != null) {
log.debug("Setting an authenticationSuccessHandler");
login = login.successHandler(authenticationSuccessHandler);
}
if(authenticationFailureHandler != null) {
log.debug("Setting an authenticationFailureHandler");
login = login.failureHandler(authenticationFailureHandler);
}
HttpSecurity httpSecurity = login.and();
return httpSecurity;
}
}

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
@@ -10,6 +10,10 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import lombok.SneakyThrows;
/**
* It extends the @UsernamePasswordAuthenticationFilter and it overrides the successfulAuthentication method to put jwtToken in the response
*
*/
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
@@ -25,6 +29,6 @@ public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilte
HttpServletResponse res,
FilterChain chain,
Authentication auth) {
jwtAuthenticationSuccessHandler.onSuccess(auth, res);
jwtAuthenticationSuccessHandler.onLoginSuccess(auth, res);
}
}

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
@@ -7,7 +7,8 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
public interface JwtAuthenticationSuccessHandler {
void onSuccess(Authentication authentication, HttpServletResponse response) throws IOException;
String cookieMustBeDeletedAtLogout();
void onLoginSuccess(Authentication authentication, HttpServletResponse response) throws IOException;
}

View File

@@ -1,10 +1,12 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
@@ -12,20 +14,24 @@ import org.springframework.security.core.userdetails.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
import it.fabioformosa.quartzmanager.security.JwtTokenHelper;
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
//@Component
/**
* It depends on @JwtTokenHelper to generate the jwtToken.
* On login success, it generates the jwtToken and it returns it to the login according to possible strategies: cookie, response header.
* You can choice the strategy through @JwtSecurityProperties
*
*/
public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuccessHandler {
// @Autowired
private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationSuccessHandlerImpl.class);
private final JwtSecurityProperties jwtSecurityProps;
private final JwtTokenHelper jwtTokenHelper;
private final ObjectMapper objectMapper;
// @Value("${server.servlet.context-path}")
private final String contextPath;
@Autowired
@@ -44,9 +50,10 @@ public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuc
}
@Override
public void onSuccess(Authentication authentication, HttpServletResponse response) throws IOException {
User user = (User) authentication.getPrincipal();
public void onLoginSuccess(Authentication authentication, HttpServletResponse response) throws IOException {
log.debug("Login successed, generating jwtToken...");
User user = (User) authentication.getPrincipal();
String jwtToken = jwtTokenHelper.generateToken(user.getUsername());
if(jwtSecurityProps.getCookieStrategy().isEnabled()) {
@@ -55,10 +62,13 @@ public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuc
authCookie.setMaxAge((int) jwtSecurityProps.getExpirationInSec());
authCookie.setPath(contextPath);
response.addCookie(authCookie);
log.debug("Set jwtToken into the cookie {}", jwtSecurityProps.getCookieStrategy().getCookie());
}
if(jwtSecurityProps.getHeaderStrategy().isEnabled())
if(jwtSecurityProps.getHeaderStrategy().isEnabled()) {
jwtTokenHelper.setHeader(response, jwtToken);
log.debug("Set jwtToken into the response header {}", jwtSecurityProps.getHeaderStrategy().getHeader());
}
UserTokenState userTokenState = new UserTokenState(jwtToken, jwtSecurityProps.getExpirationInSec());
String jwtResponse = objectMapper.writeValueAsString(userTokenState);

View File

@@ -0,0 +1,105 @@
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
import java.util.ArrayList;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.web.filter.OncePerRequestFilter;
/**
* It finds the jwtToken into the request, it validates it and sets an @Authentication into the @SecurityContextHolder.
* If the request has a path included into the paths that must be skipped, it sets an anonymous authentication
*
* It delegates the jwtToken retrieve to the @JwtTokenHelper that applies several strategies.
*
*/
public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(JwtTokenAuthenticationFilter.class);
private static final String ROOT_MATCHER = "/";
private static final String FAVICON_MATCHER = "/favicon.ico";
private static final String HTML_MATCHER = "/**/*.html";
private static final String CSS_MATCHER = "/**/*.css";
private static final String JS_MATCHER = "/**/*.js";
private static final String IMG_MATCHER = "/images/*";
private static final String LOGIN_MATCHER = "/api/login";
private static final String LOGOUT_MATCHER = "/api/logout";
private static List<String> PATH_TO_SKIP = Arrays.asList(
ROOT_MATCHER,
HTML_MATCHER,
FAVICON_MATCHER,
CSS_MATCHER,
JS_MATCHER,
IMG_MATCHER,
LOGIN_MATCHER,
LOGOUT_MATCHER
);
private final JwtTokenHelper jwtTokenHelper;
private final UserDetailsService userDetailsService;
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) {
log.debug("Found a jwtToken into the request {}", request.getPathInfo());
try {
String username = jwtTokenHelper.getUsernameFromToken(jwtToken);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
JwtTokenBasedAuthentication authentication = new JwtTokenBasedAuthentication(userDetails);
authentication.setToken(jwtToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
log.error("Authentication failed! an expected error occurred authenticating the request {}", request.getRequestURL());
// SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication());
// log.error("Switched to Anonymous Authentication, "
// + "because an error occurred setting authentication in security context holder due to " + e.getMessage(), e);
}
}
else if(skipPathRequest(request, PATH_TO_SKIP)) {
log.debug("Detected a path to be skipped from authentication, so activated anonymous auth for {}", request.getRequestURL());
SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication());
}
else
log.debug("Not found any jwtToken and the request hasn't a path to be skipped from auth. Path: {}", request.getRequestURL());
chain.doFilter(request, response);
}
private boolean skipPathRequest(HttpServletRequest request, List<String> pathsToSkip ) {
if(pathsToSkip == null)
pathsToSkip = new ArrayList<String>();
List<RequestMatcher> matchers = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
OrRequestMatcher compositeMatchers = new OrRequestMatcher(matchers);
return compositeMatchers.matches(request);
}
}

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@@ -10,12 +10,13 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
import lombok.extern.slf4j.Slf4j;
/**
*
@@ -23,16 +24,17 @@ import lombok.extern.slf4j.Slf4j;
*
*/
@Slf4j
//@Component
public class JwtTokenHelper {
private static final Logger log = LoggerFactory.getLogger(JwtTokenHelper.class);
private static String base64EncodeSecretKey(String secretKey) {
return Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8));
}
// @Value("${app.name}")
private final String appName;
// @Autowired
private final JwtSecurityProperties jwtSecurityProps;
@@ -117,6 +119,7 @@ public class JwtTokenHelper {
} catch (Exception e) {
username = null;
log.error("Error getting claims from jwt token due to " + e.getMessage(), e);
throw e;
}
return username;
}

View File

@@ -1,5 +1,7 @@
package it.fabioformosa.quartzmanager.configuration.helpers.impl;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -7,18 +9,19 @@ import org.springframework.security.web.authentication.preauth.AbstractPreAuthen
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.web.filter.GenericFilterBean;
import it.fabioformosa.quartzmanager.configuration.helpers.LoginConfigurer;
import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationFilter;
import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationSuccessHandler;
import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
//@Component
//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "userpwd-filter-enabled", havingValue = "true", matchIfMissing = false)
public class UsernamePasswordFiterLoginConfig implements LoginConfigurer {
/**
* It adds a new filter @JwtAuthenticationFilter after @AbstractPreAuthenticatedProcessingFilter that match login path
*
*/
public class JwtUsernamePasswordFiterLoginConfig implements LoginConfigurer {
private static final Logger log = LoggerFactory.getLogger(JwtUsernamePasswordFiterLoginConfig.class);
// @Autowired
private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
public UsernamePasswordFiterLoginConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
public JwtUsernamePasswordFiterLoginConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
super();
this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
}
@@ -36,6 +39,7 @@ public class UsernamePasswordFiterLoginConfig implements LoginConfigurer {
@Override
public HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
log.debug("Configuring login through JwtAuthenticationFilter...");
return http.addFilterAfter(authenticationProcessingFilter(loginPath, authenticationManager), AbstractPreAuthenticatedProcessingFilter.class);
}

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;
import java.util.HashMap;
@@ -17,7 +17,6 @@ public class LogoutSuccess implements LogoutSuccessHandler {
private final ObjectMapper objectMapper;
// @Autowired
public LogoutSuccess(ObjectMapper objectMapper) {
super();
this.objectMapper = objectMapper;

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.configuration.helpers.impl;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
@@ -7,9 +7,12 @@ import org.springframework.security.config.annotation.web.configurers.LogoutConf
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;
import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
/**
* It wraps the httpSecurity to provide new function as login and logout
*
*/
public class QuartzManagerHttpSecurity extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
public static QuartzManagerHttpSecurity from(HttpSecurity httpSecurity){
@@ -20,35 +23,35 @@ public class QuartzManagerHttpSecurity extends SecurityConfigurerAdapter<Default
private HttpSecurity httpSecurity;
private LoginConfigurer loginConfiger;
private LoginConfigurer loginConfigurer;
private LogoutSuccess logoutSuccess;
public QuartzManagerHttpSecurity(HttpSecurity httpSecurity) {
this.httpSecurity = httpSecurity;
// applicationContext = httpSecurity.getSharedObject(ApplicationContext.class);
// loginConfiger = applicationContext.getBean(LoginConfigurer.class);
// logoutSuccess = applicationContext.getBean(LogoutSuccess.class);
}
public QuartzManagerHttpSecurity login(String loginPath, AuthenticationManager authenticationManager) throws Exception {
httpSecurity = loginConfiger.login(loginPath, httpSecurity, authenticationManager);
if(loginConfigurer == null || logoutSuccess == null)
throw new IllegalStateException("QuartzManagerHttpSecurity requires to be set loginConfigurer and logoutSuccess!");
httpSecurity = loginConfigurer.login(loginPath, httpSecurity, authenticationManager);
return this;
}
public QuartzManagerHttpSecurity loginConfig(LoginConfigurer loginConfigurer, LogoutSuccess logoutSuccess) {
loginConfiger = loginConfigurer;
this.logoutSuccess = logoutSuccess;
return this;
}
public LogoutConfigurer<HttpSecurity> logout(String logoutPath) throws Exception {
LogoutConfigurer<HttpSecurity> logoutConfigurer = httpSecurity.logout().logoutRequestMatcher(new AntPathRequestMatcher(logoutPath))
.logoutSuccessHandler(logoutSuccess);
String cookie = loginConfiger.cookieMustBeDeletedAtLogout();
String cookie = loginConfigurer.cookieMustBeDeletedAtLogout();
if(cookie != null)
logoutConfigurer.deleteCookies(cookie);
return logoutConfigurer;
}
public QuartzManagerHttpSecurity withLoginConfigurer(LoginConfigurer loginConfigurer, LogoutSuccess logoutSuccess) {
this.loginConfigurer = loginConfigurer;
this.logoutSuccess = logoutSuccess;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import javax.servlet.http.HttpServletRequest;

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security.auth;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import java.io.IOException;

View File

@@ -1,4 +1,4 @@
package it.fabioformosa.quartzmanager.security;
package it.fabioformosa.quartzmanager.security.helpers.impl;
import javax.servlet.http.HttpServletRequest;

View File

@@ -11,6 +11,7 @@ spring:
thymeleaf:
cache: false
mode: LEGACYHTML5
jpa.open-in-view: false
quartz:
enabled: true
@@ -36,10 +37,10 @@ quartz-manager:
secret: "bibidibobidiboo"
expiration-in-sec: 28800 # 8 hours
header-strategy:
enabled: true
enabled: false
header: "Authorization"
cookie-strategy:
enabled: false
enabled: true
cookie: AUTH-TOKEN
jobClass: it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob
accounts:

View File

@@ -4,7 +4,7 @@
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%.10thread] %-5level [%-40.40logger{49}:%-3L] --- %m%n
%d{yyyy-MM-dd HH:mm:ss.SSS} [%.11thread] %-5level [%-40.40logger{49}:%-3L] --- %m%n
</Pattern>
</layout>
</appender>