⒈封装验证码类
1 package cn.coreqi.security.validate; 2 3 import java.awt.image.BufferedImage; 4 import java.time.LocalDateTime; 5 6 public class ImageCode { 7 private BufferedImage image; 8 private String code; 9 private LocalDateTime expireTime; //过期时间10 11 public ImageCode(BufferedImage image, String code, Integer expireIn) {12 this.image = image;13 this.code = code;14 this.expireTime = LocalDateTime.now().plusSeconds(expireIn);15 }16 17 public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {18 this.image = image;19 this.code = code;20 this.expireTime = expireTime;21 }22 23 public boolean isExpried(){24 return LocalDateTime.now().isAfter(expireTime);25 }26 27 public BufferedImage getImage() {28 return image;29 }30 31 public void setImage(BufferedImage image) {32 this.image = image;33 }34 35 public String getCode() {36 return code;37 }38 39 public void setCode(String code) {40 this.code = code;41 }42 43 public LocalDateTime getExpireTime() {44 return expireTime;45 }46 47 public void setExpireTime(LocalDateTime expireTime) {48 this.expireTime = expireTime;49 }50 }
⒉封装验证码控制器
1 package cn.coreqi.security.controller; 2 3 import cn.coreqi.security.validate.ImageCode; 4 import com.sun.image.codec.jpeg.JPEGCodec; 5 import com.sun.image.codec.jpeg.JPEGImageEncoder; 6 import org.springframework.social.connect.web.HttpSessionSessionStrategy; 7 import org.springframework.social.connect.web.SessionStrategy; 8 import org.springframework.web.bind.annotation.GetMapping; 9 import org.springframework.web.bind.annotation.RestController;10 import org.springframework.web.context.request.ServletWebRequest;11 12 import javax.imageio.ImageIO;13 import javax.servlet.http.HttpServletRequest;14 import javax.servlet.http.HttpServletResponse;15 import java.awt.*;16 import java.awt.image.BufferedImage;17 import java.io.IOException;18 import java.util.Random;19 20 @RestController21 public class ValidateController {22 23 public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";24 private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();25 26 @GetMapping("code/image")27 public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {28 ImageCode imageCode = createImageCode(request);29 sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);30 31 response.setHeader("Pragma","No-cache");32 response.setHeader("Cache-Control","no-cache");33 //response.setDateHeader("Expires", 0);34 35 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(response.getOutputStream());36 encoder.encode(imageCode.getImage());37 38 //ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream()); //当tomcat下temp文件夹不存在则"Can't create output stream"39 }40 41 private ImageCode createImageCode(HttpServletRequest request) {42 int width = 67;43 int height = 23;44 BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);45 46 Graphics g = image.getGraphics();47 48 Random random = new Random();49 50 g.setColor(getRandColor(200,250));51 g.fillRect(0,0,width,height);52 g.setFont(new Font("Times New Roman",Font.ITALIC,20));53 g.setColor(getRandColor(160,200));54 for (int i = 0;i < 155; i++){55 int x = random.nextInt(width);56 int y = random.nextInt(height);57 int xl = random.nextInt(12);58 int yl = random.nextInt(12);59 g.drawLine(x,y,x+xl,y+yl);60 }61 String sRand = "";62 for(int i = 0;i < 4; i++){63 String rand = String.valueOf(random.nextInt(10));64 sRand += rand;65 g.setColor(new Color(20 + random.nextInt(110),20 + random.nextInt(110),20 + random.nextInt(110)));66 g.drawString(rand,13 * i + 6,16);67 }68 g.dispose();69 return new ImageCode(image,sRand,60);70 }71 72 /**73 * 生成随机背景条纹74 * @param fc75 * @param bc76 * @return77 */78 private Color getRandColor(int fc, int bc) {79 Random random = new Random();80 if(fc > 255){81 fc = 255;82 }83 if(bc > 255){84 bc = 255;85 }86 int r = fc + random.nextInt(bc - fc);87 int g = fc + random.nextInt(bc - fc);88 int b = fc + random.nextInt(bc - fc);89 return new Color(r,g,b);90 }91 }
⒊放行验证码的Rest地址
⒋表单添加验证码
1 2 图形验证码:3 4 5 6 7
⒌声明一个验证码异常,用于抛出特定的验证码异常
1 package cn.coreqi.security.validate;2 3 import org.springframework.security.core.AuthenticationException;4 5 public class ValidateCodeException extends AuthenticationException {6 public ValidateCodeException(String msg) {7 super(msg);8 }9 }
⒍创建一个过滤器,用于验证请求中的验证码是否正确
1 package cn.coreqi.security.Filter; 2 3 import cn.coreqi.security.validate.ImageCode; 4 import cn.coreqi.security.validate.ValidateCodeException; 5 import org.springframework.security.web.authentication.AuthenticationFailureHandler; 6 import org.springframework.social.connect.web.HttpSessionSessionStrategy; 7 import org.springframework.social.connect.web.SessionStrategy; 8 import org.springframework.util.StringUtils; 9 import org.springframework.web.bind.ServletRequestBindingException;10 import org.springframework.web.bind.ServletRequestUtils;11 import org.springframework.web.context.request.ServletWebRequest;12 import org.springframework.web.filter.OncePerRequestFilter;13 import cn.coreqi.security.controller.*;14 15 import javax.servlet.FilterChain;16 import javax.servlet.ServletException;17 import javax.servlet.http.HttpServletRequest;18 import javax.servlet.http.HttpServletResponse;19 import java.io.IOException;20 21 public class ValidateCodeFilter extends OncePerRequestFilter {22 23 private AuthenticationFailureHandler authenticationFailureHandler;24 25 private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();26 27 public AuthenticationFailureHandler getAuthenticationFailureHandler() {28 return authenticationFailureHandler;29 }30 31 public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {32 this.authenticationFailureHandler = authenticationFailureHandler;33 }34 35 public SessionStrategy getSessionStrategy() {36 return sessionStrategy;37 }38 39 public void setSessionStrategy(SessionStrategy sessionStrategy) {40 this.sessionStrategy = sessionStrategy;41 }42 43 @Override44 protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {45 if (httpServletRequest.equals("/authentication/form") && httpServletRequest.getMethod().equals("post")) {46 try {47 validate(new ServletWebRequest(httpServletRequest));48 49 }catch (ValidateCodeException e){50 authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);51 return;52 }53 }54 filterChain.doFilter(httpServletRequest,httpServletResponse); //如果不是登录请求,直接调用后面的过滤器链55 }56 57 private void validate(ServletWebRequest request) throws ServletRequestBindingException {58 ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateController.SESSION_KEY);59 String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");60 if(!StringUtils.hasText(codeInRequest)){61 throw new ValidateCodeException("验证码的值不能为空!");62 }63 if(codeInSession == null){64 throw new ValidateCodeException("验证码不存在!");65 }66 if(codeInSession.isExpried()){67 sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY);68 throw new ValidateCodeException("验证码已过期!");69 }70 if(!codeInSession.getCode().equals(codeInRequest)){71 throw new ValidateCodeException("验证码不正确!");72 }73 sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY);74 }75 }
⒎在SpringSecurity过滤器链中注册我们的过滤器
1 package cn.coreqi.security.config; 2 3 import cn.coreqi.security.Filter.ValidateCodeFilter; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 import org.springframework.security.crypto.password.NoOpPasswordEncoder;10 import org.springframework.security.crypto.password.PasswordEncoder;11 import org.springframework.security.web.authentication.AuthenticationFailureHandler;12 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;13 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;14 15 @Configuration16 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {17 18 @Autowired19 private AuthenticationSuccessHandler coreqiAuthenticationSuccessHandler;20 21 @Autowired22 private AuthenticationFailureHandler coreqiAuthenticationFailureHandler;23 24 @Bean25 public PasswordEncoder passwordEncoder(){26 return NoOpPasswordEncoder.getInstance();27 }28 29 @Override30 protected void configure(HttpSecurity http) throws Exception {31 ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();32 validateCodeFilter.setAuthenticationFailureHandler(coreqiAuthenticationFailureHandler);33 34 //http.httpBasic() //httpBasic登录 BasicAuthenticationFilter35 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) //加载用户名密码过滤器的前面36 .formLogin() //表单登录 UsernamePasswordAuthenticationFilter37 .loginPage("/coreqi-signIn.html") //指定登录页面38 //.loginPage("/authentication/require")39 .loginProcessingUrl("/authentication/form") //指定表单提交的地址用于替换UsernamePasswordAuthenticationFilter默认的提交地址40 .successHandler(coreqiAuthenticationSuccessHandler) //登录成功以后要用我们自定义的登录成功处理器,不用Spring默认的。41 .failureHandler(coreqiAuthenticationFailureHandler) //自己体会把42 .and()43 .authorizeRequests() //对授权请求进行配置44 .antMatchers("/coreqi-signIn.html","/code/image").permitAll() //指定登录页面不需要身份认证45 .anyRequest().authenticated() //任何请求都需要身份认证46 .and().csrf().disable(); //禁用CSRF47 //FilterSecurityInterceptor 整个SpringSecurity过滤器链的最后一环48 }49 }