spring security is a large module, and this article only involves authentication of custom parameters. The default verification parameters of spring security are only username and password, which are generally not enough. Because time has passed too long, some forgetting and some of them may be missing. OK, no nonsense.
The spring and spring security configurations use javaConfig, and the versions are 4.2.5 and 4.0.4 in turn.
Overall idea: Customize EntryPoint, add custom parameters to extend AuthenticationToken and AuthenticationProvider for verification.
First define EntryPoint:
import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class MyAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { public MyAuthenticationEntryPoint(String loginFormUrl) { super(loginFormUrl); } @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { super.commence(request, response, authException); }}Next is token, validCode is the verification code parameter:
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;public class MyUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { private String validCode; public MyUsernamePasswordAuthenticationToken(String principal, String credentials, String validCode) { super(principal, credentials); this.validCode = validCode; } public String getValidCode() { return validCode; } public void setValidCode(String validCode) { this.validCode = validCode; }}Continue ProcessingFilter,
import com.core.shared.ValidateCodeHandle;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;public class MyValidCodeProcessingFilter extends AbstractAuthenticationProcessingFilter { private String usernameParam = "username"; private String passwordParam = "password"; private String validCodeParam = "validateCode"; public MyValidCodeProcessingFilter() { super(new AntPathRequestMatcher("/user/login", "POST")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { String username = request.getParameter(usernameParam); String password = request.getParameter(passwordParam); String validCode = request.getParameter(validCodeParam); valid(validCode, request.getSession()); MyUsernamePasswordAuthenticationToken token = new MyUsernamePasswordAuthenticationToken(username, password, validCode); return this.getAuthenticationManager().authenticate(token); } public void valid(String validCode, HttpSession session) { if (validCode == null) { throw new ValidCodeErrorException("ValidateCode is empty!"); } if (!ValidateCodeHandle.matchCode(session.getId(), validCode)) { throw new ValidCodeErrorException("ValidCodeError!"); } }} Define three parameters separately, which are used to receive the parameters from the login form. The construction method gives the login url and the post method required
Next is authentication. Before the authentication username and password have arrived, I just authenticated the verification code.
Here is a tool class ValidateCodeHandle and ValidCodeErrorException:
import java.util.concurrent.ConcurrentHashMap;public class ValidateCodeHandle { private static ConcurrentHashMap validateCode = new ConcurrentHashMap<>(); public static ConcurrentHashMap getCode() { return validateCode; } public static void save(String sessionId, String code) { validateCode.put(sessionId, code); } public static String getValidateCode(String sessionId) { Object obj = validateCode.get(sessionId); if (obj != null) { return String.valueOf(obj); } return null; } public static boolean matchCode(String sessionId, String inputCode) { String saveCode = getValidateCode(sessionId); if (saveCode.equals(inputCode)) { return true; } return false; }}Here you need to inherit AuthenticationException to indicate that it is a security authentication failure, so that the subsequent failure process will be followed.
import org.springframework.security.core.AuthenticationException;public class ValidCodeErrorException extends AuthenticationException { public ValidCodeErrorException(String msg) { super(msg); } public ValidCodeErrorException(String msg, Throwable t) { super(msg, t); }}Next is Provider:
import org.springframework.security.authentication.BadCredentialsException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.authentication.dao.DaoAuthenticationProvider;import org.springframework.security.core.AuthenticationException;import org.springframework.security.core.userdetails.UserDetails;public class MyAuthenticationProvider extends DaoAuthenticationProvider { @Override public boolean supports(Class<?> authentication) { return MyUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { Object salt = null; if (getSaltSource() != null) { salt = getSaltSource().getSalt(userDetails); } if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException("Username or password error!"); } String presentedPassword = authentication.getCredentials().toString(); if (!this.getPasswordEncoder().isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) { logger.debug("Authentication failed: password does not match stored value"); throw new BadCredentialsException("Under username or password!"); } }} The supports method specifies the use of a custom token, and the additionalAuthenticationChecks method is exactly the same as the parent class's logic, I just changed the information returned by the exception.
Next is the handler that handles the successful authentication and failed authentication
import org.springframework.security.core.Authentication;import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class FrontAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { public FrontAuthenticationSuccessHandler(String defaultTargetUrl) { super(defaultTargetUrl); } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { super.onAuthenticationSuccess(request, response, authentication); }} import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class FrontAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { public FrontAuthenticationFailureHandler(String defaultFailureUrl) { super(defaultFailureUrl); } @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { super.onAuthenticationFailure(request, response, exception); }}Finally, the most important security config:
import com.service.user.CustomerService;import com.web.filter.SiteMeshFilter;import com.web.mySecurity.*;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.authentication.ProviderManager;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.crypto.password.StandardPasswordEncoder;import org.springframework.security.web.access.ExceptionTranslationFilter;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;import org.springframework.web.filter.CharacterEncodingFilter;import javax.servlet.DispatcherType;import javax.servlet.FilterRegistration;import javax.servlet.ServletContext;import java.util.ArrayList;import java.util.EnumSet;import java.util.List;@Configuration@EnableWebSecuritypublic class SecurityConfig extends AbstractSecurityWebApplicationInitializer { @Bean public PasswordEncoder passwordEncoder() { return new StandardPasswordEncoder("MD5"); } @Autowired private CustomerService customerService; @Configuration @Order(1) public static class FrontendWebSecurityConfigureAdapter extends WebSecurityConfigurerAdapter { @Autowired private MyValidCodeProcessingFilter myValidCodeProcessingFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/user/login", "/user/logout").permitAll() .anyRequest().authenticated() .and() .addFilterBefore(myValidCodeProcessingFilter, UsernamePasswordAuthenticationFilter.class) .formLogin() .loginPage("/user/login") .and() .logout() .logoutUrl("/user/logout") .logoutSuccessUrl("/user/login"); } } @Bean(name = "frontAuthenticationProvider") public MyAuthenticationProvider frontAuthenticationProvider() { MyAuthenticationProvider myAuthenticationProvider = new MyAuthenticationProvider(); myAuthenticationProvider.setUserDetailsService(customerService); myAuthenticationProvider.setPasswordEncoder(passwordEncoder()); return myAuthenticationProvider; } @Bean public AuthenticationManager authenticationManager() { List<AuthenticationProvider> list = new ArrayList<>(); list.add(frontAuthenticationProvider()); AuthenticationManager authenticationManager = new ProviderManager(list); return authenticationManager; } @Bean public MyValidCodeProcessingFilter myValidCodeProcessingFilter(AuthenticationManager authenticationManager) { MyValidCodeProcessingFilter filter = new MyValidCodeProcessingFilter(); filter.setAuthenticationManager(authenticationManager); filter.setAuthenticationSuccessHandler(frontAuthenticationSuccessHandler()); filter.setAuthenticationFailureHandler(frontAuthenticationFailureHandler()); return filter; } @Bean public FrontAuthenticationFailureHandler frontAuthenticationFailureHandler() { return new FrontAuthenticationFailureHandler("/user/login"); } @Bean public FrontAuthenticationSuccessHandler frontAuthenticationSuccessHandler() { return new FrontAuthenticationSuccessHandler("/front/test"); } @Bean public MyAuthenticationEntryPoint myAuthenticationEntryPoint() { return new MyAuthenticationEntryPoint("/user/login"); }}First of all, it is a bean of the encryption class, and customerService is a simple query user
@Service("customerService")public class CustomerServiceImpl implements CustomerService { @Autowired private UserDao userDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userDao.findCustomerByUsername(username); }} Next is FrontendWebSecurityConfigureAdapter. I rewritten the configure method, first disable csrf, enable authorizeRequests(), where "/user/login" and "/user/logout" let go of permission verification, and other requests require login authentication, and then addFilterBefore(). Before adding my custom myValidCodeProcessingFilter to the default UsernamePasswordAuthenticationFilter in security, I first perform my custom parameter authentication, and then formLogin(), configure login url and logout url, and login url, login url, login url, and login url to log out url, that is, I have to write a controller yourself.
Next is the bean display declaration of AuthenticationProvider, AuthenticationManager, ProcessingFilter, AuthenticationFailureHandler, AuthenticationSuccessHandler, and EntryPoint.
Below is login.jsp
<body><div> <form:form autocomplete="false" commandName="userDTO" method="post"> <div> <span><b>${SPRING_SECURITY_LAST_EXCEPTION.message}</b></span> </div> username:<form:input path="username" cssClass="form-control"/><br/> password:<form:password path="password" cssClass="form-control"/><br/> validateCode:<form:input path="validateCode" cssClass="form-control"/> <label>${validate_code}</label> <div> <label> <input type="checkbox" name="remember-me"/> Remember me</label> </div> <input type="submit" value="submit"/> </form:form></div></body>When the verification code fails, the ValidCodeErrorException is thrown. Since it inherits the AuthenticationException, when the security encounters AuthenticationException, the AuthenticationFailureHandler will be triggered. The above bean declares that the authentication failed and jumps to the login url, so there is ${SPRING_SECURITY_LAST_EXCEPTION.message} in login.jsp. The exception information is thrown when I authenticate, which can prompt the user friendly.
The entire custom security verification process is completed
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.