SecurityConfig.java
package com.nonononoki.alovoa.config;
import com.nonononoki.alovoa.component.*;
import lombok.RequiredArgsConstructor;
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.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
import org.springframework.web.cors.CorsConfiguration;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
@Value("${app.text.key}")
private String key;
@Value("${app.login.remember.key}")
private String rememberKey;
@Value("${app.url.front-end}")
private String urlFrontEnd;
@Value("${app.domain}")
private String domain;
@Autowired
private Environment env;
@Autowired
private AuthFailureHandler failureHandler;
@Autowired
private CustomUserDetailsService customUserDetailsService;
private final AuthenticationConfiguration configuration;
public static final String ROLE_USER = "ROLE_USER";
public static final String ROLE_ADMIN = "ROLE_ADMIN";
public static final String COOKIE_SESSION = "JSESSIONID";
public static final String COOKIE_REMEMBER = "remember-me";
public static String getRoleUser() {
return ROLE_USER;
}
public static String getRoleAdmin() {
return ROLE_ADMIN;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http
.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(authProvider());
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin").hasAnyAuthority(ROLE_ADMIN)
.requestMatchers("/admin/**").hasAnyAuthority(ROLE_ADMIN)
.requestMatchers("/css/**").permitAll()
.requestMatchers("/js/**").permitAll()
.requestMatchers("/img/**").permitAll()
.requestMatchers("/font/**").permitAll()
.requestMatchers("/json/**").permitAll()
.requestMatchers("/oauth2/**").permitAll()
.requestMatchers("/").permitAll()
.requestMatchers("/login/**").permitAll()
.requestMatchers("/terms-conditions").permitAll()
.requestMatchers("/imprint").permitAll()
.requestMatchers("/imprint/*").permitAll()
.requestMatchers("/privacy").permitAll()
.requestMatchers("/faq").permitAll()
.requestMatchers("/tos").permitAll()
.requestMatchers("/register").permitAll()
.requestMatchers("/register/**").permitAll()
.requestMatchers("/captcha/**").permitAll()
.requestMatchers("/donate-list").permitAll()
.requestMatchers("/donate/received/**").permitAll()
.requestMatchers("/password/**").permitAll()
.requestMatchers("/favicon.ico").permitAll()
.requestMatchers("/sw.js").permitAll()
.requestMatchers("/robots.txt").permitAll()
.requestMatchers("/.well-known/assetlinks.json").permitAll()
.requestMatchers("/text/*").permitAll()
.requestMatchers("/manifest/**").permitAll()
.requestMatchers("/fonts/**").permitAll()
.requestMatchers("/error").permitAll()
.requestMatchers("/info").permitAll()
.requestMatchers("/user/delete-account-confirm").permitAll()
.requestMatchers("/delete-account/*").permitAll()
.requestMatchers("/media/*").permitAll()
.anyRequest().authenticated()
)
.csrf(AbstractHttpConfigurer::disable)
.formLogin(formLogin -> formLogin
.loginPage("/login")
.permitAll()
).logout(logout ->
logout.deleteCookies("remove")
.invalidateHttpSession(true)
.deleteCookies(COOKIE_SESSION, COOKIE_REMEMBER)
.logoutUrl("/logout")
.logoutSuccessUrl("/?logout")
).oauth2Login(login -> login.loginPage("/?auth-error").defaultSuccessUrl("/login/oauth2/success"))
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.rememberMe(remember -> remember.rememberMeServices(oAuthRememberMeServices()).key(rememberKey))
.sessionManagement(session -> session.maximumSessions(10).expiredSessionStrategy(getSessionInformationExpiredStrategy())
.sessionRegistry(sessionRegistry()))
.cors(cors -> cors.configurationSource(request -> {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(List.of(domain, urlFrontEnd));
configuration.setAllowedMethods(List.of("*"));
configuration.setAllowedHeaders(List.of("*"));
return configuration;
}))
.securityContext((securityContext) -> securityContext.requireExplicitSave(false));
if (env.acceptsProfiles(Profiles.of("prod"))) {
http.requiresChannel(channel -> channel.anyRequest().requiresSecure());
}
return http.build();
}
@Bean
AuthenticationManager authenticationManager() throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
AuthSuccessHandler successHandler() {
return new AuthSuccessHandler(this);
}
@Bean
AuthFilter authenticationFilter() throws Exception {
AuthFilter filter = new AuthFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
filter.setAuthenticationFailureHandler(failureHandler);
filter.setRememberMeServices(rememberMeServices());
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
return filter;
}
// https://stackoverflow.com/questions/32463022/sessionregistry-is-empty-when-i-use-concurrentsessioncontrolauthenticationstrate
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
List<SessionAuthenticationStrategy> stratList = new ArrayList<>();
SessionFixationProtectionStrategy concStrat = new SessionFixationProtectionStrategy();
stratList.add(concStrat);
RegisterSessionAuthenticationStrategy regStrat = new RegisterSessionAuthenticationStrategy(sessionRegistry());
stratList.add(regStrat);
return new CompositeSessionAuthenticationStrategy(stratList);
}
public SessionInformationExpiredStrategy getSessionInformationExpiredStrategy() {
return new SimpleRedirectSessionInformationExpiredStrategy("/logout");
}
@Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
TokenBasedRememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices(rememberKey, customUserDetailsService);
}
@Bean
TokenBasedRememberMeServices oAuthRememberMeServices() {
CustomTokenBasedRememberMeServices rememberMeService = new CustomTokenBasedRememberMeServices(rememberKey,
customUserDetailsService);
rememberMeService.setAlwaysRemember(true);
return rememberMeService;
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
AuthProvider authProvider() {
return new AuthProvider();
}
public CustomTokenBasedRememberMeServices getOAuthRememberMeServices() {
return (CustomTokenBasedRememberMeServices) oAuthRememberMeServices();
}
}