一、Spring Security登录接口兼容JSON格式登录
1.1、概述
前后端分离中,前端和后端的数据交互通常是JSON格式,而Spring Security的登录接口默认支持的是form-data或者x-www-form-urlencoded的,如下所示:
那么如何让Spring Security的登录接口也支持JSON格式登录呢?请看下文分析
1.2、自定义过滤器
/**
* @Author : 一叶浮萍归大海
* @Date: 2024/1/13 9:30
* @Description:
*/
public class MyUsernamePasswordAuthenticationFilter7007 extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!HttpMethod.POST.name().equals(request.getMethod())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String sessionCode = (String) request.getSession().getAttribute("code");
if (MediaType.APPLICATION_JSON_VALUE.equals(request.getContentType()) || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(request.getContentType())) {
Map<String, String> loginData = new HashMap<>(16);
try {
loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
} catch (Exception e) {
e.printStackTrace();
} finally {
String paramCode = loginData.get("code");
try {
checkCode(response,paramCode,sessionCode);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
String username = loginData.get(getUsernameParameter());
String password = loginData.get(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);
} else {
try {
checkCode(response,request.getParameter("code"),sessionCode);
} catch (Exception e) {
throw new RuntimeException(e);
}
return super.attemptAuthentication(request, response);
}
}
/**
* 检查验证码
* @param response
* @param paramCode
* @param sessionCode
*/
private void checkCode(HttpServletResponse response, String paramCode, String sessionCode) throws Exception {
if (StringUtils.isBlank(paramCode)) {
R r = R.error(ResponseEnum.VERIFY_CODE_IS_NULL.getCode(), ResponseEnum.VERIFY_CODE_IS_NULL.getMessage());
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(r));
out.flush();
out.close();
return;
}
if (StringUtils.isBlank(sessionCode)) {
R r = R.error(ResponseEnum.VERIFY_CODE_IS_EXPIRED.getCode(), ResponseEnum.VERIFY_CODE_IS_EXPIRED.getMessage());
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(r));
out.flush();
out.close();
return;
}
if (!StringUtils.equals(paramCode.toLowerCase(), sessionCode.toLowerCase())) {
R r = R.error(ResponseEnum.VERIFY_CODE_IS_NOT_MATCH.getCode(), ResponseEnum.VERIFY_CODE_IS_NOT_MATCH.getMessage());
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(r));
out.flush();
out.close();
return;
}
}
}
1.3、配置类
/**
* @Author : 一叶浮萍归大海
* @Date: 2024/1/11 21:50
* @Description: Spring Security配置类
*/
@Configuration
public class MyWebSecurityConfigurerAdapter7007 extends WebSecurityConfigurerAdapter {
@Resource
private MyAuthenticationSuccessHandler7007 successHandler;
@Resource
private MyAuthenticationFailureHandler7007 failureHandler;
@Resource
private MyLogoutSuccessHandler7007 logoutSuccessHandler;
@Resource
private MyAuthenticationEntryPoint7007 authenticationEntryPoint;
@Resource
private MyAccessDeniedHandler7007 accessDeniedHandler;
@Resource
private UserDetailsService userDetailsServiceImpl;
/**
* 密码加密器
*
* @return
*/
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
/**
* 定义基于MyBatis-Plus的用户
*
* @return
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImpl);
}
/**
* 角色继承
*
* @return
*/
@Bean
protected RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_admin > ROLE_dba");
return roleHierarchy;
}
@Bean
public MyUsernamePasswordAuthenticationFilter7007 usernamePasswordAuthenticationFilter() throws Exception {
MyUsernamePasswordAuthenticationFilter7007 usernamePasswordAuthenticationFilter = new MyUsernamePasswordAuthenticationFilter7007();
usernamePasswordAuthenticationFilter.setFilterProcessesUrl("/login");
usernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
// 登录成功回调
usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(successHandler);
// 登录失败回调
usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(failureHandler);
return usernamePasswordAuthenticationFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/dba/**").hasRole("dba")
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/helloWorld", "/verifyCode/getVerifyCode")
.permitAll()
.anyRequest()
.authenticated()
.and()
/**
* 注销登录回调
*/
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler)
.permitAll()
.and()
.csrf()
.disable()
/**
* 未认证 & 权限不足回调
*/
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
http.addFilterAfter(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}