How Spring security Rewrites Filter for json Login
- 2021-11-13 07:54:58
- OfStack
Spring security Rewrite Filter Implement json Login
In using SpringSecurity, everyone knows that the default login data is transmitted in the form of key/value, and the login data in JSON format is not supported by default. If there is such a requirement, you need to solve it yourself. This paper mainly solves this problem:
JSON login
The above demonstration is an original login scheme. If you want to pass the username and password through JSON, you need to customize the relevant filters. By analyzing the source code, we found that the default username and password are extracted from the UsernamePasswordAuthenticationFilter filter, and some source codes are as follows:
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
//...
//...
}
It can be seen from here that the default username/password extraction is extracted through getParameter in request. If you want to use JSON to pass the username and password, you only need to replace this filter. The custom filter is as follows:
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
ObjectMapper mapper = new ObjectMapper();
UsernamePasswordAuthenticationToken authRequest = null;
try (InputStream is = request.getInputStream()) {
Map<String, String> authenticationBean = mapper.readValue(is, Map.class);
authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.get("username"), authenticationBean.get("password"));
} catch (IOException e) {
e.printStackTrace();
authRequest = new UsernamePasswordAuthenticationToken("", "");
} finally {
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
} else {
return super.attemptAuthentication(request, response);
}
}
}
Here, only the user name/password acquisition scheme is revised to obtain the user name and password from JSON, and then the following modifications are made in SecurityConfig:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().csrf().disable();
http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.ok(" Login Successful !");
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
});
filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
resp.setContentype("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.error(" Login failed !");
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
});
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
Done ~
Spring security5 Login with json
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
@SneakyThrows(IOException.class) //lombok try catch
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
ObjectMapper mapper = new ObjectMapper();
Map<String,String> map = mapper.readValue(request.getInputStream(), Map.class);
String username = map.get(super.getUsernameParameter());
String password = map.get(super.getPasswordParameter());
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
return super.attemptAuthentication(request, response);
}
}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
}
CustomUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
CustomUsernamePasswordAuthenticationFilter filter = new CustomUsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(super.authenticationManagerBean());
filter.setFilterProcessesUrl(customSecurityProperties.getLoginUrl());
// Processed login successfully
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler());
// Failed to process login
filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler());
return filter;
}
}