在使用 SpringSecurity 中,大伙都知道默认的登录数据是通过 key/value 的形式来传递的,默认情况下不支持 JSON格式的登录数据,如果有这种需求,就需要自己来解决,本文主要和小伙伴来聊聊这个话题。
通过之前的分析,我们已经知道了登录参数的提取在 UsernamePasswordAuthenticationFilter 过滤器中提取的,因此我们只需要模仿UsernamePasswordAuthenticationFilter过滤器重写一个过滤器,放在UsernamePasswordAuthenticationFilter的位置即可
public class LoginFilter 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);
}
}
}
(1)首先确保进入过滤器的是POST请求
(2)根据请求的content-type判断参数是不是JSON格式,如果不是则调用父类的attemptAuthentication方法
(3)如果请求时JSON格式,则先利用jackson提供的ObjectMapper工具,将输入流转化为Map对象,然后从Map对象中提取出用户名 密码,构造出UsernamePasswordAuthenticationToken对象,进行认证
(4)其实和原UsernamePasswordAuthenticationFilter比起来,只有获取参数的方法不同,其他的都类似
接着,我们需要把这个过滤器放在过滤器链中
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.and().csrf().disable();
//addFilterAt表示放在指定位置上
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
//注册一个AuthenticationManager实例
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
//注册一个过滤器实例
@Bean
LoginFilter loginFilter() throws Exception {
LoginFilter filter = new LoginFilterFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHander((req,resp.auth) ->{
resp.setContentType("application/json;charset=utf-8");
PrintWriter out=resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(auth));
});
return filter
}
}
(1)重写父类的authenticationManagerBean方法,提供一个AuthenticationManager配给过滤器
(2)配置过滤器实例,为过滤器配置成功、失败处理器
(3)在HttpSecurity中使用addFilterAt方法将过滤器添加到 UsernamePasswordAuthenticationFilter的位置上
需要注意的是,我们获取AuthenticationManager实例时,有两种方法 一种是重写父类的authenticationManage()方法,一种是重写父类的authenticationManageBean()方法
区别就在于重写父类的authenticationManage()方法获得的是全局AuthenticationManager实例,重写父类的authenticationManageBean()方法获得的是局部AuthenticationManager实例
在实际应用时,我们一般都是重写父类的authenticationManageBean()方法获得局部AuthenticationManager实例
原文:https://www.cnblogs.com/wangstudyblog/p/14793681.html