众所周知SpringBoot-Security可以继承WebSecurityConfigurerAdapter来进行登录验证,若我们想自定义登录验证的规则,则可以编写一个登录过滤的类通过继承UsernamePasswordAuthenticationFilter,关于这方面的内容,我是通过看松哥的微人事项目进行的简单学习,这里贴上松哥的博客地址:http://springboot.javaboy.org/2019/1120/springboot-vue,感谢松哥的贡献。
我来简单的写一下个人对这个的理解,若有错误之处,希望大家能在评论区指出。
1 /** 2 * 该类主要是自定义登录验证的规则,包括检查验证码 3 */ 4 public class LoginFilter extends UsernamePasswordAuthenticationFilter { 5 @Autowired 6 private SessionRegistry sessionRegistry; //用于向session里注册用户 7 8 @Override 9 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { 10 //登录表单只支持post,进行验证 11 if(!request.getMethod().equals("POST")){ 12 throw new AuthenticationServiceException( 13 "Authentication method not supported: " + request.getMethod()); 14 } 15 //如果请求类型为json的话,自定义处理 16 String localCode = (String) request.getSession().getAttribute("verify_code"); 17 String verifyCode = null; 18 if(request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().contains(MediaType.APPLICATION_JSON_UTF8_VALUE)){ 19 Map<String,String> loginData = new HashMap<>();//用于存放登录用户信息的键值对 20 try { 21 //把请求里信息读进map 22 loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class); 23 } catch (IOException e) { 24 e.printStackTrace(); 25 }finally { 26 verifyCode = loginData.get("code"); 27 checkCode(localCode,verifyCode); 28 //以后要在这里进行检查验证码 29 } 30 //通过父类提供的方法获取用户名密码 31 String username = loginData.get(getUsernameParameter()); 32 String password = loginData.get(getPasswordParameter()); 33 //这一步照抄父类的处理,可能是处理为空时候的用户密码? 34 if(username==null){ 35 username=""; 36 } 37 if(password==null){ 38 password=""; 39 } 40 username=username.trim(); //去除用户名前后的空格 41 //开始验证 42 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); 43 setDetails(request,authRequest); 44 //向session里注册此用户 45 Hr principal = new Hr(); 46 principal.setName(username); 47 sessionRegistry.registerNewSession(request.getSession(true).getId(),principal); 48 return this.getAuthenticationManager().authenticate(authRequest); //获取身份验证管理器,提交令牌验证 49 } else { 50 //检查一下验证码,然后直接调用父类的 51 checkCode(localCode,verifyCode); 52 return super.attemptAuthentication(request,response); 53 } 54 } 55 56 private void checkCode(String localCode,String verifyCode){ 57 if(verifyCode==null || verifyCode.equals("") || localCode==null || localCode.equals("") || !localCode.toLowerCase().equals(verifyCode.toLowerCase())){ 58 throw new AuthenticationServiceException("验证码不正确!"); 59 } 60 } 61 }
在这篇代码中,主要是希望登录数据以JSON的方式传送过来,所以在开头进行了request类型是否为JSON的验证,又通过JACKSON提供的ObjectMapper().readValue将request请求的数据读成Map形式存储。中间的处理过程完全参照父类UsernamePasswordAuthenticationFilter里的处理,最后向session注册一个用户。
综上举一反三,若想进行其他的一些数据处理或者判断,都可以以继承这个类的方式自定义,比如里面的验证码的验证。父类还提供了一些其他非常有用的方法,比如getUsernameParameter,可以直接获取到传过来的username,当然你也可以自定义。
最后在SecurityConfig里new 一个LoginFilter的实例,在重写的configure(HttpSecurity http)方法里,以AOP形式织入。
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);