diff --git a/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java b/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java index 91d590769..6a1b649db 100644 --- a/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java +++ b/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java @@ -78,8 +78,10 @@ public class HaloConfiguration { } @Bean - public FilterRegistrationBean adminAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) { - AdminAuthenticationFilter adminFilter = new AdminAuthenticationFilter(); + public FilterRegistrationBean adminAuthenticationFilter(HaloProperties haloProperties, + ObjectMapper objectMapper, + StringCacheStore cacheStore) { + AdminAuthenticationFilter adminFilter = new AdminAuthenticationFilter(cacheStore); // Set failure handler adminFilter.setFailureHandler(new AdminAuthenticationFailureHandler(haloProperties.getProductionEnv(), objectMapper)); diff --git a/src/main/java/cc/ryanc/halo/filter/CorsFilter.java b/src/main/java/cc/ryanc/halo/filter/CorsFilter.java index dd8a057e3..9cceef8aa 100644 --- a/src/main/java/cc/ryanc/halo/filter/CorsFilter.java +++ b/src/main/java/cc/ryanc/halo/filter/CorsFilter.java @@ -24,7 +24,7 @@ public class CorsFilter extends OncePerRequestFilter { httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*"); httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTION"); - httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "false"); + httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600"); if (CorsUtils.isPreFlightRequest(httpServletRequest)) { diff --git a/src/main/java/cc/ryanc/halo/handler/file/LocalFileHandler.java b/src/main/java/cc/ryanc/halo/handler/file/LocalFileHandler.java index c617fd194..5b397596d 100644 --- a/src/main/java/cc/ryanc/halo/handler/file/LocalFileHandler.java +++ b/src/main/java/cc/ryanc/halo/handler/file/LocalFileHandler.java @@ -96,8 +96,10 @@ public class LocalFileHandler implements FileHandler { // Build directory String subDir = UPLOAD_SUB_DIR + year + File.separator + month + File.separator; + String originalBasename = FilenameUtils.getBasename(file.getOriginalFilename()); + // Get basename - String basename = FilenameUtils.getBasename(file.getOriginalFilename()) + '-' + HaloUtils.randomUUIDWithoutDash(); + String basename = originalBasename + '-' + HaloUtils.randomUUIDWithoutDash(); // Get extension String extension = FilenameUtils.getExtension(file.getOriginalFilename()); @@ -123,7 +125,7 @@ public class LocalFileHandler implements FileHandler { // Build upload result UploadResult uploadResult = new UploadResult(); - uploadResult.setFilename(basename); + uploadResult.setFilename(originalBasename); uploadResult.setFilePath(subFilePath); uploadResult.setKey(subFilePath); uploadResult.setSuffix(extension); diff --git a/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java b/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java index 9732fb3f8..30eacd1c8 100644 --- a/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java +++ b/src/main/java/cc/ryanc/halo/model/enums/PropertyEnum.java @@ -109,6 +109,7 @@ public interface PropertyEnum extends ValueEnum { || type.isAssignableFrom(Double.class) || type.isAssignableFrom(Float.class) || type.isAssignableFrom(Enum.class) + || type.isAssignableFrom(ValueEnum.class) ); } } diff --git a/src/main/java/cc/ryanc/halo/security/filter/AdminAuthenticationFilter.java b/src/main/java/cc/ryanc/halo/security/filter/AdminAuthenticationFilter.java index 1cc75444e..a28bacc97 100644 --- a/src/main/java/cc/ryanc/halo/security/filter/AdminAuthenticationFilter.java +++ b/src/main/java/cc/ryanc/halo/security/filter/AdminAuthenticationFilter.java @@ -1,6 +1,18 @@ package cc.ryanc.halo.security.filter; +import cc.ryanc.halo.cache.StringCacheStore; +import cc.ryanc.halo.exception.AuthenticationException; +import cc.ryanc.halo.security.authentication.AuthenticationImpl; +import cc.ryanc.halo.security.context.SecurityContextHolder; +import cc.ryanc.halo.security.context.SecurityContextImpl; import cc.ryanc.halo.security.handler.AuthenticationFailureHandler; +import cc.ryanc.halo.security.support.UserDetail; +import cc.ryanc.halo.utils.JsonUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; @@ -8,6 +20,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.Optional; /** * Admin authentication filter. @@ -16,15 +29,92 @@ import java.io.IOException; */ public class AdminAuthenticationFilter extends OncePerRequestFilter { + /** + * Admin session key. + */ + public final static String ADMIN_SESSION_KEY = "halo.admin.session"; + + /** + * Admin token header name. + */ + public final static String ADMIN_TOKEN_HEADER_NAME = "ADMIN-" + HttpHeaders.AUTHORIZATION; + + /** + * Admin token param name. + */ + public final static String ADMIN_TOKEN_PARAM_NAME = "adminToken"; + private AuthenticationFailureHandler failureHandler; + private final StringCacheStore cacheStore; + + public AdminAuthenticationFilter(StringCacheStore cacheStore) { + this.cacheStore = cacheStore; + } + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - // TODO Handle admin authentication - filterChain.doFilter(request, response); + // Get token from request + String token = getTokenFromRequest(request); + + if (StringUtils.isNotBlank(token)) { + + // Valid the token + Optional userDetailOptional = cacheStore.get(token); + + if (!userDetailOptional.isPresent()) { + failureHandler.onFailure(request, response, new AuthenticationException("The token has been expired or not exist").setErrorData(token)); + return; + } + + UserDetail userDetail = JsonUtils.jsonToObject(userDetailOptional.get(), UserDetail.class); + + // Set security + SecurityContextHolder.setContext(new SecurityContextImpl(new AuthenticationImpl(userDetail))); + + filterChain.doFilter(request, response); + return; + } + + // Get info from session + Object adminSessionValue = request.getSession().getAttribute(ADMIN_SESSION_KEY); + + if (adminSessionValue instanceof UserDetail) { + // Convert to user detail + UserDetail userDetail = (UserDetail) adminSessionValue; + + // Set security context + SecurityContextHolder.setContext(new SecurityContextImpl(new AuthenticationImpl(userDetail))); + + filterChain.doFilter(request, response); + return; + } + + failureHandler.onFailure(request, response, new AuthenticationException("You have to login before accessing admin api")); } public void setFailureHandler(AuthenticationFailureHandler failureHandler) { this.failureHandler = failureHandler; } + + /** + * Gets token from 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"); + + // Get from header + String token = request.getHeader(ADMIN_TOKEN_HEADER_NAME); + + // Get from param + if (StringUtils.isBlank(token)) { + token = request.getParameter(ADMIN_TOKEN_PARAM_NAME); + } + + return token; + } } diff --git a/src/main/java/cc/ryanc/halo/security/handler/AdminAuthenticationFailureHandler.java b/src/main/java/cc/ryanc/halo/security/handler/AdminAuthenticationFailureHandler.java index a0ebadf60..2b4b91219 100644 --- a/src/main/java/cc/ryanc/halo/security/handler/AdminAuthenticationFailureHandler.java +++ b/src/main/java/cc/ryanc/halo/security/handler/AdminAuthenticationFailureHandler.java @@ -1,13 +1,7 @@ package cc.ryanc.halo.security.handler; -import cc.ryanc.halo.exception.HaloException; import com.fasterxml.jackson.databind.ObjectMapper; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - /** * Authentication failure handler. * @@ -19,8 +13,8 @@ public class AdminAuthenticationFailureHandler extends DefaultAuthenticationFail super(productionEnv, objectMapper); } - @Override - public void onFailure(HttpServletRequest request, HttpServletResponse response, HaloException exception) throws IOException, ServletException { - // TODO handler the admin authentication failure. - } +// @Override +// public void onFailure(HttpServletRequest request, HttpServletResponse response, HaloException exception) throws IOException, ServletException { +// // TODO handler the admin authentication failure. +// } } diff --git a/src/main/java/cc/ryanc/halo/security/handler/DefaultAuthenticationFailureHandler.java b/src/main/java/cc/ryanc/halo/security/handler/DefaultAuthenticationFailureHandler.java index 9518cdb55..556632762 100644 --- a/src/main/java/cc/ryanc/halo/security/handler/DefaultAuthenticationFailureHandler.java +++ b/src/main/java/cc/ryanc/halo/security/handler/DefaultAuthenticationFailureHandler.java @@ -38,6 +38,7 @@ public class DefaultAuthenticationFailureHandler implements AuthenticationFailur BaseResponse errorDetail = new BaseResponse(); + errorDetail.setStatus(exception.getStatus().value()); errorDetail.setMessage(exception.getMessage()); if (!productionEnv) { diff --git a/src/main/java/cc/ryanc/halo/service/OptionService.java b/src/main/java/cc/ryanc/halo/service/OptionService.java index c7639f9b6..e76174b01 100755 --- a/src/main/java/cc/ryanc/halo/service/OptionService.java +++ b/src/main/java/cc/ryanc/halo/service/OptionService.java @@ -4,6 +4,7 @@ import cc.ryanc.halo.exception.MissingPropertyException; import cc.ryanc.halo.model.dto.OptionOutputDTO; import cc.ryanc.halo.model.entity.Option; import cc.ryanc.halo.model.enums.PropertyEnum; +import cc.ryanc.halo.model.enums.ValueEnum; import cc.ryanc.halo.model.params.OptionParam; import cc.ryanc.halo.service.base.CrudService; import com.qiniu.common.Zone; @@ -170,6 +171,7 @@ public interface OptionService extends CrudService { * @param value type * @return value */ + @NonNull Optional getByKey(@NonNull String key, @NonNull Class valueType); /** @@ -180,6 +182,7 @@ public interface OptionService extends CrudService { * @param enum value type * @return an optional enum value */ + @NonNull > Optional getEnumByProperty(@NonNull PropertyEnum property, @NonNull Class valueType); /** @@ -191,7 +194,36 @@ public interface OptionService extends CrudService { * @param enum value type * @return enum value */ - > T getEnumByPropertyOrDefault(@NonNull PropertyEnum property, @NonNull Class valueType, T defaultValue); + @Nullable + > T getEnumByPropertyOrDefault(@NonNull PropertyEnum property, @NonNull Class valueType, @Nullable T defaultValue); + + /** + * Gets value enum by property. + * + * @param property property must not be blank + * @param valueType enum value type must not be null + * @param enumType enum type must not be null + * @param enum value type + * @param value enum type + * @return an optional value enum value + */ + @NonNull + > Optional getValueEnumByProperty(@NonNull PropertyEnum property, @NonNull Class valueType, @NonNull Class enumType); + + /** + * Gets value enum by property. + * + * @param property property must not be blank + * @param valueType enum value type must not be null + * @param enumType enum type must not be null + * @param defaultValue default value enum value + * @param enum value type + * @param value enum type + * @return value enum value or null if the default value is null + */ + @Nullable + > E getValueEnumByPropertyOrDefault(@NonNull PropertyEnum property, @NonNull Class valueType, @NonNull Class enumType, @Nullable E defaultValue); + /** * Gets post page size. diff --git a/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java index 2486c30aa..705a00e2a 100644 --- a/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java +++ b/src/main/java/cc/ryanc/halo/service/impl/OptionServiceImpl.java @@ -6,6 +6,7 @@ import cc.ryanc.halo.model.entity.Option; import cc.ryanc.halo.model.enums.BlogProperties; import cc.ryanc.halo.model.enums.PropertyEnum; import cc.ryanc.halo.model.enums.QnYunProperties; +import cc.ryanc.halo.model.enums.ValueEnum; import cc.ryanc.halo.model.params.OptionParam; import cc.ryanc.halo.repository.OptionRepository; import cc.ryanc.halo.service.OptionService; @@ -198,6 +199,16 @@ public class OptionServiceImpl extends AbstractCrudService impl return getEnumByProperty(property, valueType).orElse(defaultValue); } + @Override + public > Optional getValueEnumByProperty(PropertyEnum property, Class valueType, Class enumType) { + return getByProperty(property).map(value -> ValueEnum.valueToEnum(enumType, PropertyEnum.convertTo(value, valueType))); + } + + @Override + public > E getValueEnumByPropertyOrDefault(PropertyEnum property, Class valueType, Class enumType, E defaultValue) { + return getValueEnumByProperty(property, valueType, enumType).orElse(defaultValue); + } + @Override public int getPostPageSize() { try {