Add tryAuthUrlMethodPatterns for skipping authentication failure if necessary

pull/137/head
johnniang 2019-03-30 23:27:37 +08:00
parent 78b6fe5fe2
commit a0738f9ea2
3 changed files with 96 additions and 21 deletions

View File

@ -19,6 +19,7 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -92,10 +93,13 @@ public class HaloConfiguration {
@Bean @Bean
public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) { public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) {
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(); ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter();
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
failureHandler.setProductionEnv(haloProperties.getProductionEnv());
failureHandler.setObjectMapper(objectMapper);
// Set failure handler // Set failure handler
apiFilter.setFailureHandler(new DefaultAuthenticationFailureHandler() apiFilter.setFailureHandler(failureHandler);
.setProductionEnv(haloProperties.getProductionEnv())
.setObjectMapper(objectMapper));
FilterRegistrationBean<ApiAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>(); FilterRegistrationBean<ApiAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
authenticationFilter.setFilter(apiFilter); authenticationFilter.setFilter(apiFilter);
@ -110,11 +114,16 @@ public class HaloConfiguration {
HaloProperties haloProperties, HaloProperties haloProperties,
ObjectMapper objectMapper) { ObjectMapper objectMapper) {
AdminAuthenticationFilter adminAuthenticationFilter = new AdminAuthenticationFilter(cacheStore, userService, haloProperties); AdminAuthenticationFilter adminAuthenticationFilter = new AdminAuthenticationFilter(cacheStore, userService, haloProperties);
AdminAuthenticationFailureHandler failureHandler = new AdminAuthenticationFailureHandler();
failureHandler.setProductionEnv(haloProperties.getProductionEnv());
failureHandler.setObjectMapper(objectMapper);
// Config the admin filter // Config the admin filter
adminAuthenticationFilter.addExcludeUrlPatterns("/admin/api/login") adminAuthenticationFilter.addExcludeUrlPatterns("/admin/api/login");
.setFailureHandler(new AdminAuthenticationFailureHandler() adminAuthenticationFilter.addTryAuthUrlMethodPattern("/admin/api/comments", HttpMethod.POST.name());
.setProductionEnv(haloProperties.getProductionEnv()) adminAuthenticationFilter.setFailureHandler(
.setObjectMapper(objectMapper)); failureHandler);
FilterRegistrationBean<AdminAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>(); FilterRegistrationBean<AdminAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
authenticationFilter.setFilter(adminAuthenticationFilter); authenticationFilter.setFilter(adminAuthenticationFilter);

View File

@ -12,6 +12,7 @@ import cc.ryanc.halo.security.handler.DefaultAuthenticationFailureHandler;
import cc.ryanc.halo.security.support.UserDetail; import cc.ryanc.halo.security.support.UserDetail;
import cc.ryanc.halo.service.UserService; import cc.ryanc.halo.service.UserService;
import cc.ryanc.halo.utils.JsonUtils; import cc.ryanc.halo.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
@ -32,6 +33,7 @@ import java.util.*;
* *
* @author johnniang * @author johnniang
*/ */
@Slf4j
public class AdminAuthenticationFilter extends OncePerRequestFilter { public class AdminAuthenticationFilter extends OncePerRequestFilter {
/** /**
@ -59,7 +61,15 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
private final AntPathMatcher antPathMatcher; private final AntPathMatcher antPathMatcher;
private Set<String> excludeUrlPatterns = new HashSet<>(1); /**
* Exclude url patterns.
*/
private Set<String> excludeUrlPatterns = new HashSet<>(2);
/**
* Try authenticating url, method patterns.
*/
private Map<String, String> tryAuthUrlMethodPatterns = new HashMap<>(2);
public AdminAuthenticationFilter(StringCacheStore cacheStore, public AdminAuthenticationFilter(StringCacheStore cacheStore,
UserService userService, UserService userService,
@ -121,14 +131,42 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
return; return;
} }
if (shouldSkipAuthenticateFailure(request)) {
// If should skip this authentication failure
log.debug("Skipping authentication failure, url: [{}], method: [{}]", request.getServletPath(), request.getMethod());
filterChain.doFilter(request, response);
return;
}
getFailureHandler().onFailure(request, response, new AuthenticationException("You have to login before accessing admin api")); getFailureHandler().onFailure(request, response, new AuthenticationException("You have to login before accessing admin api"));
} }
@Override @Override
protected boolean shouldNotFilter(HttpServletRequest request) { 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())); 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) * Gets authentication failure handler. (Default: @DefaultAuthenticationFailureHandler)
* *
@ -137,8 +175,13 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
public AuthenticationFailureHandler getFailureHandler() { public AuthenticationFailureHandler getFailureHandler() {
if (failureHandler == null) { if (failureHandler == null) {
synchronized (this) { synchronized (this) {
// Create default authentication failure handler if (failureHandler == null) {
failureHandler = new DefaultAuthenticationFailureHandler().setProductionEnv(haloProperties.getProductionEnv()); // Create default authentication failure handler
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
failureHandler.setProductionEnv(haloProperties.getProductionEnv());
this.failureHandler = failureHandler;
}
} }
} }
return failureHandler; return failureHandler;
@ -149,9 +192,10 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
* *
* @param failureHandler authentication failure handler * @param failureHandler authentication failure handler
*/ */
public AdminAuthenticationFilter setFailureHandler(AuthenticationFailureHandler failureHandler) { public void setFailureHandler(@NonNull AuthenticationFailureHandler failureHandler) {
Assert.notNull(failureHandler, "Authentication failure handler must not be null");
this.failureHandler = failureHandler; this.failureHandler = failureHandler;
return this;
} }
/** /**
@ -159,11 +203,10 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
* *
* @param excludeUrlPatterns exclude urls * @param excludeUrlPatterns exclude urls
*/ */
public AdminAuthenticationFilter setExcludeUrlPatterns(Collection<String> excludeUrlPatterns) { public void setExcludeUrlPatterns(@NonNull Collection<String> excludeUrlPatterns) {
Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null"); Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null");
this.excludeUrlPatterns = new HashSet<>(excludeUrlPatterns); this.excludeUrlPatterns = new HashSet<>(excludeUrlPatterns);
return this;
} }
/** /**
@ -171,11 +214,32 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
* *
* @param excludeUrlPatterns exclude urls * @param excludeUrlPatterns exclude urls
*/ */
public AdminAuthenticationFilter addExcludeUrlPatterns(String... excludeUrlPatterns) { public void addExcludeUrlPatterns(@NonNull String... excludeUrlPatterns) {
Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null"); Assert.notNull(excludeUrlPatterns, "Exclude url patterns must not be null");
Collections.addAll(this.excludeUrlPatterns, excludeUrlPatterns); Collections.addAll(this.excludeUrlPatterns, excludeUrlPatterns);
return this; }
/**
* Gets exclude url patterns.
*
* @return exclude url patterns.
*/
public Set<String> 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);
} }
/** /**

View File

@ -55,17 +55,19 @@ public class DefaultAuthenticationFailureHandler implements AuthenticationFailur
* Sets custom object mapper. * Sets custom object mapper.
* *
* @param objectMapper object mapper * @param objectMapper object mapper
* @return current authentication failure handler
*/ */
public DefaultAuthenticationFailureHandler setObjectMapper(ObjectMapper objectMapper) { public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "Object mapper must not be null"); Assert.notNull(objectMapper, "Object mapper must not be null");
this.objectMapper = objectMapper; this.objectMapper = objectMapper;
return this;
} }
public DefaultAuthenticationFailureHandler setProductionEnv(boolean productionEnv) { /**
* Sets production environment.
*
* @param productionEnv production environment
*/
public void setProductionEnv(boolean productionEnv) {
this.productionEnv = productionEnv; this.productionEnv = productionEnv;
return this;
} }
} }