mirror of https://github.com/halo-dev/halo
Complete api authentication
parent
574ae54208
commit
f7d16e06a3
|
@ -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());
|
||||||
|
|
|
@ -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())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue