Background
- Implement custom login by overriding
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication
- I found that no matter how I log in, I still jump to the login page when accessing protected resources.
Solution
/** *Add this sentence */ myFilter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());
Reference
A little rain in Jiangnan
Complete code
package com.example.springsecuritytest; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.context.DelegatingSecurityContextRepository; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.util.AntPathMatcher; import java.io.IOException; import java.util.Properties; @Configuration public class SecurityConfig {<!-- --> @Bean SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {<!-- --> /** * data source */ InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); UserDetails abc = User.withUsername("abc").password("{noop}123").build(); inMemoryUserDetailsManager.createUser(abc); AuthenticationManagerBuilder authenticationManagerBuilder = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder.userDetailsService(inMemoryUserDetailsManager); AuthenticationManager authenticationManager = authenticationManagerBuilder.build(); MyFilter myFilter = new MyFilter(); myFilter.setAuthenticationManager(authenticationManager); myFilter.setPostOnly(true); myFilter.setRequiresAuthenticationRequestMatcher( new AntPathRequestMatcher("/dologin", "POST") ); myFilter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());/**Important*/ httpSecurity.authorizeHttpRequests(f -> f .requestMatchers("/login").permitAll() .requestMatchers("/vc").permitAll() .requestMatchers("/dologin").permitAll() .anyRequest().authenticated() ); httpSecurity.formLogin(f -> f .loginPage("/login") .loginProcessingUrl("/dologin") ); httpSecurity.addFilterAt(myFilter, UsernamePasswordAuthenticationFilter.class); httpSecurity.authenticationManager(authenticationManager); httpSecurity.csrf(AbstractHttpConfigurer::disable); return httpSecurity.build(); } @Bean public DefaultKaptcha getDefaultKaptcha(){<!-- --> DefaultKaptcha captchaProducer = new DefaultKaptcha(); Properties properties = new Properties(); properties.setProperty("kaptcha.border", "yes");//Picture border properties.setProperty("kaptcha.border.color", "20,15,90");// Border color properties.setProperty("kaptcha.textproducer.font.color", "blue"); // Font color properties.setProperty("kaptcha.image.width", "110");// Image width properties.setProperty("kaptcha.image.height", "40");// Image height properties.setProperty("kaptcha.textproducer.font.size", "30");// Font size properties.setProperty("kaptcha.session.key", "code");// session key properties.setProperty("kaptcha.textproducer.char.length", "5");//Verification code length properties.setProperty("kaptcha.textproducer.font.names", "Song style, Kai style, Microsoft Yahei");// Font Config config = new Config(properties); //com.google.code.kaptcha.util.Config captchaProducer.setConfig(config); return captchaProducer; } }
package com.example.springsecuritytest; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.IOException; import com.google.code.kaptcha.impl.DefaultKaptcha; @Controller public class TestController {<!-- --> @Autowired DefaultKaptcha defaultKaptcha; @RequestMapping("/test") public String s(){<!-- --> return "Hello"; } @RequestMapping("/index") public String ss(){<!-- --> return "index"; } @RequestMapping("/login") public String ass(){<!-- --> return "login"; } @RequestMapping("/vc") public void fasf(HttpServletRequest request, HttpServletResponse response) throws IOException {<!-- --> response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); response.addHeader("Cache-Control", "post-check=0, pre-check=0"); response.setHeader("Pragma", "no-cache"); response.setContentType("image/jpeg"); //Corresponding format String text = defaultKaptcha.createText(); BufferedImage image = defaultKaptcha.createImage(text); HttpSession session = request.getSession(); session.setAttribute("vc",text); System.out.println(text); ServletOutputStream outputStream = response.getOutputStream(); ImageIO.write(image,"jpeg",outputStream); //Tool class writes } }
package com.example.springsecuritytest; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.thymeleaf.util.StringUtils; public class MyFilter extends UsernamePasswordAuthenticationFilter {<!-- --> @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {<!-- --> // UsernamePasswordAuthenticationToken abc = new UsernamePasswordAuthenticationToken("abc", "123"); // setDetails(request,abc); if (!request.getMethod().equals("POST")) {<!-- --> throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } HttpSession session = request.getSession(); String vc = (String) session.getAttribute("vc"); String vc1 = request.getParameter("vc"); if(!StringUtils.equals(vc,vc1)){<!-- --> System.out.println("Verification code does not match"); throw new BadCredentialsException("Verification code does not match"); } System.out.println("Verification code matches"); return super.attemptAuthentication(request, response); // return this.getAuthenticationManager().authenticate(abc); // return super.attemptAuthentication(request, response); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form class="form-signin" method="post" action="/dologin"> <h2 class="form-signin-heading">Please sign in</h2> <p> <label for="username" class="sr-only">Username</label> <input type="text" id="username" name="username" class="form-control" placeholder="Username" required="" autofocus="\ "> </p> <p> <label for="password" class="sr-only">Password</label> <input type="password" id="password" name="password" class="form-control" placeholder="Password" required=""> </p> <p> <label for="vc" class="sr-only">Password</label> <input type="text" id="vc" name="vc" class="form-control" placeholder="vc" required=""> </p> <img src="/vc"> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> </form> </body> </html>
<dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency> \t\t