👆👆👆
Don't forget to scan the code to get the information [HD Java learning roadmap]
And [full set of learning videos and supporting materials]
preface
In the previous chapter, brother one by one It shows you how to add and execute custom filters in Spring Security to realize the verification code verification function. This implementation method is only one of the ways to realize the function of authentication code. Next, we will learn another implementation method, which is to use the AuthenticationProvider to realize the function of authentication code. Through this case, we will learn how to customize the AuthenticationProvider.
1, Introduction to certification provider
In the previous chapter, I took you to realize the graphic verification code effect by using the custom filter. Next, we use another way to realize the graphic verification code based on the custom authentication provider.
1. Authentication provider
In Chapter 11, Yige I told you about the authentication and authorization implementation process of Spring Security, in which I explained the role of AuthenticationProvider. Next, let's take a look at the class diagram of AuthenticationProvider interface:
As can be seen from the above figure, AuthenticationProvider is an interface with a direct subclass AbstractUserDetailsAuthenticationProvider, which has two abstract methods: additionalAuthenticationChecks() and retrieveUser(), as shown in the following figure:
We can write a subclass to inherit the abstract user details authentification provider and copy the two abstract methods to meet our own needs. The Dao authen notation provider subclass in Spring Security implements authentication and authorization based on the database model by copying these two abstract methods.
Today, we will implement the verification function of graphic verification code by inheriting DaoAuthenticationProvider.
2. Introduction to webauthenticationdetails class
After understanding the AuthenticationProvider class above, we need to know another class WebAuthenticationDetails.
We know that there is a username password authentication token class in Spring Security, which encapsulates the user's principal and credentials information. This class also inherits the details information from its parent class AbstractAuthenticationToken.
The details information represents the additional information of the authenticated user, such as the remoteAddress and sessionId of the requesting user. These two information are defined in another WebAuthenticationDetails class, so we can use WebAuthenticationDetails to encapsulate the additional information of the user.
After understanding the above necessary API s, we can realize today's requirements.
2, Implement graphic verification code
1. Add dependent packages
As in the previous case, we can first create a new module and create a process strategy.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies>
In this case, we still use the open source verification code solution kaptcha on github, so we need to add the dependency package of kaptcha on the basis of the original project.
2. Create Producer object
As in the previous case, create a CaptchaConfig configuration class, create a Producer object in this class, and configure the verification code object as necessary.
@Configuration public class CaptchaConfig { @Bean public Producer captcha() { // Configure basic parameters of graphic verification code Properties properties = new Properties(); // image width properties.setProperty("kaptcha.image.width", "150"); // Picture length properties.setProperty("kaptcha.image.height", "50"); // character set properties.setProperty("kaptcha.textproducer.char.string", "0123456789"); // Character length properties.setProperty("kaptcha.textproducer.char.length", "4"); Config config = new Config(properties); // Use the default graphic verification code to implement, of course, you can also customize the implementation DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); defaultKaptcha.setConfig(config); return defaultKaptcha; } }
3. Create an interface for generating verification code
After creating the Producer object above, create an interface to generate the verification code, which is responsible for generating the verification code picture and storing the verification code in the session.
@Controller public class CaptchaController { @Autowired private Producer captchaProducer; @GetMapping("/captcha.jpg") public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException { // Set content type response.setContentType("image/jpeg"); // Create verification code text String capText = captchaProducer.createText(); // Set the verification code text to session request.getSession().setAttribute("captcha", capText); // Create verification code picture BufferedImage bi = captchaProducer.createImage(capText); // Get response output stream ServletOutputStream out = response.getOutputStream(); // Write the picture verification code data to the response output stream ImageIO.write(bi, "jpg", out); // Push and close the response output stream try { out.flush(); } finally { out.close(); } } }
4. Custom exception
Next, customize a runtime exception to handle the exception prompt when the verification code fails.
public class VerificationCodeException extends AuthenticationException { public VerificationCodeException() { super("Verification of graphic verification code failed"); } }
5. Customize WebAuthenticationDetails
I introduced WebAuthenticationDetails to you above. I know that this class can encapsulate the user's additional information, so here we customize a WebAuthenticationDetails class to encapsulate the authentication code information, and compare the authentication code passed by the user with the authentication code saved in the session.
/** * Add additional user authentication information */ public class MyWebAuthenticationDetails extends WebAuthenticationDetails { private String imageCode; private String savedImageCode; public String getImageCode() { return imageCode; } public String getSavedImageCode() { return savedImageCode; } /** * Supplement the verification code submitted by the user and the verification code saved by the session */ public MyWebAuthenticationDetails(HttpServletRequest request) { super(request); this.imageCode = request.getParameter("captcha"); //Get session object HttpSession session = request.getSession(); this.savedImageCode = (String) session.getAttribute("captcha"); if (!StringUtils.isEmpty(this.savedImageCode)) { // Clear the verification code casually, whether it is failure or success, so the client should refresh the verification code when login fails session.removeAttribute("captcha"); } } }
6. Customize AuthenticationDetailsSource
AuthenticationDetailsSource is an interface with a buildDetails method. This method will be called when creating a new details object of authentication, and a request parameter can be passed to the details object here, as shown in the following figure:
Therefore, here we define an AuthenticationDetailsSource class, build the WebAuthenticationDetails object defined above through this class, and pass the HttpServletRequest object to WebAuthenticationDetails.
@Component public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,WebAuthenticationDetails> { /** * Create a WebAuthenticationDetails object */ @Override public WebAuthenticationDetails buildDetails(HttpServletRequest request) { return new MyWebAuthenticationDetails(request); } }
7. Customize DaoAuthenticationProvider
Next, by inheriting the DaoAuthenticationProvider parent class, the verification operation of the graphic verification code is introduced.
/** * On top of the conventional database authentication, the function of graphic verification code is added */ @Component public class MyAuthenticationProvider extends DaoAuthenticationProvider { /** * The constructor injects UserDetailService and PasswordEncoder */ public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { this.setUserDetailsService(userDetailsService); this.setPasswordEncoder(passwordEncoder); } /** * Add additional graphic verification code function over conventional authentication */ @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException { //Get the associated details object in the token token and convert it into our custom MyWebAuthenticationDetails MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) usernamePasswordAuthenticationToken.getDetails(); String imageCode = details.getImageCode(); String savedImageCode = details.getSavedImageCode(); // Verify graphic verification code if (StringUtils.isEmpty(imageCode) || StringUtils.isEmpty(savedImageCode) || !imageCode.equals(savedImageCode)) { throw new VerificationCodeException(); } //Before the normal authentication check, add additional verification about the graphic verification code super.additionalAuthenticationChecks(userDetails, usernamePasswordAuthenticationToken); } }
8. Add SecurityConfig
Then create the SecurityConfig class and configure the AuthenticationDetailsSource and AuthenticationProvider classes we wrote earlier.
@SuppressWarnings("all") @EnableWebSecurity(debug = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> myWebAuthenticationDetailsSource; @Autowired private AuthenticationProvider authenticationProvider; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/api/**") .hasRole("ADMIN") .antMatchers("/user/api/**") .hasRole("USER") .antMatchers("/app/api/**", "/captcha.jpg") .permitAll() .anyRequest() .authenticated() .and() .formLogin() //The user-defined AuthenticationDetailsSource is configured here .authenticationDetailsSource(myWebAuthenticationDetailsSource) .failureHandler(new SecurityAuthenticationFailureHandler()) .successHandler(new SecurityAuthenticationSuccessHandler()) .loginPage("/myLogin.html") .loginProcessingUrl("/login") .permitAll() .and() .csrf() .disable(); } //Associate our customized AuthenticationProvider here @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider); } @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } }
9. Write test page
Finally, write a custom login page and add a reference to the verification code interface here. I list the core code of html here.
<body> <div class="login"> <h2>Access Form</h2> <div class="login-top"> <h1>validate logon</h1> <form action="/login" method="post"> <input type="text" name="username" placeholder="username" /> <input type="password" name="password" placeholder="password" /> <div style="display: flex;"> <!-- Input box of new graphic verification code --> <input type="text" name="captcha" placeholder="captcha" /> <!-- Picture pointing to graphic verification code API --> <img src="/captcha.jpg" alt="captcha" height="50px" width="150px" style="margin-left: 20px;"> </div> <div class="forgot"> <a href="#">Forget password</a> <input type="submit" value="Sign in" > </div> </form> </div> <div class="login-bottom"> <h3>new user <a href="#"> note & nbsp; volume</a></h3> </ div> </ div> </ body>
10. Code structure
The main code structure of this case is shown in the figure below. You can create it for reference.
11. Start project testing
Next, we start the project, jump to the login page, and we can see that the verification code has been created.
At this point, we can see the generated digital verification code. After we enter the correct user name, password and verification code, we can successfully log in and access the web interface.
So far, we have implemented the graphical verification code function based on the user-defined authentication provider. This implementation method is more complex than the first implementation method. In fact, it can meet our development needs.
Welfare at the end of the article
Have you learned today's content?
👆👆👆
Don't forget to scan the code to get the information [HD Java learning roadmap]
And [full set of learning videos and supporting materials]