diff --git a/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java b/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java index 9727d4384..589c93c7c 100644 --- a/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java +++ b/src/main/java/cc/ryanc/halo/config/HaloConfiguration.java @@ -69,7 +69,9 @@ public class HaloConfiguration { public FilterRegistrationBean apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) { ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(); // Set failure handler - apiFilter.setFailureHandler(new DefaultAuthenticationFailureHandler(haloProperties.getProductionEnv(), objectMapper)); + apiFilter.setFailureHandler(new DefaultAuthenticationFailureHandler() + .setProductionEnv(haloProperties.getProductionEnv()) + .setObjectMapper(objectMapper)); FilterRegistrationBean authenticationFilter = new FilterRegistrationBean<>(); authenticationFilter.setFilter(apiFilter); @@ -79,19 +81,19 @@ public class HaloConfiguration { } @Bean - public FilterRegistrationBean adminAuthenticationFilter(HaloProperties haloProperties, - ObjectMapper objectMapper, - StringCacheStore cacheStore, - UserService userService) { - AdminAuthenticationFilter adminFilter = new AdminAuthenticationFilter(cacheStore, userService, "/admin/api/login"); - // Set auth enabled - adminFilter.setAuthEnabled(haloProperties.getAuthEnabled()); - - // Set failure handler - adminFilter.setFailureHandler(new AdminAuthenticationFailureHandler(haloProperties.getProductionEnv(), objectMapper)); + public FilterRegistrationBean adminAuthenticationFilter(StringCacheStore cacheStore, + UserService userService, + HaloProperties haloProperties, + ObjectMapper objectMapper) { + AdminAuthenticationFilter adminAuthenticationFilter = new AdminAuthenticationFilter(cacheStore, userService, haloProperties); + // Config the admin filter + adminAuthenticationFilter.setExcludeUrlPatterns("/admin/api/login") + .setFailureHandler(new AdminAuthenticationFailureHandler() + .setProductionEnv(haloProperties.getProductionEnv()) + .setObjectMapper(objectMapper)); FilterRegistrationBean authenticationFilter = new FilterRegistrationBean<>(); - authenticationFilter.setFilter(adminFilter); + authenticationFilter.setFilter(adminAuthenticationFilter); authenticationFilter.addUrlPatterns("/admin/*"); authenticationFilter.setOrder(1); return authenticationFilter; 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 a6d21ad87..3ead87671 100644 --- a/src/main/java/cc/ryanc/halo/security/filter/AdminAuthenticationFilter.java +++ b/src/main/java/cc/ryanc/halo/security/filter/AdminAuthenticationFilter.java @@ -1,12 +1,14 @@ package cc.ryanc.halo.security.filter; import cc.ryanc.halo.cache.StringCacheStore; +import cc.ryanc.halo.config.properties.HaloProperties; import cc.ryanc.halo.exception.AuthenticationException; import cc.ryanc.halo.model.entity.User; 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.handler.DefaultAuthenticationFailureHandler; import cc.ryanc.halo.security.support.UserDetail; import cc.ryanc.halo.service.UserService; import cc.ryanc.halo.utils.JsonUtils; @@ -49,30 +51,29 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter { private AuthenticationFailureHandler failureHandler; - /** - * Authentication enabled. - */ - private boolean authEnabled = true; + private final HaloProperties haloProperties; private final StringCacheStore cacheStore; private final UserService userService; - private final Collection excludeUrlPatterns; - private final AntPathMatcher antPathMatcher; - public AdminAuthenticationFilter(StringCacheStore cacheStore, UserService userService, String... excludeUrls) { + private Collection excludeUrlPatterns; + + public AdminAuthenticationFilter(StringCacheStore cacheStore, + UserService userService, + HaloProperties haloProperties) { this.cacheStore = cacheStore; this.userService = userService; - this.excludeUrlPatterns = excludeUrls == null ? Collections.emptyList() : Collections.unmodifiableCollection(Arrays.asList(excludeUrls)); + this.haloProperties = haloProperties; antPathMatcher = new AntPathMatcher(); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - if (!authEnabled) { + if (!haloProperties.getAuthEnabled()) { List users = userService.listAll(); if (!users.isEmpty()) { // Set security context @@ -93,7 +94,7 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter { Optional userDetailOptional = cacheStore.get(token); if (!userDetailOptional.isPresent()) { - failureHandler.onFailure(request, response, new AuthenticationException("The token has been expired or not exist").setErrorData(token)); + getFailureHandler().onFailure(request, response, new AuthenticationException("The token has been expired or not exist").setErrorData(token)); return; } @@ -120,7 +121,7 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter { return; } - failureHandler.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 @@ -128,12 +129,39 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter { return excludeUrlPatterns.stream().anyMatch(p -> antPathMatcher.match(p, request.getServletPath())); } - public void setFailureHandler(AuthenticationFailureHandler failureHandler) { - this.failureHandler = failureHandler; + /** + * Gets authentication failure handler. (Default: @DefaultAuthenticationFailureHandler) + * + * @return authentication failure handler + */ + public AuthenticationFailureHandler getFailureHandler() { + if (failureHandler == null) { + synchronized (this) { + // Create default authentication failure handler + failureHandler = new DefaultAuthenticationFailureHandler().setProductionEnv(haloProperties.getProductionEnv()); + } + } + return failureHandler; } - public void setAuthEnabled(boolean authEnabled) { - this.authEnabled = authEnabled; + /** + * Sets authentication failure handler. + * + * @param failureHandler authentication failure handler + */ + public AdminAuthenticationFilter setFailureHandler(AuthenticationFailureHandler failureHandler) { + this.failureHandler = failureHandler; + return this; + } + + /** + * Set exclude url patterns. + * + * @param excludeUrls exclude urls + */ + public AdminAuthenticationFilter setExcludeUrlPatterns(String... excludeUrls) { + this.excludeUrlPatterns = excludeUrls == null ? Collections.emptyList() : Collections.unmodifiableCollection(Arrays.asList(excludeUrls)); + return this; } /** 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 e5346dd02..65f03b305 100644 --- a/src/main/java/cc/ryanc/halo/security/handler/AdminAuthenticationFailureHandler.java +++ b/src/main/java/cc/ryanc/halo/security/handler/AdminAuthenticationFailureHandler.java @@ -1,7 +1,6 @@ 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; @@ -15,10 +14,6 @@ import java.io.IOException; */ public class AdminAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler { - public AdminAuthenticationFailureHandler(boolean productionEnv, ObjectMapper objectMapper) { - super(productionEnv, objectMapper); - } - @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 64f988cc5..c25a4ff76 100644 --- a/src/main/java/cc/ryanc/halo/security/handler/DefaultAuthenticationFailureHandler.java +++ b/src/main/java/cc/ryanc/halo/security/handler/DefaultAuthenticationFailureHandler.java @@ -3,10 +3,12 @@ package cc.ryanc.halo.security.handler; import cc.ryanc.halo.exception.HaloException; import cc.ryanc.halo.model.support.BaseResponse; import cc.ryanc.halo.utils.ExceptionUtils; +import cc.ryanc.halo.utils.JsonUtils; import cn.hutool.extra.servlet.ServletUtil; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; +import org.springframework.util.Assert; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -22,14 +24,11 @@ import java.io.IOException; @Slf4j public class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandler { - private final boolean productionEnv; + private boolean productionEnv = true; - private final ObjectMapper objectMapper; + private ObjectMapper objectMapper = JsonUtils.DEFAULT_JSON_MAPPER; - public DefaultAuthenticationFailureHandler(boolean productionEnv, - ObjectMapper objectMapper) { - this.productionEnv = productionEnv; - this.objectMapper = objectMapper; + public DefaultAuthenticationFailureHandler() { } @Override @@ -52,4 +51,21 @@ public class DefaultAuthenticationFailureHandler implements AuthenticationFailur response.getWriter().write(objectMapper.writeValueAsString(errorDetail)); } + /** + * Sets custom object mapper. + * + * @param objectMapper object mapper + * @return current authentication failure handler + */ + public DefaultAuthenticationFailureHandler setObjectMapper(ObjectMapper objectMapper) { + Assert.notNull(objectMapper, "Object mapper must not be null"); + + this.objectMapper = objectMapper; + return this; + } + + public DefaultAuthenticationFailureHandler setProductionEnv(boolean productionEnv) { + this.productionEnv = productionEnv; + return this; + } } diff --git a/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java index a345ab91d..063d234af 100644 --- a/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java +++ b/src/main/java/cc/ryanc/halo/service/impl/CommentServiceImpl.java @@ -11,6 +11,8 @@ import cc.ryanc.halo.model.vo.CommentListVO; import cc.ryanc.halo.model.vo.CommentVO; import cc.ryanc.halo.repository.CommentRepository; import cc.ryanc.halo.repository.PostRepository; +import cc.ryanc.halo.security.authentication.Authentication; +import cc.ryanc.halo.security.context.SecurityContextHolder; import cc.ryanc.halo.service.CommentService; import cc.ryanc.halo.service.OptionService; import cc.ryanc.halo.service.base.AbstractCrudService; @@ -110,7 +112,12 @@ public class CommentServiceImpl extends AbstractCrudService imple comment.setIpAddress(ServletUtil.getClientIP(request)); comment.setUserAgent(ServletUtil.getHeaderIgnoreCase(request, HttpHeaders.USER_AGENT)); // TODO Check user login status and set this field - comment.setIsAdmin(false); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) { + // If the user is login + comment.setIsAdmin(true); + } + comment.setAuthor(HtmlUtils.htmlEscape(comment.getAuthor())); comment.setGavatarMd5(SecureUtil.md5(comment.getEmail())); diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/api/CommentController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/api/CommentController.java index f5cc5d764..b9c19fb11 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/api/CommentController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/api/CommentController.java @@ -2,12 +2,17 @@ package cc.ryanc.halo.web.controller.admin.api; import cc.ryanc.halo.model.dto.CommentOutputDTO; import cc.ryanc.halo.model.entity.Comment; +import cc.ryanc.halo.model.entity.User; +import cc.ryanc.halo.model.enums.BlogProperties; import cc.ryanc.halo.model.enums.CommentStatus; import cc.ryanc.halo.model.params.CommentParam; import cc.ryanc.halo.model.vo.CommentListVO; +import cc.ryanc.halo.security.authentication.Authentication; +import cc.ryanc.halo.security.context.SecurityContextHolder; import cc.ryanc.halo.service.CommentService; import cc.ryanc.halo.service.OptionService; import cc.ryanc.halo.service.PostService; +import cc.ryanc.halo.utils.ValidationUtils; import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -58,6 +63,19 @@ public class CommentController { @PostMapping public CommentOutputDTO createBy(@Valid @RequestBody CommentParam commentParam, HttpServletRequest request) { + // Get authentication + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) { + User user = authentication.getDetail().getUser(); + // If the admin is login + commentParam.setAuthor(user.getNickname()); + commentParam.setEmail(user.getEmail()); + commentParam.setAuthor(optionService.getByPropertyOfNullable(BlogProperties.BLOG_URL)); + } + + // Validate the comment param manually + ValidationUtils.validate(commentParam); + // Check post id postService.mustExistById(commentParam.getPostId());