mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2026-05-15 06:10:29 +09:00
Merge pull request #7 from fabioformosa/#6_security_refactoring
#6 security refactoring
This commit is contained in:
2
quartz-manager-api/.gitignore
vendored
2
quartz-manager-api/.gitignore
vendored
@@ -7,3 +7,5 @@
|
||||
/mvnw
|
||||
/mvnw.cmd
|
||||
/.classpath
|
||||
/.idea/
|
||||
/quartz-manager.iml
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- SPRING -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -58,6 +59,11 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
@@ -69,6 +75,7 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MISC -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
@@ -91,23 +98,31 @@
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.nekohtml</groupId>
|
||||
<artifactId>nekohtml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>spring-mock-mvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.10</version>
|
||||
</dependency>
|
||||
|
||||
<!-- QUARTZ -->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
@@ -158,13 +173,6 @@
|
||||
<version>${springfox.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package it.fabioformosa.quartzmanager.configuration;
|
||||
|
||||
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;
|
||||
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,114 +18,175 @@ 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.security.auth.LogoutSuccess;
|
||||
import it.fabioformosa.quartzmanager.security.auth.RestAuthenticationEntryPoint;
|
||||
import it.fabioformosa.quartzmanager.security.auth.TokenAuthenticationFilter;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import it.fabioformosa.quartzmanager.configuration.properties.InMemoryAccountProperties;
|
||||
import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@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;
|
||||
private static final String LOGIN_PATH = "/api/login";
|
||||
private static final String LOGOUT_PATH = "/api/logout";
|
||||
|
||||
@Autowired
|
||||
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
|
||||
@Value("${server.servlet.context-path}")
|
||||
private String contextPath;
|
||||
|
||||
@Autowired
|
||||
private LogoutSuccess logoutSuccess;
|
||||
@Value("${app.name}")
|
||||
private String APP_NAME;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
@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 AuthenticationFailureHandler authenticationFailureHandler;
|
||||
@Autowired
|
||||
private JwtSecurityProperties jwtSecurityProps;
|
||||
|
||||
@Value("${quartz-manager.account.user}")
|
||||
private String adminUser;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Value("${quartz-manager.account.pwd}")
|
||||
private String adminPwd;
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
// @Bean
|
||||
// @Override
|
||||
// public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
// return super.authenticationManagerBean();
|
||||
// }
|
||||
@Autowired
|
||||
private InMemoryAccountProperties inMemoryAccountProps;
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.ignoring().antMatchers("/css/**", //
|
||||
"/js/**", //
|
||||
"/img/**", //
|
||||
"/lib/**", //
|
||||
"/swagger-resources/**", "/swagger-ui.html","/v2/api-docs", //
|
||||
"/webjars/**");
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
@Override
|
||||
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder)throws Exception {
|
||||
configureInMemoryAuthentication(authenticationManagerBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception {
|
||||
return new TokenAuthenticationFilter();
|
||||
}
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable() //
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //
|
||||
.exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() //
|
||||
.addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) //
|
||||
.authorizeRequests().anyRequest().authenticated();
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsServiceBean() throws Exception {
|
||||
return super.userDetailsServiceBean();
|
||||
}
|
||||
QuartzManagerHttpSecurity.from(http).withLoginConfigurer(loginConfigurer(), logoutConfigurer()) //
|
||||
.login(LOGIN_PATH, authenticationManager()).logout(LOGOUT_PATH);
|
||||
|
||||
// @Bean
|
||||
// public PasswordEncoder passwordEncoder() {
|
||||
// return new BCryptPasswordEncoder();
|
||||
// }
|
||||
// temporary disabled csfr
|
||||
// http.csrf().ignoringAntMatchers("/api/login", "/api/signup") //
|
||||
// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //
|
||||
}
|
||||
|
||||
@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);
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.ignoring()//
|
||||
.antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI) //
|
||||
.antMatchers(HttpMethod.GET,"/css/**", "/js/**", "/img/**", "/lib/**");
|
||||
}
|
||||
|
||||
}
|
||||
private void configureInMemoryAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
|
||||
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
if(inMemoryAccountProps.isEnabled() && inMemoryAccountProps.getUsers() != null && !inMemoryAccountProps.getUsers().isEmpty()) {
|
||||
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuth = authenticationManagerBuilder.inMemoryAuthentication();
|
||||
inMemoryAccountProps.getUsers()
|
||||
.forEach(u -> inMemoryAuth
|
||||
.withUser(u.getName())
|
||||
.password(encoder.encode(u.getPassword()))
|
||||
.roles(u.getRoles().toArray(new String[0])));
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
|
||||
return source;
|
||||
}
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
|
||||
return source;
|
||||
}
|
||||
|
||||
@Bean
|
||||
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() {
|
||||
return new JwtTokenHelper(APP_NAME, jwtSecurityProps);
|
||||
}
|
||||
|
||||
@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
|
||||
@Override
|
||||
public UserDetailsService userDetailsServiceBean() throws Exception {
|
||||
return super.userDetailsServiceBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LoginConfigurer userpwdFilterLoginConfigurer() {
|
||||
LoginConfigurer loginConfigurer = new JwtUsernamePasswordFiterLoginConfig(jwtAuthenticationSuccessHandler());
|
||||
return loginConfigurer;
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public PasswordEncoder passwordEncoder() {
|
||||
// return new BCryptPasswordEncoder();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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<User> users;
|
||||
|
||||
@Getter @Setter
|
||||
public static class User {
|
||||
private String name;
|
||||
private String password;
|
||||
private List<String> roles = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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
|
||||
public class JwtSecurityProperties {
|
||||
private boolean enabled;
|
||||
private String secret;
|
||||
private long expirationInSec;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.helpers.impl.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-strategy-cookie}")
|
||||
private String TOKEN_COOKIE;
|
||||
|
||||
@RequestMapping(value = "/changePassword", method = RequestMethod.POST)
|
||||
@@ -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);
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
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.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.TokenHelper;
|
||||
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
|
||||
|
||||
@Component
|
||||
public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
@Value("${jwt.expires_in_sec}")
|
||||
private int EXPIRES_IN_SEC;
|
||||
|
||||
@Value("${jwt.cookie}")
|
||||
private String TOKEN_COOKIE;
|
||||
|
||||
@Autowired
|
||||
TokenHelper tokenHelper;
|
||||
//
|
||||
@Autowired
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
@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() );
|
||||
|
||||
Cookie authCookie = new Cookie( TOKEN_COOKIE, jws );
|
||||
|
||||
authCookie.setHttpOnly( true );
|
||||
|
||||
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"));
|
||||
// }
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.TokenHelper;
|
||||
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 TokenHelper tokenHelper;
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
private List<String> 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<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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -8,15 +8,13 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
|
||||
//@Component
|
||||
//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
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,
|
||||
Authentication authentication ) throws IOException, ServletException {
|
||||
clearAuthenticationAttributes(request);
|
||||
jwtAuthenticationSuccessHandler.onLoginSuccess(authentication, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
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.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
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;
|
||||
|
||||
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
|
||||
this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
|
||||
setAuthenticationManager(authenticationManager);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
protected void successfulAuthentication(HttpServletRequest req,
|
||||
HttpServletResponse res,
|
||||
FilterChain chain,
|
||||
Authentication auth) {
|
||||
jwtAuthenticationSuccessHandler.onLoginSuccess(auth, res);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
public interface JwtAuthenticationSuccessHandler {
|
||||
|
||||
String cookieMustBeDeletedAtLogout();
|
||||
|
||||
void onLoginSuccess(Authentication authentication, HttpServletResponse response) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
|
||||
import it.fabioformosa.quartzmanager.security.model.UserTokenState;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationSuccessHandlerImpl.class);
|
||||
|
||||
private final JwtSecurityProperties jwtSecurityProps;
|
||||
|
||||
private final JwtTokenHelper jwtTokenHelper;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final String contextPath;
|
||||
|
||||
@Autowired
|
||||
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 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()) {
|
||||
Cookie authCookie = new Cookie(jwtSecurityProps.getCookieStrategy().getCookie(), jwtToken);
|
||||
authCookie.setHttpOnly(true);
|
||||
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()) {
|
||||
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);
|
||||
response.setContentType("application/json");
|
||||
response.getWriter().write(jwtResponse);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +1,52 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
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.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import it.fabioformosa.quartzmanager.configuration.properties.JwtSecurityProperties;
|
||||
|
||||
/**
|
||||
* JWT Temporary disabled
|
||||
*
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TokenHelper {
|
||||
public class JwtTokenHelper {
|
||||
|
||||
@Value("${app.name}")
|
||||
private String APP_NAME;
|
||||
private static final Logger log = LoggerFactory.getLogger(JwtTokenHelper.class);
|
||||
|
||||
@Value("${jwt.secret}")
|
||||
private String SECRET;
|
||||
private static String base64EncodeSecretKey(String secretKey) {
|
||||
return Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Value("${jwt.expires_in_sec}")
|
||||
private int EXPIRES_IN_SEC;
|
||||
// @Value("${app.name}")
|
||||
private final String appName;
|
||||
|
||||
@Value("${jwt.header}")
|
||||
private String AUTH_HEADER;
|
||||
|
||||
@Value("${jwt.cookie}")
|
||||
private String AUTH_COOKIE;
|
||||
|
||||
// @Autowired
|
||||
// UserDetailsService userDetailsService;
|
||||
// @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();
|
||||
@@ -62,34 +63,25 @@ public class TokenHelper {
|
||||
}
|
||||
|
||||
private Date generateExpirationDate() {
|
||||
return new Date(getCurrentTimeMillis() + EXPIRES_IN_SEC * 1000);
|
||||
return new Date(getCurrentTimeMillis() + jwtSecurityProps.getExpirationInSec() * 1000);
|
||||
}
|
||||
|
||||
String generateToken(Map<String, Object> claims) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setExpiration(generateExpirationDate())
|
||||
.signWith( SIGNATURE_ALGORITHM, SECRET )
|
||||
.compact();
|
||||
private String generateToken(Map<String, Object> claims) {
|
||||
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, SECRET)
|
||||
.compact();
|
||||
.signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())).compact();
|
||||
}
|
||||
|
||||
private Claims getClaimsFromToken(String token) {
|
||||
Claims claims;
|
||||
try {
|
||||
claims = Jwts.parser()
|
||||
.setSigningKey(SECRET)
|
||||
.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);
|
||||
@@ -101,9 +93,9 @@ public class TokenHelper {
|
||||
* 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 <code>null</code> if not found.
|
||||
*/
|
||||
public Cookie getCookieValueByName(HttpServletRequest request, String name) {
|
||||
@@ -119,18 +111,6 @@ public class TokenHelper {
|
||||
return DateTime.now().getMillis();
|
||||
}
|
||||
|
||||
public String getToken( HttpServletRequest request ) {
|
||||
Cookie authCookie = getCookieValueByName( request, AUTH_COOKIE );
|
||||
if ( authCookie != null )
|
||||
return authCookie.getValue();
|
||||
|
||||
String authHeader = request.getHeader(AUTH_HEADER);
|
||||
if ( authHeader != null && authHeader.startsWith("Bearer "))
|
||||
return authHeader.substring(7);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUsernameFromToken(String token) {
|
||||
String username;
|
||||
try {
|
||||
@@ -139,6 +119,7 @@ public class TokenHelper {
|
||||
} catch (Exception e) {
|
||||
username = null;
|
||||
log.error("Error getting claims from jwt token due to " + e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
return username;
|
||||
}
|
||||
@@ -155,4 +136,27 @@ public class TokenHelper {
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if(request.getParameter("access_token") != null)
|
||||
return request.getParameter("access_token");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setHeader(HttpServletResponse response, String token) {
|
||||
response.addHeader(jwtSecurityProps.getHeaderStrategy().getHeader(), "Bearer " + token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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;
|
||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.helpers.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);
|
||||
|
||||
private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
|
||||
|
||||
public JwtUsernamePasswordFiterLoginConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
|
||||
super();
|
||||
this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
|
||||
}
|
||||
|
||||
public GenericFilterBean authenticationProcessingFilter(String loginPath, AuthenticationManager authenticationManager) throws Exception {
|
||||
JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtAuthenticationSuccessHandler);
|
||||
authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(loginPath, HttpMethod.POST.name(), false));
|
||||
return authenticationProcessingFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cookieMustBeDeletedAtLogout() {
|
||||
return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public class LogoutSuccess implements LogoutSuccessHandler {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public LogoutSuccess(ObjectMapper objectMapper) {
|
||||
super();
|
||||
this.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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
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.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){
|
||||
QuartzManagerHttpSecurity newInstance = new QuartzManagerHttpSecurity(httpSecurity);
|
||||
newInstance.setBuilder(httpSecurity);
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
private HttpSecurity httpSecurity;
|
||||
|
||||
private LoginConfigurer loginConfigurer;
|
||||
|
||||
private LogoutSuccess logoutSuccess;
|
||||
|
||||
public QuartzManagerHttpSecurity(HttpSecurity httpSecurity) {
|
||||
this.httpSecurity = httpSecurity;
|
||||
// applicationContext = httpSecurity.getSharedObject(ApplicationContext.class);
|
||||
}
|
||||
|
||||
public QuartzManagerHttpSecurity login(String loginPath, AuthenticationManager authenticationManager) throws Exception {
|
||||
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 LogoutConfigurer<HttpSecurity> logout(String logoutPath) throws Exception {
|
||||
LogoutConfigurer<HttpSecurity> logoutConfigurer = httpSecurity.logout().logoutRequestMatcher(new AntPathRequestMatcher(logoutPath))
|
||||
.logoutSuccessHandler(logoutSuccess);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package it.fabioformosa.quartzmanager.security.auth;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -1,4 +1,4 @@
|
||||
package it.fabioformosa.quartzmanager.security;
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@@ -11,6 +11,7 @@ spring:
|
||||
thymeleaf:
|
||||
cache: false
|
||||
mode: LEGACYHTML5
|
||||
jpa.open-in-view: false
|
||||
|
||||
quartz:
|
||||
enabled: true
|
||||
@@ -19,12 +20,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 +28,26 @@ 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-strategy:
|
||||
enabled: false
|
||||
header: "Authorization"
|
||||
cookie-strategy:
|
||||
enabled: true
|
||||
cookie: AUTH-TOKEN
|
||||
jobClass: it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob
|
||||
account:
|
||||
user: admin
|
||||
pwd: admin
|
||||
|
||||
accounts:
|
||||
in-memory:
|
||||
enabled: true
|
||||
users:
|
||||
- name: admin
|
||||
password: admin
|
||||
roles:
|
||||
- ADMIN
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"aot": true,
|
||||
"outputPath": "../server/src/main/resources/static",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
@@ -27,6 +28,12 @@
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
@@ -119,7 +126,7 @@
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "app",
|
||||
"styleext": "css"
|
||||
"style": "css"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "app"
|
||||
|
||||
12
quartz-manager-frontend/browserslist
Normal file
12
quartz-manager-frontend/browserslist
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
@@ -12,19 +12,19 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "7.2.13",
|
||||
"@angular/cdk": "7.3.7",
|
||||
"@angular/common": "7.2.13",
|
||||
"@angular/compiler": "7.2.13",
|
||||
"@angular/core": "7.2.13",
|
||||
"@angular/flex-layout": "7.0.0-beta.24",
|
||||
"@angular/forms": "7.2.13",
|
||||
"@angular/http": "7.2.13",
|
||||
"@angular/material": "7.3.7",
|
||||
"@angular/platform-browser": "7.2.13",
|
||||
"@angular/platform-browser-dynamic": "7.2.13",
|
||||
"@angular/platform-server": "7.2.13",
|
||||
"@angular/router": "7.2.13",
|
||||
"@angular/animations": "9.1.4",
|
||||
"@angular/cdk": "9.2.1",
|
||||
"@angular/common": "9.1.4",
|
||||
"@angular/compiler": "9.1.4",
|
||||
"@angular/core": "9.1.4",
|
||||
"@angular/flex-layout": "9.0.0-beta.29",
|
||||
"@angular/forms": "9.1.4",
|
||||
"@angular/material": "9.2.1",
|
||||
"@angular/platform-browser": "9.1.4",
|
||||
"@angular/platform-browser-dynamic": "9.1.4",
|
||||
"@angular/platform-server": "9.1.4",
|
||||
"@angular/router": "9.1.4",
|
||||
"@auth0/angular-jwt": "^4.0.0",
|
||||
"@fortawesome/fontawesome": "^1.1.4",
|
||||
"@fortawesome/fontawesome-free-regular": "^5.0.8",
|
||||
"@fortawesome/fontawesome-free-solid": "^5.0.8",
|
||||
@@ -32,22 +32,22 @@
|
||||
"core-js": "2.5.1",
|
||||
"hammerjs": "2.0.8",
|
||||
"net": "^1.0.2",
|
||||
"rxjs": "6.4.0",
|
||||
"rxjs": "6.5.5",
|
||||
"stompjs": "^2.3.3",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "0.8.18"
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.13.0",
|
||||
"@angular-devkit/core": "^0.2.0",
|
||||
"@angular/cli": "7.3.7",
|
||||
"@angular/compiler-cli": "7.2.13",
|
||||
"@angular/language-service": "7.2.13",
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular-devkit/core": "^9.1.4",
|
||||
"@angular/cli": "9.1.4",
|
||||
"@angular/compiler-cli": "9.1.4",
|
||||
"@angular/language-service": "9.1.4",
|
||||
"@types/hammerjs": "2.0.34",
|
||||
"@types/jasmine": "2.5.54",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/node": "6.0.90",
|
||||
"codelyzer": "4.2.1",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "2.6.4",
|
||||
"jasmine-spec-reporter": "4.1.1",
|
||||
"karma": "1.7.1",
|
||||
@@ -59,6 +59,6 @@
|
||||
"protractor": "5.1.2",
|
||||
"ts-node": "3.0.6",
|
||||
"tslint": "5.7.0",
|
||||
"typescript": "3.2.4"
|
||||
"typescript": "3.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"/quartz-manager": {
|
||||
"target": "http://localhost:8080",
|
||||
"secure": false,
|
||||
"cookiePathRewrite": "/",
|
||||
"ws":true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, APP_INITIALIZER} from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import {JWT_OPTIONS, JwtModule} from "@auth0/angular-jwt";
|
||||
|
||||
// material
|
||||
import {
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatTooltipModule,
|
||||
MatCardModule,
|
||||
MatChipsModule,
|
||||
MatInputModule,
|
||||
MatIconRegistry,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
} from '@angular/material';
|
||||
import {MatIconRegistry} from '@angular/material/icon';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatChipsModule} from '@angular/material/chips';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { AppComponent } from './app.component';
|
||||
@@ -77,6 +78,15 @@ export function initUserFactory(userService: UserService) {
|
||||
// debug: true
|
||||
// };
|
||||
|
||||
export function jwtOptionsFactory(apiService: ApiService) {
|
||||
return {
|
||||
tokenGetter: () => {
|
||||
return apiService.getToken();
|
||||
},
|
||||
whitelistedDomains: ['localhost:8080', 'localhost:4200']
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
@@ -99,9 +109,15 @@ export function initUserFactory(userService: UserService) {
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
HttpModule,
|
||||
HttpClientModule,
|
||||
AppRoutingModule,
|
||||
JwtModule.forRoot({
|
||||
jwtOptionsProvider: {
|
||||
provide: JWT_OPTIONS,
|
||||
useFactory: jwtOptionsFactory,
|
||||
deps: [ApiService]
|
||||
}
|
||||
}),
|
||||
MatMenuModule,
|
||||
MatTooltipModule,
|
||||
MatButtonModule,
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
}
|
||||
|
||||
|
||||
/deep/ {
|
||||
::ng-deep {
|
||||
.app-header-accountMenu.mat-menu-panel {
|
||||
border-radius: 3px;
|
||||
max-width: initial;
|
||||
|
||||
@@ -4,11 +4,14 @@ export class SocketOption{
|
||||
brokerName : string;
|
||||
reconnectionTimeout : number = 30000
|
||||
|
||||
constructor(socketUrl : string, topicName : string, brokerName : string = null, reconnectionTimeout : number = 30000){
|
||||
getAccessToken: Function = () => null;
|
||||
|
||||
constructor(socketUrl : string, topicName : string, getAccessToken?: Function, brokerName : string = null, reconnectionTimeout : number = 30000){
|
||||
this.socketUrl = socketUrl;
|
||||
this.topicName = topicName;
|
||||
this.brokerName = brokerName;
|
||||
this.reconnectionTimeout = reconnectionTimeout;
|
||||
this.getAccessToken = getAccessToken || (() => null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { catchError, map, filter } from 'rxjs/operators'
|
||||
import { catchError, map, filter, tap } from 'rxjs/operators'
|
||||
import { serialize } from 'app/shared/utilities/serialize';
|
||||
|
||||
export enum RequestMethod {
|
||||
@@ -17,22 +17,43 @@ export enum RequestMethod {
|
||||
@Injectable()
|
||||
export class ApiService {
|
||||
|
||||
private static extractTokenFromHttpResponse(res: HttpResponse<any>): string {
|
||||
let authorization: string = null;
|
||||
let headers: HttpHeaders = res.headers;
|
||||
if (headers && headers.has('Authorization')){
|
||||
authorization = headers.get('Authorization');
|
||||
if(authorization.startsWith('Bearer '))
|
||||
authorization = authorization.substring(7);
|
||||
}
|
||||
return authorization;
|
||||
}
|
||||
|
||||
headers = new HttpHeaders({
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
private jwtToken: string;
|
||||
|
||||
constructor( private http: HttpClient) { }
|
||||
|
||||
setToken(token: string) {
|
||||
this.jwtToken = token;
|
||||
}
|
||||
|
||||
getToken = () => this.jwtToken;
|
||||
|
||||
get(path: string, args?: any): Observable<any> {
|
||||
const options = {
|
||||
headers: this.headers,
|
||||
withCredentials: true
|
||||
};
|
||||
|
||||
if (args) {
|
||||
if (args)
|
||||
options['params'] = serialize(args);
|
||||
}
|
||||
|
||||
// if(this.jwtToken)
|
||||
// options.headers = options.headers.set('Authorization', `Bearer ${this.jwtToken}`);
|
||||
|
||||
return this.http.get(path, options)
|
||||
.pipe(catchError(this.checkError.bind(this)));
|
||||
@@ -51,14 +72,24 @@ export class ApiService {
|
||||
}
|
||||
|
||||
private request(path: string, body: any, method = RequestMethod.Post, customHeaders?: HttpHeaders): Observable<any> {
|
||||
const req = new HttpRequest(method, path, body, {
|
||||
const options = {
|
||||
headers: customHeaders || this.headers,
|
||||
withCredentials: true
|
||||
});
|
||||
}
|
||||
|
||||
// if(this.jwtToken)
|
||||
// options.headers = options.headers.append('Authorization', `Bearer ${this.jwtToken}`);
|
||||
|
||||
const req = new HttpRequest(method, path, body, options);
|
||||
|
||||
return this.http.request(req)
|
||||
.pipe(
|
||||
filter(response => response instanceof HttpResponse),
|
||||
tap((resp: HttpResponse<any>) => {
|
||||
let jwtToken = ApiService.extractTokenFromHttpResponse(resp);
|
||||
if(jwtToken)
|
||||
this.setToken(jwtToken);
|
||||
}),
|
||||
map((response: HttpResponse<any>) => response.body),
|
||||
catchError(error => this.checkError(error))
|
||||
)
|
||||
@@ -74,4 +105,6 @@ export class ApiService {
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -21,10 +21,13 @@ export class AuthService {
|
||||
'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(
|
||||
map(() => {
|
||||
console.log("Login success");
|
||||
this.userService.getMyInfo().subscribe();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
signup(user){
|
||||
@@ -40,6 +43,7 @@ export class AuthService {
|
||||
logout() {
|
||||
return this.apiService.post(this.config.logout_url, {})
|
||||
.pipe(map(() => {
|
||||
this.apiService.setToken(null);
|
||||
this.userService.currentUser = null;
|
||||
}));
|
||||
}
|
||||
@@ -48,4 +52,5 @@ export class AuthService {
|
||||
return this.apiService.post(this.config.change_password_url, passwordChanger);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Injectable, OnInit } from '@angular/core';
|
||||
import { WebsocketService } from '.';
|
||||
import { WebsocketService, ApiService } from '.';
|
||||
import { SocketOption } from '../model/SocketOption.model';
|
||||
|
||||
Injectable()
|
||||
@Injectable()
|
||||
export class LogsWebsocketService extends WebsocketService {
|
||||
|
||||
constructor(){
|
||||
super(new SocketOption('/quartz-manager/logs', '/topic/logs'))
|
||||
constructor(private apiService: ApiService){
|
||||
super(new SocketOption('/quartz-manager/logs', '/topic/logs', apiService.getToken))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Injectable, OnInit } from '@angular/core';
|
||||
import { WebsocketService } from '.';
|
||||
import { WebsocketService, ApiService } from '.';
|
||||
import { SocketOption } from '../model/SocketOption.model';
|
||||
|
||||
Injectable()
|
||||
@Injectable()
|
||||
export class ProgressWebsocketService extends WebsocketService {
|
||||
|
||||
constructor(){
|
||||
super(new SocketOption('/quartz-manager/progress', '/topic/progress'))
|
||||
constructor(private apiService: ApiService){
|
||||
super(new SocketOption('/quartz-manager/progress', '/topic/progress', apiService.getToken))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Headers } from '@angular/http';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Headers } from '@angular/http';
|
||||
import { ApiService } from './api.service';
|
||||
import { ConfigService } from './config.service';
|
||||
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import { Injectable, OnInit } from '@angular/core';
|
||||
import { Headers } from '@angular/http';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
import { SocketEndpoint } from '../model/SocketEndpoint.model'
|
||||
|
||||
|
||||
@@ -39,10 +33,7 @@ export class WebsocketService {
|
||||
this.observableStompConnection = new Observable((observer) => {
|
||||
const subscriberIndex = this.subscriberIndex++;
|
||||
this.addToSubscribers({ index: subscriberIndex, observer });
|
||||
return () => {
|
||||
const index = subscriberIndex;
|
||||
this.removeFromSubscribers(index);
|
||||
};
|
||||
return () => this.removeFromSubscribers(subscriberIndex);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,13 +42,9 @@ export class WebsocketService {
|
||||
}
|
||||
|
||||
removeFromSubscribers = (index) => {
|
||||
let subscribeFromIndex;
|
||||
for (let i=0 ; i < this.subscribers.length; i++)
|
||||
if(i === index){
|
||||
subscribeFromIndex = this.subscribers[i];
|
||||
this.subscribers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
if(index > this.subscribers.length)
|
||||
throw new Error(`Unexpected error removing subscriber from websocket, because index ${index} is greater than subscriber length ${this.subscribers.length}`);
|
||||
this.subscribers.splice(index, 1);
|
||||
}
|
||||
|
||||
getObservable = () => {
|
||||
@@ -71,7 +58,7 @@ export class WebsocketService {
|
||||
out.headers = {};
|
||||
out.headers.messageId = data.headers["message-id"];
|
||||
|
||||
let messageIdIndex = this._messageIds.indexOf( out.headers.messageId);
|
||||
let messageIdIndex = this._messageIds.indexOf(out.headers.messageId);
|
||||
if ( messageIdIndex > -1) {
|
||||
out.self = true;
|
||||
this._messageIds = this._messageIds.splice(messageIdIndex, 1);
|
||||
@@ -81,24 +68,20 @@ export class WebsocketService {
|
||||
|
||||
_socketListener = (frame) => {
|
||||
console.log('Connected: ' + frame);
|
||||
this._socket.stomp.subscribe(this._options.topicName, (data) => {
|
||||
this.subscribers.forEach(subscriber => {
|
||||
subscriber.observer.next(this.getMessage(data));
|
||||
})
|
||||
})
|
||||
this._socket.stomp.subscribe(
|
||||
this._options.topicName,
|
||||
data => this.subscribers.forEach(subscriber => subscriber.observer.next(this.getMessage(data)))
|
||||
);
|
||||
}
|
||||
|
||||
_onSocketError = (errorMsg) => {
|
||||
let out: any = {};
|
||||
out.type = 'ERROR';
|
||||
out.message = errorMsg;
|
||||
this.subscribers.forEach(subscriber => {
|
||||
subscriber.observer.error(out);
|
||||
})
|
||||
this.subscribers.forEach(subscriber => subscriber.observer.error(out));
|
||||
this.scheduleReconnection();
|
||||
}
|
||||
|
||||
|
||||
scheduleReconnection = () => {
|
||||
this.reconnectionPromise = setTimeout(() => {
|
||||
console.log("Socket reconnecting... (if it fails, next attempt in " + this._options.reconnectionTimeout + " msec)");
|
||||
@@ -126,7 +109,12 @@ export class WebsocketService {
|
||||
|
||||
connect = () => {
|
||||
const headers = {};
|
||||
this._socket.client = new SockJS(this._options.socketUrl);
|
||||
|
||||
let socketUrl = this._options.socketUrl;
|
||||
if(this._options.getAccessToken())
|
||||
socketUrl += `?access_token=${this._options.getAccessToken()}`
|
||||
|
||||
this._socket.client = new SockJS(socketUrl);
|
||||
this._socket.stomp = Stomp.over(this._socket.client);
|
||||
this._socket.stomp.connect(headers, this._socketListener, this._onSocketError);
|
||||
this._socket.stomp.onclose = this.scheduleReconnection;
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "es2015",
|
||||
"baseUrl": "",
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"test.ts",
|
||||
"**/*.spec.ts"
|
||||
"files": [
|
||||
"main.ts",
|
||||
"polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"baseUrl": "",
|
||||
"types": [
|
||||
"jasmine",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"downlevelIteration": true,
|
||||
"importHelpers": true,
|
||||
"outDir": "./dist/out-tsc",
|
||||
"baseUrl": "src",
|
||||
@@ -9,7 +10,7 @@
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es5",
|
||||
"target": "es2015",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
@@ -17,6 +18,6 @@
|
||||
"es2016",
|
||||
"dom"
|
||||
],
|
||||
"module": "es2015"
|
||||
"module": "esnext"
|
||||
}
|
||||
}
|
||||
@@ -100,12 +100,12 @@
|
||||
|
||||
"directive-selector": [true, "attribute", "app", "camelCase"],
|
||||
"component-selector": [true, "element", "app", "kebab-case"],
|
||||
"use-input-property-decorator": true,
|
||||
"use-output-property-decorator": true,
|
||||
"use-host-property-decorator": true,
|
||||
"no-inputs-metadata-property": true,
|
||||
"no-outputs-metadata-property": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
"no-output-rename": true,
|
||||
"use-life-cycle-interface": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true,
|
||||
"component-class-suffix": true,
|
||||
"directive-class-suffix": true,
|
||||
|
||||
Reference in New Issue
Block a user