sequence
This article describes how to customize the Login page of Spring Security. Most of the information on the web is outdated and based on back-end template technology, which is not very clear. This article provides a front and back end separation of login and return using Ajax.
Ajax returns
There are three areas that need to be handled: exception handling, which needs to be compatible with Ajax requests, success return handling, and failure return handling.
Ajax exception handling
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
if(isAjaxRequest(request)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage());
}else{
response.sendRedirect("/login.html");
}
}
public static boolean isAjaxRequest(HttpServletRequest request) {
String ajaxFlag = request.getHeader("X-Requested-With");
returnajaxFlag ! = null &&"XMLHttpRequest".equals(ajaxFlag); }}Copy the code
Here we’re customizing the ajax returns for success and failure, but here we’re simply going to return statusCode
AjaxAuthSuccessHandler
public class AjaxAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.setStatus(HttpServletResponse.SC_OK); }}Copy the code
AjaxAuthFailHandler
public class AjaxAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed"); }}Copy the code
Security configuration
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint()) .and() .csrf().disable() .authorizeRequests() .antMatchers("/login"."/css/**"."/js/**"."/fonts/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.usernameParameter("name")
.passwordParameter("password") .successHandler(new AjaxAuthSuccessHandler()) .failureHandler(new AjaxAuthFailHandler()) .permitAll() .and() .logout() .logoutUrl("/logout")
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER"); }}Copy the code
Here are a few things to note:
- PermitAll adds the front-end resource path and the interface address /login requested by the login form
- LoginPage Sets the address of the login page. In this case, we use the static page, that is, login.html in the static directory
- The Ajax configuration sets authenticationEntryPoint, successHandler, and failureHandler as the custom Ajax processing classes above
The login page
Is a pure HTML page with the following Ajax request for the login button:
$.ajax({ url: '/login'.type: 'POST', data: "name="+name+"&password="+password, success: function (res, status) { window.location.href='/ok.html' }, error: function(res, status) { dangerDialog(res.statusText); }});Copy the code
This is the request /login path that Spring Security intercepts by default. For those of you who are not familiar with Spring Security, you may wonder if I requested this path, but there is no request mapping for /login in the project. Let’s take a look.
Various filters built into Spring Security:
Alias | Filter Class | Namespace Element or Attribute |
---|---|---|
CHANNEL_FILTER | ChannelProcessingFilter | http/intercept-url@requires-channel |
SECURITY_CONTEXT_FILTER | SecurityContextPersistenceFilter | http |
CONCURRENT_SESSION_FILTER | ConcurrentSessionFilter | session-management/concurrency-control |
HEADERS_FILTER | HeaderWriterFilter | http/headers |
CSRF_FILTER | CsrfFilter | http/csrf |
LOGOUT_FILTER | LogoutFilter | http/logout |
X509_FILTER | X509AuthenticationFilter | http/x509 |
PRE_AUTH_FILTER | AbstractPreAuthenticatedProcessingFilter Subclasses | N/A |
CAS_FILTER | CasAuthenticationFilter | N/A |
FORM_LOGIN_FILTER | UsernamePasswordAuthenticationFilter | http/form-login |
BASIC_AUTH_FILTER | BasicAuthenticationFilter | http/http-basic |
SERVLET_API_SUPPORT_FILTER | SecurityContextHolderAwareRequestFilter | http/@servlet-api-provision |
JAAS_API_SUPPORT_FILTER | JaasApiIntegrationFilter | http/@jaas-api-provision |
REMEMBER_ME_FILTER | RememberMeAuthenticationFilter | http/remember-me |
ANONYMOUS_FILTER | AnonymousAuthenticationFilter | http/anonymous |
SESSION_MANAGEMENT_FILTER | SessionManagementFilter | session-management |
EXCEPTION_TRANSLATION_FILTER | ExceptionTranslationFilter | http |
FILTER_SECURITY_INTERCEPTOR | FilterSecurityInterceptor | http |
SWITCH_USER_FILTER | SwitchUserFilter | N/A |
We need to focus on is the UsernamePasswordAuthenticationFilter here, as the name implies, it is a filter, when executed/login request to intercept, so it is no need to define the login request is in engineering mapping.
UsernamePasswordAuthenticationFilter
Spring ws-security – web – RELEASE 4.2.3 – sources. The jar! /org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if(postOnly && ! request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
returnthis.getAuthenticationManager().authenticate(authRequest); } / /... }Copy the code
Here is the interception, taking the parameters submitted by login.html and passing them to the authenticationManager for authentication. The subsequent filter is followed. If successful, the corresponding session is configured.
doc
- Spring Security dynamically configures URL permissions
- Spring Security Notes: Customize Login/Logout Filter, AuthenticationProvider, AuthenticationToken