Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money
SpringSecurity adds a verification code in two ways
Custom authentication logic
Generate captcha tools
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
Copy the code
The Kaptcha configuration is added
@Configuration
public class KaptchaConfig {
@Bean
Producer kaptcha(a) {
Properties properties = new Properties();
properties.setProperty("kaptcha.image.width"."150");
properties.setProperty("kaptcha.image.height"."50");
properties.setProperty("kaptcha.textproducer.char.string"."0123456789");
properties.setProperty("kaptcha.textproducer.char.length"."4");
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
returndefaultKaptcha; }}Copy the code
Generate captcha text and place it in HttpSession
The image generated from the captcha text is written out to the front end through the IO stream.
@RestController
public class LoginController {
@Autowired
Producer producer;
@GetMapping("/vc.jpg")
public void getVerifyCode(HttpServletResponse resp, HttpSession session) throws IOException {
resp.setContentType("image/jpeg");
String text = producer.createText();
session.setAttribute("kaptcha", text);
BufferedImage image = producer.createImage(text);
try(ServletOutputStream out = resp.getOutputStream()) {
ImageIO.write(image, "jpg", out); }}@RequestMapping("/index")
public String index(a) {
return "login success";
}
@RequestMapping("/hello")
public String hello(a) {
return "hello spring security"; }}Copy the code
Form form
<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>The login</title>
<link href="/ / maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="/ / maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="/ / cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<style>
#login .container #login-row #login-column #login-box {
border: 1px solid #9C9C9C;
background-color: #EAEAEA;
}
</style>
<body>
<div id="login">
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="/doLogin" method="post">
<h3 class="text-center text-info">The login</h3>
<div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>
<div class="form-group">
<label for="username" class="text-info">User name:</label><br>
<input type="text" name="uname" id="username" class="form-control">
</div>
<div class="form-group">
<label for="password" class="text-info">Password:</label><br>
<input type="text" name="passwd" id="password" class="form-control">
</div>
<div class="form-group">
<label for="kaptcha" class="text-info">Verification code:</label><br>
<input type="text" name="kaptcha" id="kaptcha" class="form-control">
<img src="/vc.jpg" alt="">
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-info btn-md" value="Login">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
Copy the code
The captcha image address is the address of the captcha interface that we defined in Controller.
Authentication is done by AuthenticationProvider’s Authenticate method, so the verification code can be done before:
public class KaptchaAuthenticationProvider extends DaoAuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String kaptcha = req.getParameter("kaptcha");
String sessionKaptcha = (String) req.getSession().getAttribute("kaptcha");
if(kaptcha ! =null&& sessionKaptcha ! =null && kaptcha.equalsIgnoreCase(sessionKaptcha)) {
return super.authenticate(authentication);
}
throw new AuthenticationServiceException("Verification code input error"); }}Copy the code
Configure the AuthenticationManager:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
AuthenticationProvider kaptchaAuthenticationProvider(a) {
InMemoryUserDetailsManager users = new InMemoryUserDetailsManager(User.builder()
.username("xiepanapn").password("{noop}123").roles("admin").build());
KaptchaAuthenticationProvider provider = new KaptchaAuthenticationProvider();
provider.setUserDetailsService(users);
return provider;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean(a) throws Exception {
ProviderManager manager = new ProviderManager(kaptchaAuthenticationProvider());
return manager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/vc.jpg").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/mylogin.html")
.loginProcessingUrl("/doLogin")
.defaultSuccessUrl("/index.html")
.failureForwardUrl("/mylogin.html")
.usernameParameter("uname")
.passwordParameter("passwd") .permitAll() .and() .csrf().disable(); }}Copy the code
- Configure the data source provided by UserDetailsService
- Provide an AuthenticationProvider instance and configure UserDetailsService
- Rewrite authenticationManagerBean methods provide a own ProviderManager and customize the AuthenticationManager instance.
Custom filter
LoginFilter inheritance UsernamePasswordAuthenticationFilter rewrite attemptAuthentication method:
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if(! request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String kaptcha = request.getParameter("kaptcha");
String sessionKaptcha = (String) request.getSession().getAttribute("kaptcha");
if(! StringUtils.isEmpty(kaptcha) && ! StringUtils.isEmpty(sessionKaptcha) && kaptcha.equalsIgnoreCase(sessionKaptcha)) {return super.attemptAuthentication(request, response);
}
throw new AuthenticationServiceException("Verification code input error"); }}Copy the code
Configure LoginFilter in SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("javaboy")
.password("{noop}123")
.roles("admin");
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean(a)
throws Exception {
return super.authenticationManagerBean();
}
@Bean
LoginFilter loginFilter(a) throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setFilterProcessesUrl("/doLogin");
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/hello"));
loginFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/mylogin.html"));
return loginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/vc.jpg").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/mylogin.html") .permitAll() .and() .csrf().disable(); http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class); }}Copy the code
Obviously the second one is easier