#6 added login success strategies

This commit is contained in:
fabio.formosa
2020-04-22 23:23:46 +02:00
parent 2f3dadc186
commit 75d4ac9827
12 changed files with 159 additions and 169 deletions

View File

@@ -0,0 +1,89 @@
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

@@ -1,88 +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;
//@Configuration
//@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// @Configuration
@Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/","/v2/api-docs",
"/swagger-resources/**",
"/swagger-ui.html",
"/webjars/**",
"/csrf");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //
.antMatcher("/notifications").authorizeRequests().anyRequest().hasAnyRole("ADMIN").and()
.httpBasic();
}
}
// @Configuration
@Order(2)
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private ComboEntryPoint comboEntryPoint;
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**", "/webjars/**");
}
@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");
}
}
@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

@@ -35,7 +35,6 @@ import it.fabioformosa.quartzmanager.security.auth.TokenAuthenticationFilter;
* @author Fabio.Formosa
*
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@@ -53,7 +52,7 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter {
private LogoutSuccess logoutSuccess;
@Autowired
private LoginConfig loginConfigurer;
private LoginConfig loginConfig;
@Autowired
private InMemoryAccountProperties inMemoryAccountProps;
@@ -75,13 +74,14 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter {
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)
.exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() //
.addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) //
.authorizeRequests().anyRequest().authenticated();
loginConfigurer.configureLoginHandler(http, authenticationManager()).logout().logoutRequestMatcher(new AntPathRequestMatcher("/api/logout"))
loginConfig.login(http, authenticationManager()).logout().logoutRequestMatcher(new AntPathRequestMatcher("/api/logout"))
.logoutSuccessHandler(logoutSuccess).deleteCookies(TOKEN_COOKIE);
}

View File

@@ -5,6 +5,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
public interface LoginConfig {
HttpSecurity configureLoginHandler(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception;
HttpSecurity login(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception;
}

View File

@@ -23,7 +23,7 @@ public class FormLoginConfig implements LoginConfig {
private AuthenticationFailureHandler authenticationFailureHandler;
@Override
public HttpSecurity configureLoginHandler(
public HttpSecurity login(
HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
return http.formLogin().loginPage(API_LOGIN).successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and();
}

View File

@@ -1,6 +1,5 @@
package it.fabioformosa.quartzmanager.configuration.helpers.impl;
import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpMethod;
@@ -12,8 +11,8 @@ 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;
import it.fabioformosa.quartzmanager.security.auth.JwtAuthenticationSuccessHandler;
@Component
@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "userpwd-filter-enabled", havingValue = "true", matchIfMissing = false)
@@ -21,20 +20,17 @@ public class UsernamePasswordFiterLoginConfig implements LoginConfig {
private static final String API_LOGIN = "/api/login";
@Autowired
private JwtTokenHelper jwtTokenHelper;
@Autowired
private JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
public GenericFilterBean authenticationProcessingFilter(AuthenticationManager authenticationManager) throws Exception {
JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtTokenHelper, jwtAuthenticationSuccessHandler);
JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtAuthenticationSuccessHandler);
authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(API_LOGIN, HttpMethod.POST.name(), false));
return authenticationProcessingFilter;
}
@Override
public HttpSecurity configureLoginHandler(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
public HttpSecurity login(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
return http.addFilterAfter(authenticationProcessingFilter(authenticationManager), AbstractPreAuthenticatedProcessingFilter.class);
}

View File

@@ -1,10 +1,12 @@
package it.fabioformosa.quartzmanager.configuration.properties;
import lombok.Data;
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
@@ -12,6 +14,20 @@ public class JwtSecurityProperties {
private boolean enabled;
private String secret;
private long expirationInSec;
private String header;
private String cookie;
private CookieStrategy cookieStrategy;
private HeaderStrategy headerStrategy;
@Data
public static class CookieStrategy {
private boolean enabled;
private String cookie;
}
@Data
public static class HeaderStrategy {
private boolean enabled;
private String header;
}
}

View File

@@ -37,20 +37,6 @@ public class JwtTokenHelper {
@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;
// @Autowired
// UserDetailsService userDetailsService;
//
// @Value("${jwt.cookie}")
// private String AUTH_COOKIE;
@Autowired
private JwtSecurityProperties jwtSecurityProps;
@@ -131,11 +117,11 @@ public class JwtTokenHelper {
}
public String getToken(HttpServletRequest request) {
Cookie authCookie = getCookieValueByName(request, jwtSecurityProps.getCookie());
Cookie authCookie = getCookieValueByName(request, jwtSecurityProps.getCookieStrategy().getCookie());
if ( authCookie != null )
return authCookie.getValue();
String authHeader = request.getHeader(jwtSecurityProps.getHeader());
String authHeader = request.getHeader(jwtSecurityProps.getHeaderStrategy().getHeader());
if ( authHeader != null && authHeader.startsWith("Bearer "))
return authHeader.substring(7);
@@ -168,6 +154,6 @@ public class JwtTokenHelper {
}
public void setHeader(HttpServletResponse response, String token) {
response.addHeader(jwtSecurityProps.getHeader(), "Bearer " + token);
response.addHeader(jwtSecurityProps.getHeaderStrategy().getHeader(), "Bearer " + token);
}
}

View File

@@ -4,23 +4,17 @@ import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
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;
import java.io.IOException;
import lombok.SneakyThrows;
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final JwtTokenHelper jwtTokenHelper;
private JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtTokenHelper jwtTokenHelper, JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
this.jwtTokenHelper = jwtTokenHelper;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
setAuthenticationManager(authenticationManager);
}
@@ -31,10 +25,6 @@ public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilte
HttpServletResponse res,
FilterChain chain,
Authentication auth) {
UserDetails user = (UserDetails) auth.getPrincipal();
String token = jwtTokenHelper.generateToken(user.getUsername());
jwtTokenHelper.setHeader(res, token);
jwtAuthenticationSuccessHandler.onSuccess(auth, res);
}
}

View File

@@ -1,26 +1,26 @@
package it.fabioformosa.quartzmanager.security.auth;
import com.fasterxml.jackson.databind.ObjectMapper;
import it.fabioformosa.quartzmanager.security.JwtTokenHelper;
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
import java.io.IOException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
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
public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuccessHandler {
@Value("${quartz-manager.security.jwt.expiration-in-sec}")
private int EXPIRES_IN_SEC;
@Value("${quartz-manager.security.jwt.cookie}")
private String TOKEN_COOKIE;
@Autowired
private JwtSecurityProperties jwtSecurityProps;
private final JwtTokenHelper jwtTokenHelper;
@@ -28,7 +28,7 @@ public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuc
@Autowired
public JwtAuthenticationSuccessHandlerImpl(JwtTokenHelper tokenHelper, ObjectMapper objectMapper) {
this.jwtTokenHelper = tokenHelper;
jwtTokenHelper = tokenHelper;
this.objectMapper = objectMapper;
}
@@ -36,17 +36,20 @@ public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuc
public void onSuccess(Authentication authentication, HttpServletResponse response) throws IOException {
User user = (User) authentication.getPrincipal();
String jws = jwtTokenHelper.generateToken(user.getUsername());
String jwtToken = jwtTokenHelper.generateToken(user.getUsername());
//set cookie or set header?
Cookie authCookie = new Cookie(TOKEN_COOKIE, jws);
authCookie.setHttpOnly(true);
authCookie.setMaxAge(EXPIRES_IN_SEC);
authCookie.setPath("/quartz-manager");
response.addCookie(authCookie);
if(jwtSecurityProps.getCookieStrategy().isEnabled()) {
Cookie authCookie = new Cookie(jwtSecurityProps.getCookieStrategy().getCookie(), jwtToken);
authCookie.setHttpOnly(true);
authCookie.setMaxAge((int) jwtSecurityProps.getExpirationInSec());
authCookie.setPath("/quartz-manager");
response.addCookie(authCookie);
}
// JWT is also in the response
UserTokenState userTokenState = new UserTokenState(jws, EXPIRES_IN_SEC);
if(jwtSecurityProps.getHeaderStrategy().isEnabled())
jwtTokenHelper.setHeader(response, jwtToken);
UserTokenState userTokenState = new UserTokenState(jwtToken, jwtSecurityProps.getExpirationInSec());
String jwtResponse = objectMapper.writeValueAsString(userTokenState);
response.setContentType("application/json");
response.getWriter().write(jwtResponse);

View File

@@ -18,18 +18,18 @@ import com.fasterxml.jackson.databind.ObjectMapper;
@Component
public class LogoutSuccess implements LogoutSuccessHandler {
@Autowired
ObjectMapper objectMapper;
@Autowired
private ObjectMapper objectMapper;
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
Map<String, String> result = new HashMap<>();
result.put( "result", "success" );
response.setContentType("application/json");
response.getWriter().write( objectMapper.writeValueAsString( result ) );
response.setStatus(HttpServletResponse.SC_OK);
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
Map<String, String> result = new HashMap<>();
result.put( "result", "success" );
response.setContentType("application/json");
response.getWriter().write(objectMapper.writeValueAsString(result));
response.setStatus(HttpServletResponse.SC_OK);
}
}
}

View File

@@ -35,10 +35,8 @@ quartz-manager:
enabled: true
secret: "bibidibobidiboo"
expiration-in-sec: 28800 # 8 hours
header: "Authorization"
cookie: AUTH-TOKEN
header-strategy:
enabled: false
enabled: true
header: "Authorization"
cookie-strategy:
enabled: true