spring security默认的登录提交方式是表单提交,为formdata格式,记录如何使用json格式提交登录
1. 自定义jsonLoginFilter
security实现登录时,会调用 UsernamePasswordAuthenticationFilter,下面的部分源码,查看源码可以看出,其中attemptAuthentication方法是实现表单提交的,我们可以重写这个方法,使其实现json格式提交。
public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request);
username = username != null ? username.trim() : "";
String password = this.obtainPassword(request);
password = password != null ? password : "";
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
我们创建一个JsonLoginFilter类继承UsernamePasswordAuthenticationFilter
import cn.edu.huuc.mdcs.pojo.SysAccount;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
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;
/**
* 自定义json格式登录的过滤器(security默认登录提交为表单提交)
* @author spring
* @date 2024/5/15 10:07
*/
public class JsonLoginFilter extends UsernamePasswordAuthenticationFilter {
public JsonLoginFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@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 contentType = request.getContentType();
//说明是以json的形式传递参数
if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType)) {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = null;
String password = null;
try {
SysAccount user = new ObjectMapper().readValue(request.getInputStream(), SysAccount.class);
username = user.getUsername();
username = (username != null) ? username.trim() : "";
password = user.getPassword();
password = (password != null) ? password : "";
} catch (IOException e) {
throw new RuntimeException(e);
}
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
return super.attemptAuthentication(request, response);
}
}
实际上,我在源码上只添加了
if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType))
来判断是否为json格式传输的
这里提醒一下:
1.MediaType.APPLICATION_JSON_UTF8_VALUE已被弃用,使用 MediaType.APPLICATION_JSON_VALUE即可
2.不要在类上添加@Component注解
2. 配置SecurityConfig
首先需要添加一个Bean,来配置登录的信息
@Bean
public JsonLoginFilter jsonLoginFilter(AuthenticationManager authenticationManager) {
JsonLoginFilter jsonLoginFilter = new JsonLoginFilter(authenticationManager);
jsonLoginFilter.setFilterProcessesUrl("/api/login");
jsonLoginFilter.setUsernameParameter("accountName");
jsonLoginFilter.setPasswordParameter("accountPassword");
jsonLoginFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
jsonLoginFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
return jsonLoginFilter;
}
其中myAuthenticationSuccessHandler,myAuthenticationFailureHandler为验证成功失败走的类,需要自己定义
然后再SecurityFilterChain配置
这里我没有展示其他的配置,仅仅针对jsonLoginFilter的配置做了配置
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity,
JsonLoginFilter jsonLoginFilter)throws Exception {
return httpSecurity
.addFilterAt(jsonLoginFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
下面的代码是之前默认配置的formLogin,就可以删除了
.formLogin((formLogin) -> {
formLogin.loginProcessingUrl(Constants.LOGIN_URI)//登录处理地址,不需要写controller
.usernameParameter("accountName")
.passwordParameter("accountPassword")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler);
})