Bootstrap

SpringSecurity 6 自定义Filter无效解决方案

背景

  1. 通过重写org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication实现自定义登陆
  2. 发现无论如何登陆, 在访问受保护资源时依然跳转到登陆页

解决

/**
*加上这一句
*/
myFilter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());

参考

江南一点雨

完整代码

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 {


        /**
         * 数据源
         */
        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());/**重要*/


        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");// 图片边框
        properties.setProperty("kaptcha.border.color", "20,15,90");// 边框颜色
        properties.setProperty("kaptcha.textproducer.font.color", "blue"); // 字体颜色
        properties.setProperty("kaptcha.image.width", "110");// 图片宽
        properties.setProperty("kaptcha.image.height", "40");// 图片高
        properties.setProperty("kaptcha.textproducer.font.size", "30");// 字体大小
        properties.setProperty("kaptcha.session.key", "code");// session key
        properties.setProperty("kaptcha.textproducer.char.length", "5");// 验证码长度
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");// 字体
        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");				//相应格式

        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);			//工具类写出
    }


}
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("验证码不匹配");
            throw new BadCredentialsException("验证码不匹配");
        }
        System.out.println("验证码匹配");
        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>
		
;