回到序章 来来来,点这
总结的说 就是通过自定义 UsernamePasswordAuthenticationToken 的 details 实现检验。
正文 在没事干的情况下,登陆成功后通过下面的命令打印了一下当前登陆用户的 authentication。
1 System.out.println(SecurityContextHolder.getContext().getAuthentication().toString());
得到的结果是:
1 UsernamePasswordAuthenticationToken [Principal=SysUser(id=1 , username=user1, password=123 , nickName=测试账号1 , locked=true , status=1 , roles=[SysRole(id=1 , code=test1, name=test1角色)]), Credentials=[PROTECTED], Authenticated=true , Details=WebAuthenticationDetails [RemoteIpAddress=0 :0 :0 :0 :0 :0 :0 :1 , SessionId=9914AF6FFBCE3DB0AAC5E761E8574AB0], Granted Authorities=[ROLE_test1]]
好奇为啥 Details 的类型会是 WebAuthenticationDetails,于是开始研究。
首先进到 UsernamePasswordAuthenticationToken,没找到 details 参数;
继续找父类 AbstractAuthenticationToken,哦,找到了,是个 Object 类型,找一下 setter 方法,没啥特殊的:
1 2 3 public void setDetails (Object details) { this .details = details; }
看一下哪里用到了这个方法,于是看到了 UsernamePasswordAuthenticationFilter,这个过滤器在检验账号密码的时候,会创建一个 Authentication 对象( UsernamePasswordAuthenticationToken )返回。
查看该过滤器调用到的方法如下:
1 2 3 protected void setDetails (HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { authRequest.setDetails(this .authenticationDetailsSource.buildDetails(request)); }
查看 buildDetails(),最终定位到 WebAuthenticationDetailsSource 的 buildDetails() 方法。该方法就创建了 WebAuthenticationDetails 的实例。
1 2 3 4 @Override public WebAuthenticationDetails buildDetails (HttpServletRequest context) { return new WebAuthenticationDetails (context); }
进到对应的类中,发现是存储 sessionId 和 ip地址的。且入参是 HttpServletRequest。
于是突发奇想,如果增加一个子类继承它,然后增加一个参数,接着拿这个 HttpServletRequest 获得验证码,跟入参的验证码做比对,成功的话将参数设置为另一个值,Provider 里获得 details 然后直接判断这个值。
实现过程如下:
首先弄一个子类继承 WebAuthenticationDetails:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class MyWebAuthenticationDetails extends WebAuthenticationDetails { private boolean isVerify = false ; public MyWebAuthenticationDetails (HttpServletRequest request) { super (request); String code = request.getParameter("code" ); String sessionCode = request.getSession().getAttribute("code" ) + "" ; if (code != null && !"" .equals(code) && sessionCode != null && !"" .equals(sessionCode) && code.equals(sessionCode)) { isVerify = true ; } } public boolean isVerify () { return isVerify; } public void setVerify (boolean verify) { isVerify = verify; } }
然后重写 Provider 试试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import org.springframework.security.authentication.AuthenticationServiceException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.authentication.dao.DaoAuthenticationProvider;import org.springframework.security.core.AuthenticationException;import org.springframework.security.core.userdetails.UserDetails;public class MyAuthenticationProvider extends DaoAuthenticationProvider { @Override protected void additionalAuthenticationChecks (UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails(); if (!details.isVerify()) { throw new AuthenticationServiceException ("验证码错误" ); } super .additionalAuthenticationChecks(userDetails, authentication); } }
测试,Emmm,报错,提示 WebAuthenticationDetails 不能强转成 MyWebAuthenticationDetails,继续看一下源码。
哦,WebAuthenticationDetailsSource,重写它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import org.springframework.security.web.authentication.WebAuthenticationDetails;import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Component public class MyWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource { @Override public WebAuthenticationDetails buildDetails (HttpServletRequest context) { return new MyWebAuthenticationDetails (context); } }
再试,Emmm,还是不能强转,上网查,哦,在 http.formLogin() 加上 .authenticationDetailsSource(authenticationDetailsSource)
1 2 3 4 5 6 7 8 9 10 11 @Autowired private MyWebAuthenticationDetailsSource authenticationDetailsSource;@Override protected void configure (HttpSecurity http) throws Exception { http ... .formLogin() .authenticationDetailsSource(authenticationDetailsSource); ... }
完结撒花。