对于这个需求,网上的解决方案比较多,如使用filter、自定义Provider……
我采用方法是采用增加AuthenticationProvider的方式。具体实现如下:
1、增加WebAuthenticationDetails实现类,保存验证码信息
public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
private String imageCode;
private String session_imageCode;
private long session_imageTime;
public CustomWebAuthenticationDetails(HttpServletRequest request) {
super(request);
this.imageCode = request.getParameter("imageCode");
this.session_imageCode = (String)request.getSession().getAttribute("session_imageCode");
String session_verifyTime = (String)request.getSession().getAttribute("session_imageTime");
if(session_verifyTime == null) {
this.session_imageTime= 0L;
} else {
this.session_imageTime= Long.parseLong(session_verifyTime);
}
}
public String getImageCode(){
return imageCode;
}
public String getSession_imageCode() {
return session_imageCode;
}
public long getSession_imageTime() {
return session_imageTime;
}
}
2、增加AuthenticationDetailsSource实现类
@Component
public class CustomAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new CustomWebAuthenticationDetails(context);
}
}
3、自定义AuthenticationProvider实现类,并添加到验证列表中去
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
CustomWebAuthenticationDetails details = (CustomWebAuthenticationDetails) authentication.getDetails();
String imageCode = details.getImageCode();
String session_imageCode = details.getSession_imageCode();
long session_imageTime = details.getSession_imageTime();
if(imageCode == null || session_imageCode == null) {
throw new ImageCodeIllegalException("验证码错误");
}
if(!imageCode.equals(session_imageCode)) {
throw new ImageCodeIllegalException("验证码错误");
}else{
long nowTime = System.currentTimeMillis();
if((nowTime - session_imageTime)/1000 > 60) { //大于60s,超时
throw new ImageCodeIllegalException("验证码已超时");
}
}
return null; //如果后续要有验证密码的provider,这里需要直接返回null
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
4、在WebSecurityConfigurerAdapter的实现类中增加配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Inject
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource;
@Inject
private AuthenticationProvider customAuthenticationProvider;
@Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);//重点
}
@Override
protected void configure(HttpSecurity http) throws Exception {
……
.and()
.formLogin()
.loginProcessingUrl("/api/login")
.usernameParameter("username")
.passwordParameter("password")
.authenticationDetailsSource(authenticationDetailsSource) //重点
.permitAll()
……
}
}
为达到“用后即焚”的效果,在登录成功、失败后续处理的类中增加了清除验证码的操作
@Component
public class AjaxAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Autowired
private PasswordService passwordService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
//移除验证码
request.getSession().removeAttribute("session_verifyObj");
request.getSession().removeAttribute("session_verifyObjTime");
response.setStatus(HttpServletResponse.SC_OK);
}
}
@Component
public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Autowired
private PasswordService passwordService;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
//移除验证码
request.getSession().removeAttribute("session_imgeCode");
request.getSession().removeAttribute("session_imageTime");
……
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
}
}
然后在WebSecurityConfigurerAdapter的实现类中增加相关配置
……
.and()
.formLogin()
.loginProcessingUrl("/api/login")
.successHandler(ajaxAuthenticationSuccessHandler) //重点
.failureHandler(ajaxAuthenticationFailureHandler) //重点
.usernameParameter("username")
.passwordParameter("password")
.authenticationDetailsSource(authenticationDetailsSource)
.permitAll()
……
下一篇将探讨一下authenticationProvider的用法,以及当用户找不到时,不隐藏UserNotFoundException的解决办法