【Spring Security】重写 UsernamePasswordAuthenticationFilter 支持登录校验 JSON 数据

问题描述

  • 在 Spring Boot 项目中,UsernamePasswordAuthenticationFilter 通常用于处理登录验证,但默认情况下,它只支持key/value表单形式的参数校验,无法直接处理JSON格式的请求参数

  • 在 Spring Security 5.7 及更高版本中,WebSecurityConfigurerAdapter 已被弃用。取而代之的是配置类和 SecurityFilterChain Bean 的方式。

  • 由于我的项目是 Springboot3 版本,网上找一圈没重写生效,经过参考多方资料和不断重试,终于找到该方式成功实现重写。

解决方案

  1. 创建自定义的Filter
    首先,你需要创建一个自定义的Filter,继承自UsernamePasswordAuthenticationFilter,并覆盖其中的部分方法以支持JSON格式的请求参数校验。

  2. 重写attemptAuthentication方法
    在自定义的Filter中,重写attemptAuthentication方法,该方法用于从请求中获取JSON格式的数据,并进行相应的校验。

  3. 处理JSON数据
    attemptAuthentication方法中,你需要解析JSON格式的请求数据,提取出用户名和密码等必要信息。

  4. 校验逻辑
    在获取到用户输入的信息后,你可以编写相应的校验逻辑,例如验证用户名和密码的有效性。

  5. 配置Filter
    最后,将自定义的Filter配置到Spring Security中,以确保它会在登录验证时被正确调用。

示例代码

  1. 创建 CustomUsernamePasswordAuthenticationFilter Filter:
import com.fasterxml.jackson.databind.ObjectMapper;  
import jakarta.servlet.FilterChain;  
import jakarta.servlet.ServletException;  
import jakarta.servlet.http.HttpServletRequest;  
import jakarta.servlet.http.HttpServletResponse;  
import org.springframework.security.authentication.AuthenticationManager;  
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 java.io.IOException;  
import java.util.Map;  

/**
 *  按需拓展:
 *  1. code 验证码参数校验
 *  2. 同时支持 key/value 表单和 Json 格式
 *  3. ...
 */
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {  
  
    private final ObjectMapper objectMapper;  
  
    public CustomUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager, ObjectMapper objectMapper) {  
        super.setAuthenticationManager(authenticationManager);  
        this.objectMapper = objectMapper;  
    }  
  
    @Override  
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {  
        if ("application/json".equals(request.getContentType())) {  
            try {  
                Map<String, String> loginData = objectMapper.readValue(request.getInputStream(), Map.class);  
                String username = loginData.get("username");  
                String password = loginData.get("password");  
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);  
                setDetails(request, authRequest);  
                return this.getAuthenticationManager().authenticate(authRequest);  
            } catch (IOException e) {  
                throw new RuntimeException(e);  
            }  
        }  
        return super.attemptAuthentication(request, response);  
    }  
}
  1. 配置 SecurityConfiguration 到自定义过滤器链
import com.fasterxml.jackson.databind.ObjectMapper;  
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.configuration.AuthenticationConfiguration;  
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.WebSecurityCustomizer;  
import org.springframework.security.web.SecurityFilterChain;  
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;  
  
@Configuration  
@EnableWebSecurity  
public class SecurityConfiguration {  
  
    private final ObjectMapper objectMapper;  
  
    public SecurityConfiguration(ObjectMapper objectMapper) {  
        this.objectMapper = objectMapper;  
    }  
  
    @Bean  
    public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {  
        CustomUsernamePasswordAuthenticationFilter customFilter = new CustomUsernamePasswordAuthenticationFilter(authenticationManager, objectMapper);  
        // 这里记得设置路径,不然不生效!!!
        customFilter.setFilterProcessesUrl("/api/auth/login");
  
        // 多个 Filter 配置好顺序
        http.csrf().disable()  
            .authorizeHttpRequests()  
                .anyRequest().authenticated()  
                .and()  
            .addFilterAt(customFilter, UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(requestLogFilter, CustomUsernamePasswordAuthenticationFilter.class)  
            .addFilterBefore(jwtAuthenticationFilter, RequestLogFilter.class);  
  
        return http.build();  
    }  
  
    // SpringSecurity 6.X 新版写法
    @Bean  
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {  
        return authenticationConfiguration.getAuthenticationManager();  
    }  
}

功能测试

postman 接口测试

总结说明

以上仅是问题记录,如果有错误描述,还望大佬指正,共同进步!

  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值