mirror of https://github.com/halo-dev/halo
Add security module
parent
c9c68eef54
commit
f7b90652ba
|
@ -3,6 +3,11 @@ package cc.ryanc.halo.config;
|
||||||
import cc.ryanc.halo.config.properties.HaloProperties;
|
import cc.ryanc.halo.config.properties.HaloProperties;
|
||||||
import cc.ryanc.halo.filter.CorsFilter;
|
import cc.ryanc.halo.filter.CorsFilter;
|
||||||
import cc.ryanc.halo.filter.LogFilter;
|
import cc.ryanc.halo.filter.LogFilter;
|
||||||
|
import cc.ryanc.halo.security.filter.AdminAuthenticationFilter;
|
||||||
|
import cc.ryanc.halo.security.filter.ApiAuthenticationFilter;
|
||||||
|
import cc.ryanc.halo.security.handler.AdminAuthenticationFailureHandler;
|
||||||
|
import cc.ryanc.halo.security.handler.DefaultAuthenticationFailureHandler;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
@ -49,4 +54,30 @@ public class HaloConfiguration {
|
||||||
|
|
||||||
return logFilter;
|
return logFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<ApiAuthenticationFilter> apiAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) {
|
||||||
|
ApiAuthenticationFilter apiFilter = new ApiAuthenticationFilter();
|
||||||
|
// Set failure handler
|
||||||
|
apiFilter.setFailureHandler(new DefaultAuthenticationFailureHandler(haloProperties.getProductionEnv(), objectMapper));
|
||||||
|
|
||||||
|
FilterRegistrationBean<ApiAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
|
||||||
|
authenticationFilter.setFilter(apiFilter);
|
||||||
|
authenticationFilter.addUrlPatterns("/api/*");
|
||||||
|
authenticationFilter.setOrder(0);
|
||||||
|
return authenticationFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<AdminAuthenticationFilter> adminAuthenticationFilter(HaloProperties haloProperties, ObjectMapper objectMapper) {
|
||||||
|
AdminAuthenticationFilter adminFilter = new AdminAuthenticationFilter();
|
||||||
|
// Set failure handler
|
||||||
|
adminFilter.setFailureHandler(new AdminAuthenticationFailureHandler(haloProperties.getProductionEnv(), objectMapper));
|
||||||
|
|
||||||
|
FilterRegistrationBean<AdminAuthenticationFilter> authenticationFilter = new FilterRegistrationBean<>();
|
||||||
|
authenticationFilter.setFilter(adminFilter);
|
||||||
|
authenticationFilter.addUrlPatterns("/admin/*");
|
||||||
|
authenticationFilter.setOrder(1);
|
||||||
|
return authenticationFilter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
package cc.ryanc.halo.config;
|
package cc.ryanc.halo.config;
|
||||||
|
|
||||||
import cc.ryanc.halo.config.properties.HaloProperties;
|
import cc.ryanc.halo.config.properties.HaloProperties;
|
||||||
|
import cc.ryanc.halo.security.resolver.AuthenticationArgumentResolver;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,53 +34,13 @@ import java.util.Locale;
|
||||||
@PropertySource(value = "classpath:application.yaml", ignoreResourceNotFound = true, encoding = "UTF-8")
|
@PropertySource(value = "classpath:application.yaml", ignoreResourceNotFound = true, encoding = "UTF-8")
|
||||||
public class WebMvcAutoConfiguration implements WebMvcConfigurer {
|
public class WebMvcAutoConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
// @Autowired
|
|
||||||
// private LoginInterceptor loginInterceptor;
|
|
||||||
//
|
|
||||||
// @Autowired
|
|
||||||
// private InstallInterceptor installInterceptor;
|
|
||||||
//
|
|
||||||
// @Autowired
|
|
||||||
// private ApiInterceptor apiInterceptor;
|
|
||||||
//
|
|
||||||
// @Autowired
|
|
||||||
// private LocaleInterceptor localeInterceptor;
|
|
||||||
//
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HaloProperties haloProperties;
|
private HaloProperties haloProperties;
|
||||||
//
|
|
||||||
// /**
|
@Override
|
||||||
// * 注册拦截器
|
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||||
// *
|
resolvers.add(new AuthenticationArgumentResolver());
|
||||||
// * @param registry registry
|
}
|
||||||
// */
|
|
||||||
// @Override
|
|
||||||
// public void addInterceptors(InterceptorRegistry registry) {
|
|
||||||
// registry.addInterceptor(loginInterceptor)
|
|
||||||
// .addPathPatterns("/admin.*")
|
|
||||||
// .addPathPatterns("/admin/**")
|
|
||||||
// .addPathPatterns("/backup/**")
|
|
||||||
// .excludePathPatterns("/admin/login")
|
|
||||||
// .excludePathPatterns("/admin/getLogin")
|
|
||||||
// .excludePathPatterns("/admin/findPassword")
|
|
||||||
// .excludePathPatterns("/admin/sendResetPasswordEmail")
|
|
||||||
// .excludePathPatterns("/admin/toResetPassword")
|
|
||||||
// .excludePathPatterns("/admin/resetPassword")
|
|
||||||
// .excludePathPatterns("/static/**");
|
|
||||||
// registry.addInterceptor(installInterceptor)
|
|
||||||
// .addPathPatterns("/**")
|
|
||||||
// .excludePathPatterns("/install")
|
|
||||||
// .excludePathPatterns("/install/do")
|
|
||||||
// .excludePathPatterns("/static/**");
|
|
||||||
// registry.addInterceptor(apiInterceptor)
|
|
||||||
// .addPathPatterns("/api/**");
|
|
||||||
// registry.addInterceptor(localeInterceptor)
|
|
||||||
// .addPathPatterns("/admin.*")
|
|
||||||
// .addPathPatterns("/admin/**")
|
|
||||||
// .addPathPatterns("/install");
|
|
||||||
// registry.addInterceptor(localeChangeInterceptor())
|
|
||||||
// .addPathPatterns("/install");
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置静态资源路径
|
* 配置静态资源路径
|
||||||
|
|
|
@ -16,4 +16,9 @@ public class HaloProperties {
|
||||||
* Doc api disabled. (Default is true)
|
* Doc api disabled. (Default is true)
|
||||||
*/
|
*/
|
||||||
private Boolean docDisabled = true;
|
private Boolean docDisabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Production env. (Default is true)
|
||||||
|
*/
|
||||||
|
private Boolean productionEnv = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package cc.ryanc.halo.exception;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication exception.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public class AuthenticationException extends HaloException {
|
||||||
|
|
||||||
|
public AuthenticationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpStatus getStatus() {
|
||||||
|
return HttpStatus.UNAUTHORIZED;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page with title only dto.
|
* Post minimal output dto.
|
||||||
*
|
*
|
||||||
* @author johnniang
|
* @author johnniang
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package cc.ryanc.halo.model.support;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global response entity.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ErrorResponse {
|
||||||
|
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
private String devMessage;
|
||||||
|
|
||||||
|
private Object data;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package cc.ryanc.halo.security.authentication;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.security.support.UserDetail;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public interface Authentication {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user detail.
|
||||||
|
*
|
||||||
|
* @return user detail
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
UserDetail getDetail();
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package cc.ryanc.halo.security.authentication;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.security.support.UserDetail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication implementation.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public class AuthenticationImpl implements Authentication {
|
||||||
|
|
||||||
|
private final UserDetail userDetail;
|
||||||
|
|
||||||
|
public AuthenticationImpl(UserDetail userDetail) {
|
||||||
|
this.userDetail = userDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetail getDetail() {
|
||||||
|
return userDetail;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package cc.ryanc.halo.security.context;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.security.authentication.Authentication;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security context interface.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public interface SecurityContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently authenticated principal.
|
||||||
|
*
|
||||||
|
* @return the Authentication or null if authentication information is unavailable
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Authentication getAuthentication();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the currently authenticated principal, or removes the authentication information.
|
||||||
|
*
|
||||||
|
* @param authentication the new authentication or null if no further authentication should not be stored
|
||||||
|
*/
|
||||||
|
void setAuthentication(@Nullable Authentication authentication);
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package cc.ryanc.halo.security.context;
|
||||||
|
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security context holder.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 12/11/18
|
||||||
|
*/
|
||||||
|
public class SecurityContextHolder {
|
||||||
|
|
||||||
|
private final static ThreadLocal<SecurityContext> CONTEXT_HOLDER = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private SecurityContextHolder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets context.
|
||||||
|
*
|
||||||
|
* @return security context
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static SecurityContext getContext() {
|
||||||
|
// Get from thread local
|
||||||
|
SecurityContext context = CONTEXT_HOLDER.get();
|
||||||
|
if (context == null) {
|
||||||
|
// If no context is available now then create an empty context
|
||||||
|
context = createEmptyContext();
|
||||||
|
// Set to thread local
|
||||||
|
CONTEXT_HOLDER.set(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets security context.
|
||||||
|
*
|
||||||
|
* @param context security context
|
||||||
|
*/
|
||||||
|
public static void setContext(@Nullable SecurityContext context) {
|
||||||
|
CONTEXT_HOLDER.set(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears context.
|
||||||
|
*/
|
||||||
|
public static void clearContext() {
|
||||||
|
CONTEXT_HOLDER.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty security context.
|
||||||
|
*
|
||||||
|
* @return an empty security context
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
private static SecurityContext createEmptyContext() {
|
||||||
|
return new SecurityContextImpl();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package cc.ryanc.halo.security.context;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.security.authentication.Authentication;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security context implementation.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SecurityContextImpl implements SecurityContext {
|
||||||
|
|
||||||
|
private Authentication authentication;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication getAuthentication() {
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthentication(Authentication authentication) {
|
||||||
|
this.authentication = authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package cc.ryanc.halo.security.filter;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.security.handler.AuthenticationFailureHandler;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin authentication filter.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public class AdminAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private AuthenticationFailureHandler failureHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
// TODO Handle admin authentication
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
|
||||||
|
this.failureHandler = failureHandler;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package cc.ryanc.halo.security.filter;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.security.handler.AuthenticationFailureHandler;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Api authentication Filter
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public class ApiAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private AuthenticationFailureHandler failureHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
// TODO Handle authentication
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
|
||||||
|
this.failureHandler = failureHandler;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package cc.ryanc.halo.security.handler;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.exception.HaloException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication failure handler.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public interface AuthenticationFailureHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls when a user has been unsuccessfully authenticated.
|
||||||
|
*
|
||||||
|
* @param request http servlet request
|
||||||
|
* @param response http servlet response
|
||||||
|
* @param exception api exception
|
||||||
|
* @throws IOException io exception
|
||||||
|
* @throws ServletException service exception
|
||||||
|
*/
|
||||||
|
void onFailure(HttpServletRequest request, HttpServletResponse response, HaloException exception) throws IOException, ServletException;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package cc.ryanc.halo.security.handler;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.exception.HaloException;
|
||||||
|
import cc.ryanc.halo.model.support.ErrorResponse;
|
||||||
|
import cc.ryanc.halo.utils.ExceptionUtils;
|
||||||
|
import cn.hutool.extra.servlet.ServletUtil;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default AuthenticationFailureHandler.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 12/12/18
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||||
|
|
||||||
|
private final boolean productionEnv;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public DefaultAuthenticationFailureHandler(boolean productionEnv,
|
||||||
|
ObjectMapper objectMapper) {
|
||||||
|
this.productionEnv = productionEnv;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(HttpServletRequest request, HttpServletResponse response, HaloException exception) throws IOException, ServletException {
|
||||||
|
log.warn("Handle unsuccessful authentication, ip: [{}]", ServletUtil.getClientIP(request));
|
||||||
|
|
||||||
|
ErrorResponse errorDetail = new ErrorResponse();
|
||||||
|
|
||||||
|
errorDetail.setMessage(exception.getMessage());
|
||||||
|
|
||||||
|
if (!productionEnv) {
|
||||||
|
errorDetail.setDevMessage(ExceptionUtils.getStackTrace(exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Response error: [{}]", errorDetail);
|
||||||
|
|
||||||
|
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||||
|
response.setStatus(exception.getStatus().value());
|
||||||
|
response.getWriter().write(objectMapper.writeValueAsString(errorDetail));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package cc.ryanc.halo.security.resolver;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.exception.AuthenticationException;
|
||||||
|
import cc.ryanc.halo.model.entity.User;
|
||||||
|
import cc.ryanc.halo.security.authentication.Authentication;
|
||||||
|
import cc.ryanc.halo.security.context.SecurityContextHolder;
|
||||||
|
import cc.ryanc.halo.security.support.UserDetail;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication argument resolver.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 12/11/18
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class AuthenticationArgumentResolver implements HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
public AuthenticationArgumentResolver() {
|
||||||
|
log.debug("Initializing AuthenticationArgumentResolver");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
|
Class<?> parameterType = parameter.getParameterType();
|
||||||
|
return (Authentication.class.isAssignableFrom(parameterType) ||
|
||||||
|
UserDetail.class.isAssignableFrom(parameterType) ||
|
||||||
|
User.class.isAssignableFrom(parameterType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
|
||||||
|
log.debug("Handle AuthenticationArgument");
|
||||||
|
|
||||||
|
Class<?> parameterType = parameter.getParameterType();
|
||||||
|
|
||||||
|
Authentication authentication = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
|
||||||
|
.orElseThrow(() -> new AuthenticationException("You haven't signed in yet"));
|
||||||
|
|
||||||
|
if (Authentication.class.isAssignableFrom(parameterType)) {
|
||||||
|
return authentication;
|
||||||
|
} else if (UserDetail.class.isAssignableFrom(parameterType)) {
|
||||||
|
return authentication.getDetail();
|
||||||
|
} else if (User.class.isAssignableFrom(parameterType)) {
|
||||||
|
return authentication.getDetail().getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should never happen...
|
||||||
|
throw new UnsupportedOperationException("Unknown parameter type: " + parameterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package cc.ryanc.halo.security.support;
|
||||||
|
|
||||||
|
import cc.ryanc.halo.exception.AuthenticationException;
|
||||||
|
import cc.ryanc.halo.model.entity.User;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User detail.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class UserDetail {
|
||||||
|
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets user info.
|
||||||
|
*
|
||||||
|
* @return user info
|
||||||
|
* @throws AuthenticationException throws if the user is null
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
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.data.web.PageableDefault;
|
||||||
import org.springframework.data.web.SortDefault;
|
import org.springframework.data.web.SortDefault;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
@ -40,12 +41,12 @@ public class PostController {
|
||||||
* @param status post status
|
* @param status post status
|
||||||
* @param page current page
|
* @param page current page
|
||||||
* @param sort sort
|
* @param sort sort
|
||||||
*
|
|
||||||
* @return template path: admin/admin_post.ftl
|
* @return template path: admin/admin_post.ftl
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public String posts(Model model,
|
public String posts(Model model,
|
||||||
@RequestParam(value = "status", defaultValue = "0") PostStatus status,
|
@RequestParam(value = "status", defaultValue = "PUBLISHED") PostStatus status,
|
||||||
|
@PageableDefault Pageable defaultPageable,
|
||||||
@RequestParam(value = "page", defaultValue = "0") Integer page,
|
@RequestParam(value = "page", defaultValue = "0") Integer page,
|
||||||
@SortDefault.SortDefaults({
|
@SortDefault.SortDefaults({
|
||||||
@SortDefault(sort = "postPriority", direction = DESC),
|
@SortDefault(sort = "postPriority", direction = DESC),
|
||||||
|
|
Loading…
Reference in New Issue