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
public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) {
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties);
public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties,
ObjectMapper objectMapper,
OptionService optionService) {
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(haloProperties, optionService);
DefaultAuthenticationFailureHandler failureHandler = new DefaultAuthenticationFailureHandler();
failureHandler.setProductionEnv(haloProperties.isProductionEnv());

View File

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

View File

@ -1,6 +1,7 @@
package run.halo.app.security.filter;
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;
@ -41,6 +42,15 @@ public abstract class AbstractAuthenticationFilter extends OncePerRequestFilter
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
protected boolean shouldNotFilter(HttpServletRequest request) {
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"));
}
/**
* Gets token from request.
*
* @param request http servlet request must not be null
* @return token or null
*/
@Nullable
private String getTokenFromRequest(@NonNull HttpServletRequest request) {
@Override
protected String getTokenFromRequest(@NonNull HttpServletRequest request) {
Assert.notNull(request, "Http servlet request must not be null");
// Get from header

View File

@ -1,33 +1,97 @@
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.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.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.Optional;
/**
* Api authentication Filter
*
* @author johnniang
*/
@Slf4j
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);
antPathMatcher = new AntPathMatcher();
this.optionService = optionService;
}
@Override
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);
}
@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;
}
}