前言
验证码相关的依赖和代码,在上一篇已经写到了,这里不在赘述。
package com.demo.springsecuritydemo.detail;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 继承自webAuthenticationDetail
*
* @author suvue
* @date 2020/5/3
*/
public class MyWebAuthenticationDetail extends WebAuthenticationDetails {
public String imageCode;
public String savedImageCode;
public String getImageCode() {
return imageCode;
}
public void setImageCode(String imageCode) {
this.imageCode = imageCode;
}
public String getSavedImageCode() {
return savedImageCode;
}
public void setSavedImageCode(String savedImageCode) {
this.savedImageCode = savedImageCode;
}
/**
* Records the remote address and will also set the session Id if a session already
* exists (it won't create one).
*
* @param request that the authentication request was received from
*/
public MyWebAuthenticationDetail(HttpServletRequest request) {
super(request);
//补充用户提交的验证码和session中保存的验证码
this.imageCode = request.getParameter("captcha");
final HttpSession session = request.getSession();
this.savedImageCode= (String) session.getAttribute("captcha");
if (!StringUtils.isEmpty(this.savedImageCode)){
//清除验证码,不管失败成功,客户端应在登录失败时刷新验证码
session.removeAttribute("captcha");
}
}
}
package com.demo.springsecuritydemo.detailSource;
import com.demo.springsecuritydemo.detail.MyWebAuthenticationDetail;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,
WebAuthenticationDetails> {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest request) {
return new MyWebAuthenticationDetail(request);
}
}
package com.demo.springsecuritydemo.provider;
import com.demo.springsecuritydemo.detail.MyWebAuthenticationDetail;
import com.demo.springsecuritydemo.exception.VerificationCodeException;
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Objects;
/**
* 实现图片验证码的AuthenticationProvider
*
* @author suvue
* @date 2020/5/3
*/
@Component
public class MyAuthenticationProvider extends DaoAuthenticationProvider {
public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder){
this.setUserDetailsService(userDetailsService);
this.setPasswordEncoder(passwordEncoder);
}
/**
* 附加认证过程
*/
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
//实现图片验证码的校验逻辑
final MyWebAuthenticationDetail details = (MyWebAuthenticationDetail) authentication.getDetails();
final String imageCode = details.getImageCode();
final String savedImageCode = details.getSavedImageCode();
//校验不通过,抛出异常
if (StringUtils.isEmpty(imageCode) || StringUtils.isEmpty(savedImageCode) || !Objects.equals(imageCode,
savedImageCode)) {
throw new VerificationCodeException();
}
//调用父类完成密码验证
super.additionalAuthenticationChecks(userDetails, authentication);
}
}
这里贴出完整代码,关键点都有相关注释的
package com.demo.springsecuritydemo.config;
import com.demo.springsecuritydemo.filters.VerificationCodeFilter;
import com.demo.springsecuritydemo.handler.MyAuthenticationFailureHandler;
import com.demo.springsecuritydemo.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
/**
* 自定义验证完善图片验证码
*
* @author suvue
* @date 2020/5/3
*/
@EnableWebSecurity
public class WebSecurityConfig1 extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> myWebAuthenticationDetailsSource;
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
PasswordEncoder passwordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/api/**").hasRole("ADMIN")
.antMatchers("/user/api/**").hasRole("USER")
//开放captcha.jpg的访问权限
.antMatchers("/app/api/**","/captcha.jpg").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
//把我们写的detailsSource传给控制校验的filter
.authenticationDetailsSource(myWebAuthenticationDetailsSource)
.loginPage("/myLogin.html")
.loginProcessingUrl("/auth/form")
.successHandler((httpServletRequest, httpServletResponse, authentication) -> {
httpServletResponse.setContentType("application/json;charset=UTF-8");
final PrintWriter out = httpServletResponse.getWriter();
out.write("{\"error_code\":\"0\",\"message\":\"欢迎登录系统!\"}");
})
.failureHandler(new MyAuthenticationFailureHandler())
.permitAll()
.and()
.csrf().disable();
//将过滤器添加再UsernamePasswordAuthenticationFilter之前
http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//使用我们自定义的数据库模型
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder);
//添加我们的校验流程
auth.authenticationProvider(authenticationProvider);
}
}