Complete api authentication

pull/146/head
johnniang 2019-05-06 17:01:59 +08:00
parent 574ae54208
commit f7d16e06a3
6 changed files with 90 additions and 26 deletions

View File

@ -102,8 +102,10 @@ public class HaloConfiguration {
} }
@Bean @Bean
public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) { public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties,
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties); ObjectMapper objectMapper,
OptionService optionService) {
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties, optionService);
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler(); DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
failureHandler.setProductionEnv(haloProperties.isProductionEnv()); failureHandler.setProductionEnv(haloProperties.isProductionEnv());

View File

@ -8,13 +8,13 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import run.halo.app.config.properties.HaloProperties; import run.halo.app.config.properties.HaloProperties;
import run.halo.app.model.entity.User; import run.halo.app.model.entity.User;
import run.halo.app.security.filter.AdminAuthenticationFilter; import run.halo.app.security.filter.AdminAuthenticationFilter;
import run.halo.app.security.filter.ApiAuthenticationFilter;
import run.halo.app.security.support.UserDetail; import run.halo.app.security.support.UserDetail;
import springfox.documentation.builders.*; import springfox.documentation.builders.*;
import springfox.documentation.schema.AlternateTypeRule; import springfox.documentation.schema.AlternateTypeRule;
@ -34,7 +34,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static run.halo.app.model.support.HaloConst.HALO_VERSION; import static run.halo.app.model.support.HaloConst.HALO_VERSION;
import static run.halo.app.model.support.HaloConst.TOKEN_HEADER;
import static springfox.documentation.schema.AlternateTypeRules.newRule; import static springfox.documentation.schema.AlternateTypeRules.newRule;
/** /**
@ -140,8 +139,8 @@ public class SwaggerConfiguration {
private List<ApiKey> portalApiKeys() { private List<ApiKey> portalApiKeys() {
return Arrays.asList( return Arrays.asList(
new ApiKey("Token from header", HttpHeaders.AUTHORIZATION, In.HEADER.name()), new ApiKey("Token from header", ApiAuthenticationFilter.API_TOKEN_HEADER_NAME, In.HEADER.name()),
new ApiKey("Token from query", "token", In.QUERY.name()) new ApiKey("Token from query", ApiAuthenticationFilter.API_TOKEN_QUERY_NAME, In.QUERY.name())
); );
} }

View File

@ -25,11 +25,6 @@ public class HaloConst {
*/ */
public static final String HALO_VERSION = "1.0.0"; public static final String HALO_VERSION = "1.0.0";
/**
* Token of header param
*/
public static final String TOKEN_HEADER = "token";
/** /**
* Suffix of freemarker template file * Suffix of freemarker template file
*/ */

View File

@ -1,6 +1,7 @@
package run.halo.app.security.filter; package run.halo.app.security.filter;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
@ -41,6 +42,15 @@ public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter
antPathMatcher = new AntPathMatcher(); antPathMatcher = new AntPathMatcher();
} }
/**
* Gets token from request.
*
* @param request http servlet request must not be null
* @return token or null
*/
@Nullable
protected abstract String getTokenFromRequest(@NonNull HttpServletRequest request);
@Override @Override
protected boolean shouldNotFilter(HttpServletRequest request) { protected boolean shouldNotFilter(HttpServletRequest request) {
Assert.notNull(request, "Http servlet request must not be null"); Assert.notNull(request, "Http servlet request must not be null");

View File

@ -137,14 +137,8 @@ public class AdminAuthenticationFilter extends AbstractAuthenticationFilter {
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
* Gets token from request. protected String getTokenFromRequest(@NonNull HttpServletRequest request) {
*
* @param request http servlet request must not be null
* @return token or null
*/
@Nullable
private String getTokenFromRequest(@NonNull HttpServletRequest request) {
Assert.notNull(request, "Http servlet request must not be null"); Assert.notNull(request, "Http servlet request must not be null");
// Get from header // Get from header

View File

@ -1,33 +1,97 @@
package run.halo.app.security.filter; package run.halo.app.security.filter;
import org.springframework.util.AntPathMatcher; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import run.halo.app.config.properties.HaloProperties; 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.OtherProperties;
import run.halo.app.service.OptionService;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet; import java.util.Optional;
import java.util.Set;
/** /**
* Api authentication Filter * Api authentication Filter
* *
* @author johnniang * @author johnniang
*/ */
@Slf4j
public class ApiAuthenticationFilter extends AbstractAuthenticationFilter { public class ApiAuthenticationFilter extends AbstractAuthenticationFilter {
private final AntPathMatcher antPathMatcher; public final static String API_TOKEN_HEADER_NAME = "API-" + HttpHeaders.AUTHORIZATION;
public ApiAuthenticationFilter(HaloProperties haloProperties) { public final static String API_TOKEN_QUERY_NAME = "apiToken";
private final OptionService optionService;
public ApiAuthenticationFilter(HaloProperties haloProperties,
OptionService optionService) {
super(haloProperties); super(haloProperties);
antPathMatcher = new AntPathMatcher(); this.optionService = optionService;
} }
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// TODO Handle authentication // Get token
String token = getTokenFromRequest(request);
if (StringUtils.isBlank(token)) {
// If the token is missing
getFailureHandler().onFailure(request, response, new AuthenticationException("Missing API token"));
return;
}
// Get api_enable from option
Boolean apiEnabled = optionService.getByPropertyOrDefault(OtherProperties.API_ENABLED, Boolean.class, false);
if (!apiEnabled) {
getFailureHandler().onFailure(request, response, new ForbiddenException("API has been disabled by blogger currently"));
return;
}
// Get token from option
Optional<String> optionalToken = optionService.getByProperty(OtherProperties.API_TOKEN, String.class);
if (!optionalToken.isPresent()) {
// If the token is not set
getFailureHandler().onFailure(request, response, new AuthenticationException("API Token hasn't been set by blogger"));
return;
}
if (!StringUtils.equals(token, optionalToken.get())) {
// If the token is mismatch
getFailureHandler().onFailure(request, response, new AuthenticationException("Token is mismatch"));
return;
}
// Do filter
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} }
@Override
protected String getTokenFromRequest(@NonNull HttpServletRequest request) {
Assert.notNull(request, "Http servlet request must not be null");
// Get from header
String token = request.getHeader(API_TOKEN_HEADER_NAME);
// Get from param
if (StringUtils.isBlank(token)) {
token = request.getParameter(API_TOKEN_QUERY_NAME);
log.debug("Got token from parameter: [{}: {}]", API_TOKEN_QUERY_NAME, token);
} else {
log.debug("Got token from header: [{}: {}]", API_TOKEN_HEADER_NAME, token);
}
return token;
}
} }