需求:/oauth/authorize请求除标准参数外,有自定义参数,这些参数要用在spring security认证流程中,UserDetailsService的实现有多个,要根据自定义参数来区分
框架:spring-cloud-starter-oauth2,jwt
要解决的问题:1.LoginUrlAuthenticationEntryPoint.buildRedirectUrlToLoginPage方法返回的重定向url没有加queryString,2.生成默认登录页面的过滤器的url匹配是全路径匹配,会包含queryString,3.UsernamePasswordAuthenticationFilter中使用的WebAuthenticationDetailsSource只处理了remoteAddress和sessionId两个参数,没有处理自定义参数
实现:1.自定义CustomLoginUrlAuthenticationEntryPoint,代码如下
package org.liu.auth.entrypoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.RedirectUrlBuilder;
import org.springframework.security.web.util.UrlUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 自定义拒绝访问时redirect到登录url的entryPoint
*
* @Author lzs
* @Date 2022/8/18 10:19
**/
@Slf4j
public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public CustomLoginUrlAuthenticationEntryPoint() {
this("/login");
}
public CustomLoginUrlAuthenticationEntryPoint(String loginFormUrl) {
super(loginFormUrl);
}
@Override
protected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
String loginForm = determineUrlToUseForThisRequest(request, response,
authException);
if (UrlUtils.isAbsoluteUrl(loginForm)) {
return loginForm;
}
int serverPort = super.getPortResolver().getServerPort(request);
String scheme = request.getScheme();
RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();
urlBuilder.setScheme(scheme);
urlBuilder.setServerName(request.getServerName());
urlBuilder.setPort(serverPort);
urlBuilder.setContextPath(request.getContextPath());
urlBuilder.setPathInfo(loginForm);
urlBuilder.setQuery(request.getQueryString());
if (super.isForceHttps() && "http".equals(scheme)) {
Integer httpsPort = super.getPortMapper().lookupHttpsPort(serverPort);
if (httpsPort != null) {
// Overwrite scheme and port in the redirect URL
urlBuilder.setScheme("https");
urlBuilder.setPort(httpsPort);
} else {
log.warn("Unable to redirect to HTTPS as no port mapping found for HTTP port "
+ serverPort);
}
}
return urlBuilder.getUrl();
}
}
2.自定义CustomDefaultLoginPageGeneratingFilter,CustomDefaultLoginPageConfigurer
package org.liu.auth.filter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.util.HtmlUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
/**
* 重写了DefaultLoginPageGeneratingFilter,只是修改了isLoginUrlRequest方法的判断逻辑
*
* @Author lzs
* @Date 2022/8/19 8:43
**/
public class CustomDefaultLoginPageGeneratingFilter extends GenericFilterBean {
public static final String DEFAULT_LOGIN_PAGE_URL = "/login";
public static final String ERROR_PARAMETER_NAME = "error";
private String loginPageUrl;
private String logoutSuccessUrl;
private String failureUrl;
private boolean formLoginEnabled;
private boolean openIdEnabled;
private boolean oauth2LoginEnabled;
private boolean saml2LoginEnabled;
private String authenticationUrl;
private String usernameParameter;
private String passwordParameter;
private String rememberMeParameter;
private String openIDauthenticationUrl;
private String openIDusernameParameter;
private String openIDrememberMeParameter;
private Map<String, String> oauth2AuthenticationUrlToClientName;
private Map<String, String> saml2AuthenticationUrlToProviderName;
private Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = request -> Collections
.emptyMap();
public CustomDefaultLoginPageGeneratingFilter() {
}
public CustomDefaultLoginPageGeneratingFilter(AbstractAuthenticationProcessingFilter filter) {
if (filter instanceof UsernamePasswordAuthenticationFilter) {
init((UsernamePasswordAuthenticationFilter) filter, null);
} else {
init(null, filter);
}
}
public CustomDefaultLoginPageGeneratingFilter(
UsernamePasswordAuthenticationFilter authFilter,
AbstractAuthenticationProcessingFilter openIDFilter) {
init(authFilter, openIDFilter);
}
private void init(UsernamePasswordAuthenticationFilter authFilter,
AbstractAuthenticationProcessingFilter openIDFilter) {
this.loginPageUrl = DEFAULT_LOGIN_PAGE_URL;
this.logoutSuccessUrl = DEFAULT_LOGIN_PAGE_URL + "?logout";
this.failureUrl = DEFAULT_LOGIN_PAGE_URL + "?" + ERROR_PARAMETER_NAME;
if (authFilter != null) {
formLoginEnabled = true;
usernameParameter = authFilter.getUsernameParameter();
passwordParameter = authFilter.getPasswordParameter();
if (authFilter.getRememberMeServices() instanceof AbstractRememberMeServices) {
rememberMeParameter = ((AbstractRememberMeServices) authFilter
.getRememberMeServices()).getParameter();
}
}
if (openIDFilter != null) {
openIdEnabled = true;
openIDusernameParameter = "openid_identifier";
if (openIDFilter.getRememberMeServices() instanceof AbstractRememberMeServices) {
openIDrememberMeParameter = ((AbstractRememberMeServices) openIDFilter
.getRememberMeServices()).getParameter();
}
}
}
/**
* Sets a Function used to resolve a Map of the hidden inputs where the key is the
* name of the input and the value is the value of the input. Typically this is used
* to resolve the CSRF token.
*
* @param resolveHiddenInputs the function to resolve the inputs
*/
public void setResolveHiddenInputs(
Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs) {
Assert.notNull(resolveHiddenInputs, "resolveHiddenInputs cannot be null");
this.resolveHiddenInputs = resolveHiddenInputs;
}
public boolean isEnabled() {
return formLoginEnabled || openIdEnabled || oauth2LoginEnabled || this.saml2LoginEnabled;
}
public void setLogoutSuccessUrl(String logoutSuccessUrl) {
this.logoutSuccessUrl = logoutSuccessUrl;
}
public String getLoginPageUrl() {
return loginPageUrl;
}
public void setLoginPageUrl(String loginPageUrl) {
this.loginPageUrl = loginPageUrl;
}
public void setFailureUrl(String failureUrl) {
this.failureUrl = failureUrl;
}
public void setFormLoginEnabled(boolean formLoginEnabled) {
this.formLoginEnabled = formLoginEnabled;
}
public void setOpenIdEnabled(boolean openIdEnabled) {
this.openIdEnabled = openIdEnabled;
}
public void setOauth2LoginEnabled(boolean oauth2LoginEnabled) {
this.oauth2LoginEnabled = oauth2LoginEnabled;
}
public void setSaml2LoginEnabled(boolean saml2LoginEnabled) {
this.saml2LoginEnabled = saml2LoginEnabled;
}
public void setAuthenticationUrl(String authenticationUrl) {
this.authenticationUrl = authenticationUrl;
}
public void setUsernameParameter(String usernameParameter) {
this.usernameParameter = usernameParameter;
}
public void setPasswordParameter(String passwordParameter) {
this.passwordParameter = passwordParameter;
}
public void setRememberMeParameter(String rememberMeParameter) {
this.rememberMeParameter = rememberMeParameter;
this.openIDrememberMeParameter = rememberMeParameter;
}
public void setOpenIDauthenticationUrl(String openIDauthenticationUrl) {
this.openIDauthenticationUrl = openIDauthenticationUrl;
}
public void setOpenIDusernameParameter(String openIDusernameParameter) {
this.openIDusernameParameter = openIDusernameParameter;
}
public void setOauth2AuthenticationUrlToClientName(Map<String, String> oauth2AuthenticationUrlToClientName) {
this.oauth2AuthenticationUrlToClientName = oauth2AuthenticationUrlToClientName;
}
public void setSaml2AuthenticationUrlToProviderName(Map<String, String> saml2AuthenticationUrlToProviderName) {
this.saml2AuthenticationUrlToProviderName = saml2AuthenticationUrlToProviderName;
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
boolean loginError = isErrorPage(request);
boolean logoutSuccess = isLogoutSuccess(request);
if (isLoginUrlRequest(request) || loginError || logoutSuccess) {
String loginPageHtml = generateLoginPageHtml(request, loginError,
logoutSuccess);
response.setContentType("text/html;charset=UTF-8");
response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);
response.getWriter().write(loginPageHtml);
return;
}
chain.doFilter(request, response);
}
private String generateLoginPageHtml(HttpServletRequest request, boolean loginError,
boolean logoutSuccess) {
String errorMsg = "Invalid credentials";
if (loginError) {
HttpSession session = request.getSession(false);
if (session != null) {
AuthenticationException ex = (AuthenticationException) session
.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
errorMsg = ex != null ? ex.getMessage() : "Invalid credentials";
}
}
StringBuilder sb = new StringBuilder();
sb.append("<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ " <head>\n"
+ " <meta charset=\"utf-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
+ " <meta name=\"description\" content=\"\">\n"
+ " <meta name=\"author\" content=\"\">\n"
+ " <title>Please sign in</title>\n"
+ " <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
+ " <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"
+ " </head>\n"
+ " <body>\n"
+ " <div class=\"container\">\n");
String contextPath = request.getContextPath();
if (this.formLoginEnabled) {
sb.append(" <form class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.authenticationUrl + "\">\n"
+ " <h2 class=\"form-signin-heading\">Please sign in</h2>\n"
+ createError(loginError, errorMsg)
+ createLogoutSuccess(logoutSuccess)
+ " <p>\n"
+ " <label for=\"username\" class=\"sr-only\">Username</label>\n"
+ " <input type=\"text\" id=\"username\" name=\"" + this.usernameParameter + "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n"
+ " </p>\n"
+ " <p>\n"
+ " <label for=\"password\" class=\"sr-only\">Password</label>\n"
+ " <input type=\"password\" id=\"password\" name=\"" + this.passwordParameter + "\" class=\"form-control\" placeholder=\"Password\" required>\n"
+ " </p>\n"
+ createRememberMe(this.rememberMeParameter)
+ renderHiddenInputs(request)
+ " <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n"
+ " </form>\n");
}
if (openIdEnabled) {
sb.append(" <form name=\"oidf\" class=\"form-signin\" method=\"post\" action=\"" + contextPath + this.openIDauthenticationUrl + "\">\n"
+ " <h2 class=\"form-signin-heading\">Login with OpenID Identity</h2>\n"
+ createError(loginError, errorMsg)
+ createLogoutSuccess(logoutSuccess)
+ " <p>\n"
+ " <label for=\"username\" class=\"sr-only\">Identity</label>\n"
+ " <input type=\"text\" id=\"username\" name=\"" + this.openIDusernameParameter + "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n"
+ " </p>\n"
+ createRememberMe(this.openIDrememberMeParameter)
+ renderHiddenInputs(request)
+ " <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n"
+ " </form>\n");
}
if (oauth2LoginEnabled) {
sb.append("<h2 class=\"form-signin-heading\">Login with OAuth 2.0</h2>");
sb.append(createError(loginError, errorMsg));
sb.append(createLogoutSuccess(logoutSuccess));
sb.append("<table class=\"table table-striped\">\n");
for (Map.Entry<String, String> clientAuthenticationUrlToClientName : oauth2AuthenticationUrlToClientName.entrySet()) {
sb.append(" <tr><td>");
String url = clientAuthenticationUrlToClientName.getKey();
sb.append("<a href=\"").append(contextPath).append(url).append("\">");
String clientName = HtmlUtils.htmlEscape(clientAuthenticationUrlToClientName.getValue());
sb.append(clientName);
sb.append("</a>");
sb.append("</td></tr>\n");
}
sb.append("</table>\n");
}
if (this.saml2LoginEnabled) {
sb.append("<h2 class=\"form-signin-heading\">Login with SAML 2.0</h2>");
sb.append(createError(loginError, errorMsg));
sb.append(createLogoutSuccess(logoutSuccess));
sb.append("<table class=\"table table-striped\">\n");
for (Map.Entry<String, String> relyingPartyUrlToName : saml2AuthenticationUrlToProviderName.entrySet()) {
sb.append(" <tr><td>");
String url = relyingPartyUrlToName.getKey();
sb.append("<a href=\"").append(contextPath).append(url).append("\">");
String partyName = HtmlUtils.htmlEscape(relyingPartyUrlToName.getValue());
sb.append(partyName);
sb.append("</a>");
sb.append("</td></tr>\n");
}
sb.append("</table>\n");
}
sb.append("</div>\n");
sb.append("</body></html>");
return sb.toString();
}
private String renderHiddenInputs(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> input : this.resolveHiddenInputs.apply(request).entrySet()) {
sb.append("<input name=\"").append(input.getKey()).append("\" type=\"hidden\" value=\"").append(input.getValue()).append("\" />\n");
}
return sb.toString();
}
private String createRememberMe(String paramName) {
if (paramName == null) {
return "";
}
return "<p><input type='checkbox' name='"
+ paramName
+ "'/> Remember me on this computer.</p>\n";
}
private boolean isLogoutSuccess(HttpServletRequest request) {
return logoutSuccessUrl != null && matches(request, logoutSuccessUrl);
}
/**
* change this method only
*
* @param request
* @return
*/
private boolean isLoginUrlRequest(HttpServletRequest request) {
return matchesUri(request, loginPageUrl);
}
private boolean isErrorPage(HttpServletRequest request) {
return matches(request, failureUrl);
}
private static String createError(boolean isError, String message) {
return isError ? "<div class=\"alert alert-danger\" role=\"alert\">" + HtmlUtils.htmlEscape(message) + "</div>" : "";
}
private static String createLogoutSuccess(boolean isLogoutSuccess) {
return isLogoutSuccess ? "<div class=\"alert alert-success\" role=\"alert\">You have been signed out</div>" : "";
}
private boolean matchesUri(HttpServletRequest request, String url) {
if (!"GET".equals(request.getMethod()) || url == null) {
return false;
}
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.equals(url);
}
return uri.equals(request.getContextPath() + url);
}
private boolean matches(HttpServletRequest request, String url) {
if (!"GET".equals(request.getMethod()) || url == null) {
return false;
}
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if (request.getQueryString() != null) {
uri += "?" + request.getQueryString();
}
if ("".equals(request.getContextPath())) {
return uri.equals(url);
}
return uri.equals(request.getContextPath() + url);
}
}
package org.liu.auth.configurer;
import org.liu.auth.filter.CustomDefaultLoginPageGeneratingFilter;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
import org.springframework.security.web.csrf.CsrfToken;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* 自定义默认登陆页面的配置
*
* @Author lzs
* @Date 2022/8/18 17:24
**/
public class CustomDefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<DefaultLoginPageConfigurer<H>, H> {
private CustomDefaultLoginPageGeneratingFilter loginPageGeneratingFilterFilter = new CustomDefaultLoginPageGeneratingFilter();
private DefaultLogoutPageGeneratingFilter logoutPageGeneratingFilter = new DefaultLogoutPageGeneratingFilter();
@Override
public void init(H http) {
initDefaultLoginFilter(http);
initDefaultLogoutFilter(http);
}
@Override
public void configure(H http) {
if (loginPageGeneratingFilterFilter.isEnabled()) {
loginPageGeneratingFilterFilter = postProcess(loginPageGeneratingFilterFilter);
http.addFilterAfter(loginPageGeneratingFilterFilter, UsernamePasswordAuthenticationFilter.class);
http.addFilter(this.logoutPageGeneratingFilter);
}
}
private void initDefaultLoginFilter(H http) {
Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = request -> {
Map<String, String> map = new HashMap<>();
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (token != null) {
map.put(token.getParameterName(), token.getToken());
}
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String paramName = parameterNames.nextElement();
String paramValue = request.getParameter(paramName);
map.put(paramName, paramValue);
}
return map;
};
loginPageGeneratingFilterFilter.setFormLoginEnabled(true);
loginPageGeneratingFilterFilter.setUsernameParameter("username");
loginPageGeneratingFilterFilter.setPasswordParameter("password");
loginPageGeneratingFilterFilter.setLoginPageUrl("/login");
loginPageGeneratingFilterFilter.setFailureUrl("/login?error");
loginPageGeneratingFilterFilter.setAuthenticationUrl("/login");
loginPageGeneratingFilterFilter.setResolveHiddenInputs(resolveHiddenInputs);
http.setSharedObject(CustomDefaultLoginPageGeneratingFilter.class, loginPageGeneratingFilterFilter);
}
private void initDefaultLogoutFilter(H http) {
Function<HttpServletRequest, Map<String, String>> hiddenInputs = request -> {
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (token == null) {
return Collections.emptyMap();
}
return Collections.singletonMap(token.getParameterName(), token.getToken());
};
this.logoutPageGeneratingFilter.setResolveHiddenInputs(hiddenInputs);
http.setSharedObject(DefaultLogoutPageGeneratingFilter.class,
logoutPageGeneratingFilter);
}
}
3.自定义CustomWebAuthenticationDetailsSource,CustomWebAuthenticationDetails
package org.liu.auth.authentication;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import static org.liu.common.core.constants.CommonConstants.HEADER_CLIENT;
import static org.liu.common.core.constants.CommonConstants.HEADER_TENANT_ID;
/**
* @Author lzs
* @Date 2022/8/15 16:15
**/
public class CustomWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, CustomWebAuthenticationDetails> {
private RequestCache requestCache = new HttpSessionRequestCache();
@Override
public CustomWebAuthenticationDetails buildDetails(HttpServletRequest context) {
String client = context.getHeader(HEADER_CLIENT);
if (!StringUtils.hasText(client)) {
client = context.getParameter(HEADER_CLIENT);
}
Long tenantId = null;
String tenantIdStr = context.getHeader(HEADER_TENANT_ID);
if (StringUtils.hasText(tenantIdStr)) {
tenantId = Long.parseLong(tenantIdStr);
} else {
tenantIdStr = context.getParameter(HEADER_TENANT_ID);
if (StringUtils.hasText(tenantIdStr)) {
tenantId = Long.parseLong(tenantIdStr);
}
}
return new CustomWebAuthenticationDetails(context, client, tenantId);
}
}
package org.liu.auth.authentication;
import lombok.Getter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import javax.servlet.http.HttpServletRequest;
/**
* @Author lzs
* @Date 2022/8/15 16:17
**/
@Getter
public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
private final String client;
private final Long tenantId;
public CustomWebAuthenticationDetails(HttpServletRequest request, String client, Long tenantId) {
super(request);
this.client = client;
this.tenantId = tenantId;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CustomWebAuthenticationDetails) {
CustomWebAuthenticationDetails details = (CustomWebAuthenticationDetails) obj;
if (null != client && null == details.getClient()) {
return false;
}
if (null == client && null != details.getClient()) {
return false;
}
if (null != client && !client.equals(details.getClient())) {
return false;
}
if (null != tenantId && null == details.getTenantId()) {
return false;
}
if (null == tenantId && null != details.getTenantId()) {
return false;
}
if (null != tenantId && tenantId.equals(details.getTenantId())) {
return false;
}
} else {
return false;
}
return super.equals(obj);
}
@Override
public int hashCode() {
int code = super.hashCode();
if (null != client) {
code = code * (this.client.hashCode() % 7);
}
if (null != tenantId) {
code = code * (this.tenantId.hashCode() % 7);
}
return code;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(": ");
sb.append("client: ").append(this.getClient()).append("; ");
sb.append("tenantId: ").append(this.getTenantId());
return sb.toString();
}
}
4.把上面自定义的配置添加到httpSecurity中
http.formLogin().authenticationDetailsSource(customWebAuthenticationDetailsSource);
http.exceptionHandling().authenticationEntryPoint(customLoginUrlAuthenticationEntryPoint);
http.removeConfigurer(DefaultLoginPageConfigurer.class);
http.apply(new CustomDefaultLoginPageConfigurer<>());