Refactor AdminAuthenticationFilter

pull/137/head
johnniang 2019-03-29 12:15:26 +08:00
parent e973bf2200
commit 523afaebe4
6 changed files with 105 additions and 39 deletions

View File

@ -69,7 +69,9 @@ public class HaloConfiguration {
public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) { public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) {
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter(); ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter();
// Set failure handler // Set failure handler
apiFilter.setFailureHandler(new DefaultAuthenticationFailureHandler(haloProperties.getProductionEnv(), objectMapper)); apiFilter.setFailureHandler(new DefaultAuthenticationFailureHandler()
.setProductionEnv(haloProperties.getProductionEnv())
.setObjectMapper(objectMapper));
FilterRegistrationBean<ApiAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>(); FilterRegistrationBean<ApiAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
authenticationFilter.setFilter(apiFilter); authenticationFilter.setFilter(apiFilter);
@ -79,19 +81,19 @@ public class HaloConfiguration {
} }
@Bean @Bean
public FilterRegistrationBean<AdminAuthenticationFilter> adminAuthenticationFilter(HaloProperties haloProperties, public FilterRegistrationBean<AdminAuthenticationFilter> adminAuthenticationFilter(StringCacheStore cacheStore,
ObjectMapper objectMapper, UserService userService,
StringCacheStore cacheStore, HaloProperties haloProperties,
UserService userService) { ObjectMapper objectMapper) {
AdminAuthenticationFilter adminFilter = new AdminAuthenticationFilter(cacheStore, userService, "/admin/api/login"); AdminAuthenticationFilter adminAuthenticationFilter = new AdminAuthenticationFilter(cacheStore, userService, haloProperties);
// Set auth enabled // Config the admin filter
adminFilter.setAuthEnabled(haloProperties.getAuthEnabled()); adminAuthenticationFilter.setExcludeUrlPatterns("/admin/api/login")
.setFailureHandler(new AdminAuthenticationFailureHandler()
// Set failure handler .setProductionEnv(haloProperties.getProductionEnv())
adminFilter.setFailureHandler(new AdminAuthenticationFailureHandler(haloProperties.getProductionEnv(), objectMapper)); .setObjectMapper(objectMapper));
FilterRegistrationBean<AdminAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>(); FilterRegistrationBean<AdminAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
authenticationFilter.setFilter(adminFilter); authenticationFilter.setFilter(adminAuthenticationFilter);
authenticationFilter.addUrlPatterns("/admin/*"); authenticationFilter.addUrlPatterns("/admin/*");
authenticationFilter.setOrder(1); authenticationFilter.setOrder(1);
return authenticationFilter; return authenticationFilter;

View File

@ -1,12 +1,14 @@
package cc.ryanc.halo.security.filter; package cc.ryanc.halo.security.filter;
import cc.ryanc.halo.cache.StringCacheStore; import cc.ryanc.halo.cache.StringCacheStore;
import cc.ryanc.halo.config.properties.HaloProperties;
import cc.ryanc.halo.exception.AuthenticationException; import cc.ryanc.halo.exception.AuthenticationException;
import cc.ryanc.halo.model.entity.User; import cc.ryanc.halo.model.entity.User;
import cc.ryanc.halo.security.authentication.AuthenticationImpl; import cc.ryanc.halo.security.authentication.AuthenticationImpl;
import cc.ryanc.halo.security.context.SecurityContextHolder; import cc.ryanc.halo.security.context.SecurityContextHolder;
import cc.ryanc.halo.security.context.SecurityContextImpl; import cc.ryanc.halo.security.context.SecurityContextImpl;
import cc.ryanc.halo.security.handler.AuthenticationFailureHandler; 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.security.support.UserDetail;
import cc.ryanc.halo.service.UserService; import cc.ryanc.halo.service.UserService;
import cc.ryanc.halo.utils.JsonUtils; import cc.ryanc.halo.utils.JsonUtils;
@ -49,30 +51,29 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
private AuthenticationFailureHandler failureHandler; private AuthenticationFailureHandler failureHandler;
/** private final HaloProperties haloProperties;
* Authentication enabled.
*/
private boolean authEnabled = true;
private final StringCacheStore cacheStore; private final StringCacheStore cacheStore;
private final UserService userService; private final UserService userService;
private final Collection<String> excludeUrlPatterns;
private final AntPathMatcher antPathMatcher; private final AntPathMatcher antPathMatcher;
public AdminAuthenticationFilter(StringCacheStore cacheStore, UserService userService, String... excludeUrls) { private Collection<String> excludeUrlPatterns;
public AdminAuthenticationFilter(StringCacheStore cacheStore,
UserService userService,
HaloProperties haloProperties) {
this.cacheStore = cacheStore; this.cacheStore = cacheStore;
this.userService = userService; this.userService = userService;
this.excludeUrlPatterns = excludeUrls == null ? Collections.emptyList() : Collections.unmodifiableCollection(Arrays.asList(excludeUrls)); this.haloProperties = haloProperties;
antPathMatcher = new AntPathMatcher(); antPathMatcher = new AntPathMatcher();
} }
@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 {
if (!authEnabled) { if (!haloProperties.getAuthEnabled()) {
List<User> users = userService.listAll(); List<User> users = userService.listAll();
if (!users.isEmpty()) { if (!users.isEmpty()) {
// Set security context // Set security context
@ -93,7 +94,7 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
Optional<String> userDetailOptional = cacheStore.get(token); Optional<String> userDetailOptional = cacheStore.get(token);
if (!userDetailOptional.isPresent()) { 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; return;
} }
@ -120,7 +121,7 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
return; 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 @Override
@ -128,12 +129,39 @@ public class AdminAuthenticationFilter extends OncePerRequestFilter {
return excludeUrlPatterns.stream().anyMatch(p -> antPathMatcher.match(p, request.getServletPath())); 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;
} }
/** /**

View File

@ -1,7 +1,6 @@
package cc.ryanc.halo.security.handler; package cc.ryanc.halo.security.handler;
import cc.ryanc.halo.exception.HaloException; import cc.ryanc.halo.exception.HaloException;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -15,10 +14,6 @@ import java.io.IOException;
*/ */
public class AdminAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler { public class AdminAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler {
public AdminAuthenticationFailureHandler(boolean productionEnv, ObjectMapper objectMapper) {
super(productionEnv, objectMapper);
}
@Override @Override
public void onFailure(HttpServletRequest request, HttpServletResponse response, HaloException exception) throws IOException, ServletException { public void onFailure(HttpServletRequest request, HttpServletResponse response, HaloException exception) throws IOException, ServletException {
// TODO handler the admin authentication failure. // TODO handler the admin authentication failure.

View File

@ -3,10 +3,12 @@ package cc.ryanc.halo.security.handler;
import cc.ryanc.halo.exception.HaloException; import cc.ryanc.halo.exception.HaloException;
import cc.ryanc.halo.model.support.BaseResponse; import cc.ryanc.halo.model.support.BaseResponse;
import cc.ryanc.halo.utils.ExceptionUtils; import cc.ryanc.halo.utils.ExceptionUtils;
import cc.ryanc.halo.utils.JsonUtils;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -22,14 +24,11 @@ import java.io.IOException;
@Slf4j @Slf4j
public class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandler { 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, public DefaultAuthenticationFailureHandler() {
ObjectMapper objectMapper) {
this.productionEnv = productionEnv;
this.objectMapper = objectMapper;
} }
@Override @Override
@ -52,4 +51,21 @@ public class DefaultAuthenticationFailureHandler implements AuthenticationFailur
response.getWriter().write(objectMapper.writeValueAsString(errorDetail)); 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;
}
} }

View File

@ -11,6 +11,8 @@ import cc.ryanc.halo.model.vo.CommentListVO;
import cc.ryanc.halo.model.vo.CommentVO; import cc.ryanc.halo.model.vo.CommentVO;
import cc.ryanc.halo.repository.CommentRepository; import cc.ryanc.halo.repository.CommentRepository;
import cc.ryanc.halo.repository.PostRepository; 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.CommentService;
import cc.ryanc.halo.service.OptionService; import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.service.base.AbstractCrudService; import cc.ryanc.halo.service.base.AbstractCrudService;
@ -110,7 +112,12 @@ public class CommentServiceImpl extends AbstractCrudService<Comment, Long> imple
comment.setIpAddress(ServletUtil.getClientIP(request)); comment.setIpAddress(ServletUtil.getClientIP(request));
comment.setUserAgent(ServletUtil.getHeaderIgnoreCase(request, HttpHeaders.USER_AGENT)); comment.setUserAgent(ServletUtil.getHeaderIgnoreCase(request, HttpHeaders.USER_AGENT));
// TODO Check user login status and set this field // 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.setAuthor(HtmlUtils.htmlEscape(comment.getAuthor()));
comment.setGavatarMd5(SecureUtil.md5(comment.getEmail())); comment.setGavatarMd5(SecureUtil.md5(comment.getEmail()));

View File

@ -2,12 +2,17 @@ package cc.ryanc.halo.web.controller.admin.api;
import cc.ryanc.halo.model.dto.CommentOutputDTO; import cc.ryanc.halo.model.dto.CommentOutputDTO;
import cc.ryanc.halo.model.entity.Comment; 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.enums.CommentStatus;
import cc.ryanc.halo.model.params.CommentParam; import cc.ryanc.halo.model.params.CommentParam;
import cc.ryanc.halo.model.vo.CommentListVO; 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.CommentService;
import cc.ryanc.halo.service.OptionService; import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.service.PostService; import cc.ryanc.halo.service.PostService;
import cc.ryanc.halo.utils.ValidationUtils;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@ -58,6 +63,19 @@ public class CommentController {
@PostMapping @PostMapping
public CommentOutputDTO createBy(@Valid @RequestBody CommentParam commentParam, HttpServletRequest request) { 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 // Check post id
postService.mustExistById(commentParam.getPostId()); postService.mustExistById(commentParam.getPostId());