mirror of https://github.com/halo-dev/halo
fix: a fatal filter bug (#626)
parent
fd251a4724
commit
55a9d573bc
|
@ -6,10 +6,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
@ -17,17 +15,6 @@ import run.halo.app.cache.InMemoryCacheStore;
|
|||
import run.halo.app.cache.LevelCacheStore;
|
||||
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.filter.ContentFilter;
|
||||
import run.halo.app.security.handler.ContentAuthenticationFailureHandler;
|
||||
import run.halo.app.security.handler.DefaultAuthenticationFailureHandler;
|
||||
import run.halo.app.security.service.OneTimeTokenService;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
import run.halo.app.utils.HttpClientUtils;
|
||||
|
||||
import java.security.KeyManagementException;
|
||||
|
@ -83,123 +70,4 @@ public class HaloConfiguration {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CorsFilter.
|
||||
*
|
||||
* @return Cors filter registration bean
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
||||
FilterRegistrationBean<CorsFilter> corsFilter = new FilterRegistrationBean<>();
|
||||
|
||||
corsFilter.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
|
||||
corsFilter.setFilter(new CorsFilter());
|
||||
corsFilter.addUrlPatterns("/api/*");
|
||||
|
||||
return corsFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LogFilter.
|
||||
*
|
||||
* @return Log filter registration bean
|
||||
*/
|
||||
public FilterRegistrationBean<LogFilter> logFilter() {
|
||||
FilterRegistrationBean<LogFilter> logFilter = new FilterRegistrationBean<>();
|
||||
|
||||
logFilter.setOrder(Ordered.HIGHEST_PRECEDENCE + 9);
|
||||
logFilter.setFilter(new LogFilter());
|
||||
logFilter.addUrlPatterns("/*");
|
||||
|
||||
return logFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<ContentFilter> contentFilter(HaloProperties haloProperties,
|
||||
OptionService optionService,
|
||||
StringCacheStore cacheStore,
|
||||
OneTimeTokenService oneTimeTokenService) {
|
||||
ContentFilter contentFilter = new ContentFilter(haloProperties, optionService, cacheStore, oneTimeTokenService);
|
||||
contentFilter.setFailureHandler(new ContentAuthenticationFailureHandler());
|
||||
|
||||
String adminPattern = HaloUtils.ensureBoth(haloProperties.getAdminPath(), "/") + "**";
|
||||
|
||||
contentFilter.addExcludeUrlPatterns(
|
||||
adminPattern,
|
||||
"/api/**",
|
||||
"/install",
|
||||
"/version",
|
||||
"/js/**",
|
||||
"/css/**");
|
||||
|
||||
FilterRegistrationBean<ContentFilter> contentFrb = new FilterRegistrationBean<>();
|
||||
contentFrb.addUrlPatterns("/*");
|
||||
contentFrb.setFilter(contentFilter);
|
||||
contentFrb.setOrder(-1);
|
||||
|
||||
return contentFrb;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties,
|
||||
ObjectMapper objectMapper,
|
||||
OptionService optionService,
|
||||
StringCacheStore cacheStore,
|
||||
OneTimeTokenService oneTimeTokenService) {
|
||||
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties, optionService, cacheStore, oneTimeTokenService);
|
||||
apiFilter.addExcludeUrlPatterns(
|
||||
"/api/content/*/comments",
|
||||
"/api/content/**/comments/**",
|
||||
"/api/content/options/comment"
|
||||
);
|
||||
|
||||
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
|
||||
failureHandler.setProductionEnv(haloProperties.isProductionEnv());
|
||||
failureHandler.setObjectMapper(objectMapper);
|
||||
|
||||
// Set failure handler
|
||||
apiFilter.setFailureHandler(failureHandler);
|
||||
|
||||
FilterRegistrationBean<ApiAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
|
||||
authenticationFilter.setFilter(apiFilter);
|
||||
authenticationFilter.addUrlPatterns("/api/content/*");
|
||||
authenticationFilter.setOrder(0);
|
||||
|
||||
return authenticationFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<AdminAuthenticationFilter> adminAuthenticationFilter(StringCacheStore cacheStore,
|
||||
UserService userService,
|
||||
HaloProperties haloProperties,
|
||||
ObjectMapper objectMapper,
|
||||
OptionService optionService,
|
||||
OneTimeTokenService oneTimeTokenService) {
|
||||
AdminAuthenticationFilter adminAuthenticationFilter = new AdminAuthenticationFilter(cacheStore, userService,
|
||||
haloProperties, optionService, oneTimeTokenService);
|
||||
|
||||
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
|
||||
failureHandler.setProductionEnv(haloProperties.isProductionEnv());
|
||||
failureHandler.setObjectMapper(objectMapper);
|
||||
|
||||
// Config the admin filter
|
||||
adminAuthenticationFilter.addExcludeUrlPatterns(
|
||||
"/api/admin/login",
|
||||
"/api/admin/refresh/*",
|
||||
"/api/admin/installations",
|
||||
"/api/admin/recoveries/migrations/*",
|
||||
"/api/admin/migrations/*",
|
||||
"/api/admin/is_installed",
|
||||
"/api/admin/password/code",
|
||||
"/api/admin/password/reset"
|
||||
);
|
||||
adminAuthenticationFilter.setFailureHandler(failureHandler);
|
||||
|
||||
FilterRegistrationBean<AdminAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
|
||||
authenticationFilter.setFilter(adminAuthenticationFilter);
|
||||
authenticationFilter.addUrlPatterns("/api/admin/*", "/api/content/comments");
|
||||
authenticationFilter.setOrder(1);
|
||||
|
||||
return authenticationFilter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package run.halo.app.filter;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
|
@ -21,6 +24,8 @@ import static run.halo.app.model.support.HaloConst.API_ACCESS_KEY_HEADER_NAME;
|
|||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
|
||||
public class CorsFilter extends GenericFilterBean {
|
||||
|
||||
private final static String ALLOW_HEADERS = StringUtils.joinWith(",", HttpHeaders.CONTENT_TYPE, ADMIN_TOKEN_HEADER_NAME, API_ACCESS_KEY_HEADER_NAME);
|
||||
|
|
|
@ -2,6 +2,9 @@ package run.halo.app.filter;
|
|||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
|
@ -16,6 +19,8 @@ import java.io.IOException;
|
|||
* @author johnniang
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 9)
|
||||
public class LogFilter extends OncePerRequestFilter {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
import run.halo.app.cache.StringCacheStore;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.exception.BadRequestException;
|
||||
|
@ -26,10 +27,7 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static run.halo.app.model.support.HaloConst.ONE_TIME_TOKEN_HEADER_NAME;
|
||||
import static run.halo.app.model.support.HaloConst.ONE_TIME_TOKEN_QUERY_NAME;
|
||||
|
@ -45,6 +43,8 @@ public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter
|
|||
|
||||
protected final AntPathMatcher antPathMatcher;
|
||||
|
||||
private final UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
protected final HaloProperties haloProperties;
|
||||
|
||||
protected final OptionService optionService;
|
||||
|
@ -59,6 +59,9 @@ public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter
|
|||
*/
|
||||
private Set<String> excludeUrlPatterns = new HashSet<>(16);
|
||||
|
||||
private Set<String> urlPatterns = new LinkedHashSet<>();
|
||||
|
||||
|
||||
AbstractAuthenticationFilter(HaloProperties haloProperties,
|
||||
OptionService optionService,
|
||||
StringCacheStore cacheStore,
|
||||
|
@ -86,7 +89,11 @@ public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter
|
|||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
Assert.notNull(request, "Http servlet request must not be null");
|
||||
|
||||
return excludeUrlPatterns.stream().anyMatch(p -> antPathMatcher.match(p, request.getRequestURI()));
|
||||
// check white list
|
||||
boolean result = excludeUrlPatterns.stream().anyMatch(p -> antPathMatcher.match(p, urlPathHelper.getRequestUri(request)));
|
||||
|
||||
return result || urlPatterns.stream().noneMatch(p -> antPathMatcher.match(p, urlPathHelper.getRequestUri(request)));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,6 +128,20 @@ public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter
|
|||
this.excludeUrlPatterns = new HashSet<>(excludeUrlPatterns);
|
||||
}
|
||||
|
||||
public void setUrlPatterns(Collection<String> urlPatterns) {
|
||||
Assert.notNull(urlPatterns, "UrlPatterns must not be null");
|
||||
this.urlPatterns = new LinkedHashSet<>(urlPatterns);
|
||||
}
|
||||
|
||||
public Collection<String> getUrlPatterns() {
|
||||
return this.urlPatterns;
|
||||
}
|
||||
|
||||
public void addUrlPatterns(String... urlPatterns) {
|
||||
Assert.notNull(urlPatterns, "UrlPatterns must not be null");
|
||||
Collections.addAll(this.urlPatterns, urlPatterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets authentication failure handler. (Default: @DefaultAuthenticationFailureHandler)
|
||||
*
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package run.halo.app.security.filter;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.cache.StringCacheStore;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.exception.AuthenticationException;
|
||||
|
@ -10,6 +13,7 @@ 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.DefaultAuthenticationFailureHandler;
|
||||
import run.halo.app.security.service.OneTimeTokenService;
|
||||
import run.halo.app.security.support.UserDetail;
|
||||
import run.halo.app.security.util.SecurityUtils;
|
||||
|
@ -32,6 +36,8 @@ import static run.halo.app.model.support.HaloConst.ADMIN_TOKEN_QUERY_NAME;
|
|||
* @author johnniang
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(1)
|
||||
public class AdminAuthenticationFilter extends AbstractAuthenticationFilter {
|
||||
|
||||
private final HaloProperties haloProperties;
|
||||
|
@ -42,10 +48,32 @@ public class AdminAuthenticationFilter extends AbstractAuthenticationFilter {
|
|||
UserService userService,
|
||||
HaloProperties haloProperties,
|
||||
OptionService optionService,
|
||||
OneTimeTokenService oneTimeTokenService) {
|
||||
OneTimeTokenService oneTimeTokenService,
|
||||
ObjectMapper objectMapper) {
|
||||
super(haloProperties, optionService, cacheStore, oneTimeTokenService);
|
||||
this.userService = userService;
|
||||
this.haloProperties = haloProperties;
|
||||
|
||||
addUrlPatterns("/api/admin/**", "/api/content/comments");
|
||||
|
||||
addExcludeUrlPatterns(
|
||||
"/api/admin/login",
|
||||
"/api/admin/refresh/*",
|
||||
"/api/admin/installations",
|
||||
"/api/admin/recoveries/migrations/**",
|
||||
"/api/admin/migrations/**",
|
||||
"/api/admin/is_installed",
|
||||
"/api/admin/password/code",
|
||||
"/api/admin/password/reset"
|
||||
);
|
||||
|
||||
// set failure handler
|
||||
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
|
||||
failureHandler.setProductionEnv(haloProperties.isProductionEnv());
|
||||
failureHandler.setObjectMapper(objectMapper);
|
||||
|
||||
setFailureHandler(failureHandler);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package run.halo.app.security.filter;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.cache.StringCacheStore;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.exception.AuthenticationException;
|
||||
import run.halo.app.exception.ForbiddenException;
|
||||
import run.halo.app.model.properties.ApiProperties;
|
||||
import run.halo.app.model.properties.CommentProperties;
|
||||
import run.halo.app.security.handler.DefaultAuthenticationFailureHandler;
|
||||
import run.halo.app.security.service.OneTimeTokenService;
|
||||
import run.halo.app.service.OptionService;
|
||||
|
||||
|
@ -28,6 +32,8 @@ import static run.halo.app.model.support.HaloConst.API_ACCESS_KEY_QUERY_NAME;
|
|||
* @author johnniang
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(0)
|
||||
public class ApiAuthenticationFilter extends AbstractAuthenticationFilter {
|
||||
|
||||
private final OptionService optionService;
|
||||
|
@ -35,14 +41,28 @@ public class ApiAuthenticationFilter extends AbstractAuthenticationFilter {
|
|||
public ApiAuthenticationFilter(HaloProperties haloProperties,
|
||||
OptionService optionService,
|
||||
StringCacheStore cacheStore,
|
||||
OneTimeTokenService oneTimeTokenService) {
|
||||
OneTimeTokenService oneTimeTokenService,
|
||||
ObjectMapper objectMapper) {
|
||||
super(haloProperties, optionService, cacheStore, oneTimeTokenService);
|
||||
this.optionService = optionService;
|
||||
|
||||
addUrlPatterns("/api/content/**");
|
||||
|
||||
addExcludeUrlPatterns(
|
||||
"/api/content/**/comments",
|
||||
"/api/content/**/comments/**",
|
||||
"/api/content/options/comment"
|
||||
);
|
||||
|
||||
// set failure handler
|
||||
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
|
||||
failureHandler.setProductionEnv(haloProperties.isProductionEnv());
|
||||
failureHandler.setObjectMapper(objectMapper);
|
||||
setFailureHandler(failureHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAuthenticate(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
if (!haloProperties.isAuthEnabled()) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package run.halo.app.security.filter;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.cache.StringCacheStore;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.security.handler.ContentAuthenticationFailureHandler;
|
||||
import run.halo.app.security.service.OneTimeTokenService;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -17,6 +21,8 @@ import java.io.IOException;
|
|||
* @author johnniang
|
||||
* @date 19-5-6
|
||||
*/
|
||||
@Component
|
||||
@Order(-1)
|
||||
public class ContentFilter extends AbstractAuthenticationFilter {
|
||||
|
||||
public ContentFilter(HaloProperties haloProperties,
|
||||
|
@ -24,6 +30,18 @@ public class ContentFilter extends AbstractAuthenticationFilter {
|
|||
StringCacheStore cacheStore,
|
||||
OneTimeTokenService oneTimeTokenService) {
|
||||
super(haloProperties, optionService, cacheStore, oneTimeTokenService);
|
||||
|
||||
String adminPattern = HaloUtils.ensureBoth(haloProperties.getAdminPath(), "/") + "**";
|
||||
addExcludeUrlPatterns(
|
||||
adminPattern,
|
||||
"/api/**",
|
||||
"/install",
|
||||
"/version",
|
||||
"/js/**",
|
||||
"/css/**");
|
||||
|
||||
// set failure handler
|
||||
setFailureHandler(new ContentAuthenticationFailureHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue