From 9faf43a6c4b933b5f97be4348d461bf91f5bf2e2 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Mon, 24 Oct 2022 01:09:32 +0300 Subject: Spring Boot 3 --- .../config/ActivityPubClientErrorHandler.java | 3 +- .../java/com/juick/config/ActivityPubConfig.java | 3 +- .../java/com/juick/config/HttpClientConfig.java | 51 +++-- src/main/java/com/juick/config/SecurityConfig.java | 236 +++++++++------------ src/main/java/com/juick/config/WebConfig.java | 13 +- 5 files changed, 140 insertions(+), 166 deletions(-) (limited to 'src/main/java/com/juick/config') diff --git a/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java b/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java index ecb81e30..69958bbd 100644 --- a/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java +++ b/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java @@ -28,7 +28,6 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.client.DefaultResponseErrorHandler; -import javax.annotation.Nonnull; import javax.inject.Inject; import java.io.IOException; import java.net.URI; @@ -41,7 +40,7 @@ public class ActivityPubClientErrorHandler extends DefaultResponseErrorHandler { @Inject private ApplicationEventPublisher applicationEventPublisher; @Override - public void handleError(URI contextUri, HttpMethod method, @Nonnull ClientHttpResponse response) throws IOException { + public void handleError(URI contextUri, HttpMethod method, ClientHttpResponse response) throws IOException { logger.warn("HTTP ERROR {} {} : {}", response.getStatusCode().value(), response.getStatusText(), IOUtils.toString(response.getBody(), StandardCharsets.UTF_8)); if (response.getStatusCode().equals(HttpStatus.GONE)) { diff --git a/src/main/java/com/juick/config/ActivityPubConfig.java b/src/main/java/com/juick/config/ActivityPubConfig.java index 89aacff0..fb204aaa 100644 --- a/src/main/java/com/juick/config/ActivityPubConfig.java +++ b/src/main/java/com/juick/config/ActivityPubConfig.java @@ -20,7 +20,8 @@ package com.juick.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.juick.ActivityPubManager; import com.juick.util.ActivityPubRequestInterceptor; -import org.apache.http.impl.client.CloseableHttpClient; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; diff --git a/src/main/java/com/juick/config/HttpClientConfig.java b/src/main/java/com/juick/config/HttpClientConfig.java index 22c06ecb..99351828 100644 --- a/src/main/java/com/juick/config/HttpClientConfig.java +++ b/src/main/java/com/juick/config/HttpClientConfig.java @@ -1,20 +1,19 @@ package com.juick.config; import java.util.concurrent.TimeUnit; - -import org.apache.http.HeaderElement; -import org.apache.http.HeaderElementIterator; -import org.apache.http.HttpResponse; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.conn.ConnectionKeepAliveStrategy; - -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicHeaderElementIterator; -import org.apache.http.protocol.HTTP; -import org.apache.http.protocol.HttpContext; + +import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.cookie.StandardCookieSpec; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.core5.http.HeaderElement; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.message.BasicHeaderElementIterator; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.util.TimeValue; +import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; @@ -55,19 +54,19 @@ public class HttpClientConfig { public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return new ConnectionKeepAliveStrategy() { @Override - public long getKeepAliveDuration(HttpResponse response, HttpContext context) { - HeaderElementIterator it = new BasicHeaderElementIterator - (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); + public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context) { + BasicHeaderElementIterator it = new BasicHeaderElementIterator + (response.headerIterator("Keep-Alive")); while (it.hasNext()) { - HeaderElement he = it.nextElement(); + HeaderElement he = it.next(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { - return Long.parseLong(value) * 1000; + return TimeValue.of(Long.parseLong(value) * 1000, TimeUnit.MILLISECONDS); } } - return DEFAULT_KEEP_ALIVE_TIME_MILLIS; + return TimeValue.of(DEFAULT_KEEP_ALIVE_TIME_MILLIS, TimeUnit.MILLISECONDS); } }; } @@ -75,10 +74,10 @@ public class HttpClientConfig { @Bean public CloseableHttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() - .setCookieSpec(CookieSpecs.IGNORE_COOKIES) - .setConnectionRequestTimeout(REQUEST_TIMEOUT) - .setConnectTimeout(CONNECT_TIMEOUT) - .setSocketTimeout(SOCKET_TIMEOUT).build(); + .setCookieSpec(StandardCookieSpec.IGNORE) + .setConnectionRequestTimeout(Timeout.of(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS)) + .setConnectTimeout(Timeout.of(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)) + .setResponseTimeout(Timeout.of(SOCKET_TIMEOUT, TimeUnit.MILLISECONDS)).build(); return HttpClients.custom() .setDefaultRequestConfig(requestConfig) @@ -96,8 +95,8 @@ public class HttpClientConfig { try { if (connectionManager != null) { LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections..."); - connectionManager.closeExpiredConnections(); - connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS); + connectionManager.closeExpired(); + connectionManager.closeIdle(TimeValue.of(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS)); } else { LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised"); } diff --git a/src/main/java/com/juick/config/SecurityConfig.java b/src/main/java/com/juick/config/SecurityConfig.java index 1907c7c5..b5c93906 100644 --- a/src/main/java/com/juick/config/SecurityConfig.java +++ b/src/main/java/com/juick/config/SecurityConfig.java @@ -23,20 +23,17 @@ import com.juick.service.security.HTTPSignatureAuthenticationFilter; import com.juick.service.security.HashParamAuthenticationFilter; import com.juick.service.security.JuickUserDetailsService; import com.juick.service.security.entities.JuickUser; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; 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.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.NullRememberMeServices; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; @@ -46,18 +43,18 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import javax.annotation.Resource; -import javax.inject.Inject; import java.util.Arrays; import java.util.Collections; -import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; /** * Created by aalexeev on 11/21/16. */ @EnableWebSecurity +@Configuration public class SecurityConfig { - @Resource + @Inject private UserService userService; private static final String COOKIE_NAME = "juick-remember-me"; @@ -66,6 +63,7 @@ public class SecurityConfig { public UserDetailsService userDetailsService() { return new JuickUserDetailsService(userService); } + @Bean static CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); @@ -81,132 +79,106 @@ public class SecurityConfig { return source; } - @Configuration + @Inject + private SignatureManager signatureManager; + + @Bean + public HashParamAuthenticationFilter apiAuthenticationFilter() { + return new HashParamAuthenticationFilter(userService, null); + } + + @Bean + public AuthenticationEntryPoint juickAuthenticationEntryPoint() { + var entryPoint = new BasicAuthenticationEntryPoint(); + entryPoint.setRealmName("Juick"); + return entryPoint; + } + + @Value("${auth_remember_me_key:secret}") + private String rememberMeKey; + @Value("${web_domain:localhost}") + private String webDomain; + + @Bean + public HashParamAuthenticationFilter wwwAuthenticationFilter() { + return new HashParamAuthenticationFilter(userService, hashCookieServices()); + } + + @Bean + public RememberMeServices hashCookieServices() { + TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( + rememberMeKey, userDetailsService()); + + services.setCookieName(COOKIE_NAME); + services.setCookieDomain(webDomain); + services.setAlwaysRemember(true); + services.setTokenValiditySeconds(6 * 30 * 24 * 3600); + services.setUseSecureCookie(false); // TODO set true if https is supports + return services; + } + + @Bean @Order(1) - public static class ApiConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Resource - private UserService userService; - @Resource - private SignatureManager signatureManager; - ApiConfig() { - super(true); - } - @Bean - RememberMeServices apiTokenServices() { - return new NullRememberMeServices(); - } - @Bean - public HashParamAuthenticationFilter apiAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, apiTokenServices()); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/api/**") - .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) - .addFilterBefore(new HTTPSignatureAuthenticationFilter(signatureManager, userService), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS).permitAll() - .antMatchers("/api/", "/api/messages", "/api/avatar", "/api/messages/discussions", - "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", - "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", - "/api/_google", "/api/_applelogin", "/api/signup", "/api/inbox", "/api/events", "/api/info/**", - "/api/nodeinfo/2.0").permitAll() - .anyRequest().hasRole("USER") - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and() - .httpBasic().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and().cors().configurationSource(corsConfigurationSource()) - .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and().exceptionHandling().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and() - .rememberMe() - .alwaysRemember(true) - .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(6 * 30)) - .rememberMeServices(apiTokenServices()) - .key(rememberMeKey) - .and() - .headers().defaultsDisabled().cacheControl(); - } - - @Bean - public AuthenticationEntryPoint juickAuthenticationEntryPoint() { - var entryPoint = new BasicAuthenticationEntryPoint(); - entryPoint.setRealmName("Juick"); - return entryPoint; - } + public SecurityFilterChain apiChain(HttpSecurity http) throws Exception { + http.securityMatcher("/api/**") + .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) + .addFilterBefore(new HTTPSignatureAuthenticationFilter(signatureManager, userService), + BasicAuthenticationFilter.class) + .authorizeHttpRequests(requests -> requests + .requestMatchers(HttpMethod.OPTIONS).permitAll() + .requestMatchers("/api/", "/api/messages", "/api/avatar", "/api/messages/discussions", + "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", + "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", + "/api/_google", "/api/_applelogin", "/api/signup", "/api/inbox", "/api/events", + "/api/info/**", + "/api/nodeinfo/2.0") + .permitAll() + .anyRequest().hasRole("USER")) + .anonymous(anonymous -> anonymous.principal(JuickUser.ANONYMOUS_USER) + .authorities(JuickUser.ANONYMOUS_AUTHORITY)) + .httpBasic(httpBasic -> httpBasic.authenticationEntryPoint(juickAuthenticationEntryPoint())) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .exceptionHandling(exceptionHandling -> exceptionHandling.authenticationEntryPoint(juickAuthenticationEntryPoint())) + .csrf().disable() + .headers().defaultsDisabled().cacheControl(); + return http.build(); } - @Configuration - public static class WebConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - @Resource - private UserService userService; - @Inject - private UserDetailsService userDetailsService; - @Bean - @Qualifier("www") - public HashParamAuthenticationFilter wwwAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, hashCookieServices()); - } - @Bean - @Qualifier("www") - public RememberMeServices hashCookieServices() { - TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( - rememberMeKey, userDetailsService); - - services.setCookieName(COOKIE_NAME); - services.setCookieDomain(webDomain); - services.setAlwaysRemember(true); - services.setTokenValiditySeconds(6 * 30 * 24 * 3600); - services.setUseSecureCookie(false); // TODO set true if https is supports - - return services; - } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment") - .authenticated() - .antMatchers("/actuator/**").hasRole("ADMIN") - .anyRequest().permitAll() - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and().cors().configurationSource(corsConfigurationSource()) - .and().sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .invalidSessionUrl("/") - .and() - .logout() - .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) - .invalidateHttpSession(true) - .logoutUrl("/logout") - .logoutSuccessUrl("/") - .deleteCookies("hash", COOKIE_NAME) - .and() - .formLogin() - .loginPage("/login") - .permitAll() - .defaultSuccessUrl("/") - .loginProcessingUrl("/login") - .usernameParameter("username") - .passwordParameter("password") - .failureUrl("/login?error=1") - .and() - .rememberMe() - .rememberMeCookieDomain(webDomain).key(rememberMeKey) - .rememberMeServices(hashCookieServices()) - .and() - .csrf().disable() - .headers().defaultsDisabled().cacheControl(); - } + @Bean + public SecurityFilterChain wwwChain(HttpSecurity http) throws Exception { + http.addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class) + .authorizeHttpRequests(authorize -> authorize + .requestMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", + "/comment") + .authenticated() + .requestMatchers("/actuator/**").hasRole("ADMIN") + .anyRequest().permitAll()) + .anonymous(anonymous -> anonymous.principal(JuickUser.ANONYMOUS_USER) + .authorities(JuickUser.ANONYMOUS_AUTHORITY)) + .cors(cors -> cors + .configurationSource(corsConfigurationSource())) + .sessionManagement( + sessionManagement -> sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .invalidSessionUrl("/")) + .logout(logout -> logout + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .invalidateHttpSession(true) + .logoutSuccessUrl("/") + .deleteCookies("hash", COOKIE_NAME)) + .formLogin(form -> form.loginPage("/login") + .defaultSuccessUrl("/") + .loginProcessingUrl("/login") + .usernameParameter("username") + .passwordParameter("password") + .failureUrl("/login?error=1") + .permitAll()) + .csrf(csrf -> csrf.ignoringRequestMatchers("/settings/unsubscribe")) + .rememberMe(rememberMe -> rememberMe + .rememberMeCookieDomain(webDomain).key(rememberMeKey) + .rememberMeServices(hashCookieServices())) + .headers().defaultsDisabled().cacheControl(); + return http.build(); } } diff --git a/src/main/java/com/juick/config/WebConfig.java b/src/main/java/com/juick/config/WebConfig.java index 59f9a99c..bfebd77f 100644 --- a/src/main/java/com/juick/config/WebConfig.java +++ b/src/main/java/com/juick/config/WebConfig.java @@ -23,14 +23,16 @@ import com.juick.service.HelpService; import com.juick.service.StorageService; import com.juick.service.FileSystemStorageService; import com.juick.service.UserService; -import com.mitchellbosecke.pebble.PebbleEngine; import com.mitchellbosecke.pebble.extension.FormatterExtension; -import com.mitchellbosecke.pebble.loader.ClasspathLoader; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.spring.extension.SpringExtension; -import com.mitchellbosecke.pebble.spring.servlet.PebbleViewResolver; import com.overzealous.remark.Options; import com.overzealous.remark.Remark; + +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.loader.ClasspathLoader; +import io.pebbletemplates.pebble.loader.Loader; +import io.pebbletemplates.spring.extension.SpringExtension; +import io.pebbletemplates.spring.servlet.PebbleViewResolver; + import org.apache.commons.codec.CharEncoding; import org.commonmark.ext.autolink.AutolinkExtension; import org.commonmark.node.Link; @@ -175,6 +177,7 @@ public class WebConfig implements WebMvcConfigurer { viewResolver.setPrefix("templates"); viewResolver.setSuffix(".html"); viewResolver.setCharacterEncoding(CharEncoding.UTF_8); + viewResolver.setExposeRequestAttributes(true); return viewResolver; } @Override -- cgit v1.2.3