问题描述
-
在 Spring Boot 项目中,
UsernamePasswordAuthenticationFilter
通常用于处理登录验证,但默认情况下,它只支持key/value表单形式的参数校验,无法直接处理JSON格式的请求参数
。 -
在 Spring Security 5.7 及更高版本中,
WebSecurityConfigurerAdapter
已被弃用。取而代之的是配置类和SecurityFilterChain
Bean 的方式。 -
由于我的项目是 Springboot3 版本,网上找一圈没重写生效,经过参考多方资料和不断重试,终于找到该方式成功实现重写。
解决方案
-
创建自定义的Filter
首先,你需要创建一个自定义的Filter,继承自UsernamePasswordAuthenticationFilter
,并覆盖其中的部分方法以支持JSON格式的请求参数校验。 -
重写
attemptAuthentication
方法
在自定义的Filter中,重写attemptAuthentication
方法,该方法用于从请求中获取JSON格式的数据,并进行相应的校验。 -
处理JSON数据
在attemptAuthentication
方法中,你需要解析JSON格式的请求数据,提取出用户名和密码等必要信息。 -
校验逻辑
在获取到用户输入的信息后,你可以编写相应的校验逻辑,例如验证用户名和密码的有效性。 -
配置Filter
最后,将自定义的Filter配置到Spring Security中,以确保它会在登录验证时被正确调用。
示例代码
- 创建 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);
}
}
- 配置 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 接口测试
总结说明
以上仅是问题记录,如果有错误描述,还望大佬指正,共同进步!