diff --git a/src/main/java/run/halo/app/config/HaloConfiguration.java b/src/main/java/run/halo/app/config/HaloConfiguration.java index 305ade484..5f2b963e6 100644 --- a/src/main/java/run/halo/app/config/HaloConfiguration.java +++ b/src/main/java/run/halo/app/config/HaloConfiguration.java @@ -1,16 +1,5 @@ package run.halo.app.config; -import run.halo.app.cache.InMemoryCacheStore; -import run.halo.app.cache.StringCacheStore; -import run.halo.app.config.properties.HaloProperties; -import run.halo.app.filter.CorsFilter; -import run.halo.app.filter.LogFilter; -import run.halo.app.security.filter.AdminAuthenticationFilter; -import run.halo.app.security.filter.ApiAuthenticationFilter; -import run.halo.app.security.handler.AdminAuthenticationFailureHandler; -import run.halo.app.security.handler.DefaultAuthenticationFailureHandler; -import run.halo.app.service.UserService; -import run.halo.app.utils.HttpClientUtils; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -23,12 +12,16 @@ import org.springframework.http.HttpMethod; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.web.client.RestTemplate; +import run.halo.app.cache.InMemoryCacheStore; +import run.halo.app.cache.StringCacheStore; +import run.halo.app.config.properties.HaloProperties; import run.halo.app.filter.CorsFilter; import run.halo.app.filter.LogFilter; import run.halo.app.security.filter.AdminAuthenticationFilter; import run.halo.app.security.filter.ApiAuthenticationFilter; import run.halo.app.security.handler.AdminAuthenticationFailureHandler; import run.halo.app.security.handler.DefaultAuthenticationFailureHandler; +import run.halo.app.service.UserService; import run.halo.app.utils.HttpClientUtils; import java.security.KeyManagementException; @@ -99,7 +92,7 @@ public class HaloConfiguration { @Bean public FilterRegistrationBean apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) { - ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(); + ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties); DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler(); failureHandler.setProductionEnv(haloProperties.getProductionEnv()); @@ -107,6 +100,7 @@ public class HaloConfiguration { // Set failure handler apiFilter.setFailureHandler(failureHandler); + apiFilter.addExcludeUrlPatterns("/api/admin/*"); FilterRegistrationBean authenticationFilter = new FilterRegistrationBean<>(); authenticationFilter.setFilter(apiFilter); diff --git a/src/main/java/run/halo/app/security/filter/AbstractAuthenticationFilter.java b/src/main/java/run/halo/app/security/filter/AbstractAuthenticationFilter.java new file mode 100644 index 000000000..1b66b5fe4 --- /dev/null +++ b/src/main/java/run/halo/app/security/filter/AbstractAuthenticationFilter.java @@ -0,0 +1,147 @@ +package run.halo.app.security.filter; + +import org.springframework.lang.NonNull; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.Assert; +import org.springframework.web.filter.OncePerRequestFilter; +import run.halo.app.config.properties.HaloProperties; +import run.halo.app.security.handler.AuthenticationFailureHandler; +import run.halo.app.security.handler.DefaultAuthenticationFailureHandler; + +import javax.servlet.http.HttpServletRequest; +import java.util.*; + +/** + * Abstract authentication filter. + * + * @author johnniang + * @date 19-4-16 + */ +public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter { + + private AuthenticationFailureHandler failureHandler; + + /** + * Exclude url patterns. + */ + private Set excludeUrlPatterns = new HashSet<>(2); + + /** + * Try authenticating url, method patterns. + */ + private Map tryAuthUrlMethodPatterns = new HashMap<>(2); + + private final AntPathMatcher antPathMatcher; + + private final HaloProperties haloProperties; + + protected AbstractAuthenticationFilter(HaloProperties haloProperties) { + this.haloProperties = haloProperties; + + antPathMatcher = new AntPathMatcher(); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + Assert.notNull(request, "Http servlet request must not be null"); + + return excludeUrlPatterns.stream().anyMatch(p -> antPathMatcher.match(p, request.getServletPath())); + } + + /** + * Should skip authentication failure. + * + * @param request http servlet request must not be null. + * @return true if the request should skip authentication failure; false otherwise + */ + protected boolean shouldSkipAuthenticateFailure(@NonNull HttpServletRequest request) { + Assert.notNull(request, "Http servlet request must not be null"); + + for (String url : tryAuthUrlMethodPatterns.keySet()) { + if (antPathMatcher.match(url, request.getServletPath()) + && tryAuthUrlMethodPatterns.get(url).equalsIgnoreCase(request.getMethod())) { + return true; + } + } + + return false; + } + + /** + * Sets exclude url patterns. + * + * @param excludeUrlPatterns exclude urls + */ + public void setExcludeUrlPatterns(@NonNull Collection excludeUrlPatterns) { + Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null"); + + this.excludeUrlPatterns = new HashSet<>(excludeUrlPatterns); + } + + /** + * Adds exclude url patterns. + * + * @param excludeUrlPatterns exclude urls + */ + public void addExcludeUrlPatterns(@NonNull String... excludeUrlPatterns) { + Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null"); + + Collections.addAll(this.excludeUrlPatterns, excludeUrlPatterns); + } + + /** + * Gets exclude url patterns. + * + * @return exclude url patterns. + */ + @NonNull + public Set getExcludeUrlPatterns() { + return excludeUrlPatterns; + } + + /** + * Adds try authenticating url method pattern. + * + * @param url url must not be blank + * @param method method must not be blank + */ + public void addTryAuthUrlMethodPattern(@NonNull String url, @NonNull String method) { + Assert.hasText(url, "Try authenticating url must not be blank"); + Assert.hasText(method, "Try authenticating method must not be blank"); + + tryAuthUrlMethodPatterns.put(url, method); + } + + + /** + * Gets authentication failure handler. (Default: @DefaultAuthenticationFailureHandler) + * + * @return authentication failure handler + */ + @NonNull + public AuthenticationFailureHandler getFailureHandler() { + if (failureHandler == null) { + synchronized (this) { + if (failureHandler == null) { + // Create default authentication failure handler + DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler(); + failureHandler.setProductionEnv(haloProperties.getProductionEnv()); + + this.failureHandler = failureHandler; + } + } + } + return failureHandler; + } + + /** + * Sets authentication failure handler. + * + * @param failureHandler authentication failure handler + */ + public void setFailureHandler(@NonNull AuthenticationFailureHandler failureHandler) { + Assert.notNull(failureHandler, "Authentication failure handler must not be null"); + + this.failureHandler = failureHandler; + } +} diff --git a/src/main/java/run/halo/app/security/filter/AdminAuthenticationFilter.java b/src/main/java/run/halo/app/security/filter/AdminAuthenticationFilter.java index a94708862..9ea1eda0a 100644 --- a/src/main/java/run/halo/app/security/filter/AdminAuthenticationFilter.java +++ b/src/main/java/run/halo/app/security/filter/AdminAuthenticationFilter.java @@ -1,5 +1,11 @@ package run.halo.app.security.filter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import run.halo.app.cache.StringCacheStore; import run.halo.app.config.properties.HaloProperties; import run.halo.app.exception.AuthenticationException; @@ -7,35 +13,17 @@ import run.halo.app.model.entity.User; import run.halo.app.security.authentication.AuthenticationImpl; import run.halo.app.security.context.SecurityContextHolder; import run.halo.app.security.context.SecurityContextImpl; -import run.halo.app.security.handler.AuthenticationFailureHandler; -import run.halo.app.security.handler.DefaultAuthenticationFailureHandler; import run.halo.app.security.support.UserDetail; import run.halo.app.service.UserService; import run.halo.app.utils.JsonUtils; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.Assert; -import org.springframework.web.filter.OncePerRequestFilter; -import run.halo.app.exception.AuthenticationException; -import run.halo.app.model.entity.User; -import run.halo.app.security.authentication.AuthenticationImpl; -import run.halo.app.security.context.SecurityContextHolder; -import run.halo.app.security.context.SecurityContextImpl; -import run.halo.app.security.handler.AuthenticationFailureHandler; -import run.halo.app.security.handler.DefaultAuthenticationFailureHandler; -import run.halo.app.security.support.UserDetail; -import run.halo.app.utils.JsonUtils; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.*; +import java.util.List; +import java.util.Optional; /** * Admin authentication filter. @@ -43,7 +31,7 @@ import java.util.*; * @author johnniang */ @Slf4j -public class AdminAuthenticationFilter extends OncePerRequestFilter { +public class AdminAuthenticationFilter extends AbstractAuthenticationFilter { /** * Admin session key. @@ -60,33 +48,19 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter { */ public final static String ADMIN_TOKEN_PARAM_NAME = "adminToken"; - private AuthenticationFailureHandler failureHandler; - private final HaloProperties haloProperties; private final StringCacheStore cacheStore; private final UserService userService; - private final AntPathMatcher antPathMatcher; - - /** - * Exclude url patterns. - */ - private Set excludeUrlPatterns = new HashSet<>(2); - - /** - * Try authenticating url, method patterns. - */ - private Map tryAuthUrlMethodPatterns = new HashMap<>(2); - public AdminAuthenticationFilter(StringCacheStore cacheStore, UserService userService, HaloProperties haloProperties) { + super(haloProperties); this.cacheStore = cacheStore; this.userService = userService; this.haloProperties = haloProperties; - antPathMatcher = new AntPathMatcher(); } @Override @@ -151,107 +125,6 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter { getFailureHandler().onFailure(request, response, new AuthenticationException("You have to login before accessing admin api")); } - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - Assert.notNull(request, "Http servlet request must not be null"); - - return excludeUrlPatterns.stream().anyMatch(p -> antPathMatcher.match(p, request.getServletPath())); - } - - /** - * Should skip authentication failure. - * - * @param request http servlet request must not be null. - * @return true if the request should skip authentication failure; false otherwise - */ - protected boolean shouldSkipAuthenticateFailure(@NonNull HttpServletRequest request) { - Assert.notNull(request, "Http servlet request must not be null"); - - for (String url : tryAuthUrlMethodPatterns.keySet()) { - if (antPathMatcher.match(url, request.getServletPath()) - && tryAuthUrlMethodPatterns.get(url).equalsIgnoreCase(request.getMethod())) { - return true; - } - } - - return false; - } - - /** - * Gets authentication failure handler. (Default: @DefaultAuthenticationFailureHandler) - * - * @return authentication failure handler - */ - public AuthenticationFailureHandler getFailureHandler() { - if (failureHandler == null) { - synchronized (this) { - if (failureHandler == null) { - // Create default authentication failure handler - DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler(); - failureHandler.setProductionEnv(haloProperties.getProductionEnv()); - - this.failureHandler = failureHandler; - } - } - } - return failureHandler; - } - - /** - * Sets authentication failure handler. - * - * @param failureHandler authentication failure handler - */ - public void setFailureHandler(@NonNull AuthenticationFailureHandler failureHandler) { - Assert.notNull(failureHandler, "Authentication failure handler must not be null"); - - this.failureHandler = failureHandler; - } - - /** - * Sets exclude url patterns. - * - * @param excludeUrlPatterns exclude urls - */ - public void setExcludeUrlPatterns(@NonNull Collection excludeUrlPatterns) { - Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null"); - - this.excludeUrlPatterns = new HashSet<>(excludeUrlPatterns); - } - - /** - * Adds exclude url patterns. - * - * @param excludeUrlPatterns exclude urls - */ - public void addExcludeUrlPatterns(@NonNull String... excludeUrlPatterns) { - Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null"); - - Collections.addAll(this.excludeUrlPatterns, excludeUrlPatterns); - } - - /** - * Gets exclude url patterns. - * - * @return exclude url patterns. - */ - public Set getExcludeUrlPatterns() { - return excludeUrlPatterns; - } - - /** - * Adds try authenticating url method pattern. - * - * @param url url must not be blank - * @param method method must not be blank - */ - public void addTryAuthUrlMethodPattern(@NonNull String url, @NonNull String method) { - Assert.hasText(url, "Try authenticating url must not be blank"); - Assert.hasText(method, "Try authenticating method must not be blank"); - - tryAuthUrlMethodPatterns.put(url, method); - } - /** * Gets token from request. * diff --git a/src/main/java/run/halo/app/security/filter/ApiAuthenticationFilter.java b/src/main/java/run/halo/app/security/filter/ApiAuthenticationFilter.java index d9cdc9d8c..16f97847c 100644 --- a/src/main/java/run/halo/app/security/filter/ApiAuthenticationFilter.java +++ b/src/main/java/run/halo/app/security/filter/ApiAuthenticationFilter.java @@ -1,30 +1,33 @@ package run.halo.app.security.filter; -import run.halo.app.security.handler.AuthenticationFailureHandler; -import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.util.AntPathMatcher; +import run.halo.app.config.properties.HaloProperties; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.HashSet; +import java.util.Set; /** * Api authentication Filter * * @author johnniang */ -public class ApiAuthenticationFilter extends OncePerRequestFilter { +public class ApiAuthenticationFilter extends AbstractAuthenticationFilter { - private AuthenticationFailureHandler failureHandler; + private final AntPathMatcher antPathMatcher; + + public ApiAuthenticationFilter(HaloProperties haloProperties) { + super(haloProperties); + antPathMatcher = new AntPathMatcher(); + } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // TODO Handle authentication filterChain.doFilter(request, response); } - - public void setFailureHandler(AuthenticationFailureHandler failureHandler) { - this.failureHandler = failureHandler; - } }