图片验证码生成
Core模块
封装验证码类
public class ImageCode {
private BufferedImage image;
/**
* code是一个随机数,图片是根据随机数生成的,
* 存放到session里面,后面用户提交登录请求时候要去验证的
*/
private String code;
/**
* 过期时间
*/
private LocalDateTime expireTime;
public ImageCode(BufferedImage image,String code,int expireIn){
this.image=image;
this.code=code;
/**
* 过期时间传递的参数应该是一个秒数:根据这个秒数去计算过期时间 */ this.expireTime = LocalDateTime.now().plusSeconds(expireIn); } public BufferedImage getImage() { return image; } public void setImage(BufferedImage image) { this.image = image; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public LocalDateTime getExpireTime() { return expireTime; } public void setExpireTime(LocalDateTime expireTime) { this.expireTime = expireTime; } }
随机数生成图片
@RestController public class ValidateCodeController { private static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE"; private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @GetMapping("/code/image") public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException { /** * 1.根据随机数生成图片 * 2.将随机数存到session中 * 3.将生成图片写到接口的响应中 */ ImageCode imageCode = createImageCode(request); sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode); ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream()); } private ImageCode createImageCode(HttpServletRequest request) { int width = 67; int height = 23; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Random random = new Random(); g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); g.setFont(new Font("Times New Roman", Font.ITALIC, 20)); g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } String sRand = ""; for (int i = 0; i < 4; i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); g.drawString(rand, 13 * i + 6, 16); } g.dispose(); return new ImageCode(image, sRand, 60); } /** * 生成随机背景条纹 * * @param fc * @param bc * @return */ private Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) { fc = 255; } if (bc > 255) { bc = 255; } int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); } }
前端获取验证码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <h2>标准登录页面</h2> <h3>表单登录</h3> <form action="/authentication/form" method="post"> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td>图形验证码</td> <td> <input type="text" name="imageCode"> <img src="/code/image"> </td> </tr> <tr> <td colspan="2"><button type="submit">登录</button></td> </tr> </table> </form> </body> </html>
授权模块添加:允许验证码生成请求permitAll()
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SecurityProperties securityProperties; @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 定义web安全配置类:覆盖config方法 * 1.参数为HttpSecurity */ @Override protected void configure(HttpSecurity http) throws Exception { /** * 定义了任何请求都需要表单认证 */ http.formLogin()//表单登录---指定了身份认证方式 // .loginPage("/login.html") .loginPage("/authentication/require") .loginProcessingUrl("/authentication/form")//配置UsernamePasswordAuthenticationFilter需要拦截的请求 .successHandler(myAuthenticationSuccessHandler)//表单登录成功之后用自带的处理器 .failureHandler(myAuthenticationFailureHandler)//表单登录失败之后用自带的处理器 // http.httpBasic()//http的basic登录 .and() .authorizeRequests()//对请求进行授权 .antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage(),"/code/image").permitAll()//对匹配login.html的请求允许访问 .anyRequest()//任何请求 .authenticated() .and() .csrf().disable();//都需要认证 } }
图片验证码认证
实现登录请求时候 都是在实现Spring提供的接口;并且加密解密实现都是Spring已经提供的,但是spring并没有提供图形验证码,因为spring security他的基本原理就是一个过滤器链。在这个链上可以加入自己写的过滤器。在UsernamePasswordAuthticationFilter前加一个自定义的过滤器。 extends OncePerRequestFilter。实现:doFilterInternal方法。
校验验证码
创建校验验证码过滤器
(1)继承OncePerRequestFilter,由spring提供,它能保证过滤器只被调用一次。
(2)重写doFilterInternal方法,校验验证码逻辑
(3)定义校验验证码异常
AuthenticationException , security定义的抽象异常,是所有身份认证抛出异常的基类
- 校验验证码过滤器加入security过滤器链,并在UsernamePasswordAuthenticationFilter之前
验证码过滤器
public class ValidateCodeFilter extends OncePerRequestFilter { @Autowired private AuthenticationFailureHandler authenticationFailureHandler; @Autowired private SessionStrategy sessionStrategy; private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { logger.info("验证码过滤器:doFilterInternal: requestURI:[{}] requestMethod:[{}]",request.getRequestURI(),request.getMethod()); /** * 如果是需要认证请求,我们进行家宴 * 如果校验失败,使用我们自定义的校验失败处理类处理 * 如果不需要认证,我们放行进入下一个Filter */ if(StringUtils.equals("/authentication/form",request.getRequestURI()) && StringUtils.endsWithIgnoreCase(request.getMethod(),"post")){ try{ validate(new ServletWebRequest(request)); }catch (ValidateCodeException e){ authenticationFailureHandler.onAuthenticationFailure(request,response,e); return; } } filterChain.doFilter(request,response); } private void validate(ServletWebRequest servletWebRequest) throws ServletRequestBindingException { //1.获取存放到session中的验证码 ImageCode codeInSession = (ImageCode)sessionStrategy.getAttribute(servletWebRequest, ValidateCodeController.SESSION_KEY); //2.获取请求中的验证码 String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "imageCode"); if(StringUtils.isBlank(codeInRequest)){ throw new ValidateCodeException("验证码的值不能为空"); } if(codeInSession == null){ throw new ValidateCodeException("验证码不存在") } if(codeInSession.isExpried()){ sessionStrategy.removeAttribute(servletWebRequest,ValidateCodeController.SESSION_KEY); throw new ValidateCodeException("验证码已过期"); } if(StringUtils.equals(codeInSession.getCode(),codeInRequest)){ throw new ValidateCodeException("验证码不匹配") } sessionStrategy.removeAttribute(servletWebRequest,ValidateCodeController.SESSION_KEY); } }
自定义验证码校验异常ValidateCodeException
public class ValidateCodeException extends AuthenticationException {//security定义的抽象异常,是所有身份认证抛出异常的基类 public ValidateCodeException(String msg, Throwable t) { super(msg, t); } public ValidateCodeException(String msg) { super(msg); } }
校验验证码过滤器加入security过滤器链,并在UsernamePasswordAuthenticationFilter之前
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SecurityProperties securityProperties; @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 定义web安全配置类:覆盖config方法 * 1.参数为HttpSecurity */ @Override protected void configure(HttpSecurity http) throws Exception { /** * 定义了任何请求都需要表单认证 */ ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(); validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler); http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin()//表单登录---指定了身份认证方式 // .loginPage("/login.html") .loginPage("/authentication/require") .loginProcessingUrl("/authentication/form")//配置UsernamePasswordAuthenticationFilter需要拦截的请求 .successHandler(myAuthenticationSuccessHandler)//表单登录成功之后用自带的处理器 .failureHandler(myAuthenticationFailureHandler)//表单登录失败之后用自带的处理器 // http.httpBasic()//http的basic登录 .and() .authorizeRequests()//对请求进行授权 .antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage(),"/code/image").permitAll()//对匹配login.html的请求允许访问 .anyRequest()//任何请求 .authenticated() .and() .csrf().disable();//都需要认证 } }
自定义错误处理器
@Slf4j @Component public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Autowired private SecurityProperties securityProperties; //@Resource private ObjectMapper objectMapper = new ObjectMapper(); @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info("【登录失败】"); HashMap<String, Object> map = new HashMap<>(); if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){ //JSON response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setContentType("application/json;charset=UTF-8"); map.put("msg",exception.getMessage()); response.getWriter().write(objectMapper.writeValueAsString(ResponseEntity.status(500).body(map))); }else{ super.onAuthenticationFailure(request,response,exception); } } }
升级重构
在以上基础上升级重构图形验证码主要是下面3个方面:
- 验证码基本参数可配置
- 验证码拦截的接口可配置
- 验证码的生成逻辑可配置
验证码基本参数可配置
- 验证码长宽可配置
- 验证码位数可配置
- 验证码有效时间可配置
多级覆盖
默认配置
将验证码长宽可配置、验证码位数可配置、验证码有效时间可配置作为属性参数写到配置中;
ImageCodeProperties: public class ImageCodeProperties { private int width = 67; private int height = 23; private int length = 4; private int expireIn = 60; //getter setter }
配置成多个类级别配置:ValidateCodeProperties;
public class ValidateCodeProperties { private ImageCodeProperties image = new ImageCodeProperties(); public ImageCodeProperties getImage() { return image; } public void setImage(ImageCodeProperties image) { this.image = image; } }
封装到主配置中
//此类读取配置文件里所有以yxm.security开头的配置 @ConfigurationProperties(prefix = "yxm.security") public class SecurityProperties { //其中yxm.security.browser开头的配置否会读取到BrowserProperties中 private BrowserProperties browser = new BrowserProperties(); private ValidateCodeProperties code = new ValidateCodeProperties(); //getter setter }
应用级配置
在应用模块中的配置文件中设置,覆盖默认配置
yxm.security.code.image.length=6 //默认配置是4
请求级配置
public ImageCode generate(ServletWebRequest request) { //从request前端获取不到值 就用默认配置,能获取到值就用前端传递过来的配置 int width = ServletRequestUtils.getIntParameter(request.getRequest(),"width",securityProperties.getCode().getImage().getWidth()); int height = ServletRequestUtils.getIntParameter(request.getRequest(),"height",securityProperties.getCode().getImage().getHeight()); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Random random = new Random(); g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); g.setFont(new Font("Times New Roman", Font.ITALIC, 20)); g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } String sRand = ""; for (int i = 0; i < securityProperties.getCode().getImage().getLength(); i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); g.drawString(rand, 13 * i + 6, 16); } g.dispose(); return new ImageCode(image, sRand, securityProperties.getCode().getImage().getExpireIn()); }
页面请求参数设置为:200,应用demo配置中改为100;
tr> <td>图形验证码</td> <td> <input type="text" name="imageCode"> <img src="/code/image?width=200"> </td> </tr>
yxm.security.code.image.width=100
启动应用,发现:我们请求的url中width=200的值会覆盖掉:spring-security-demo里面的width=100的值;spring-security-demo里面的length=6的值覆盖掉系统默认的4。
验证码拦截的接口可配
ValidateCodeFilter过滤器的doFilterInternal方法只拦截/authentication/form接口,通过配置可拦截多个接口校验验证码
- ImageCodeProperties新增一个属性:private String url; //拦截的url
2. application.yml
# security配置 wzl: security: browser: loginPage: "/signIn.html" #当前模块resources/static/ # loginType: "REDIRECT" code: image: length: 5 width: 100 url: "/user,/user/**" #验证码校验的接口
3. ValidateCodeFilter验证码拦截器
1)在拦截器里声明一个set集合,用来存储配置文件里配置的需要拦截的urls。
2)实现InitializingBean接口,目的: 在其他参数都组装完毕的时候,初始化需要拦截的urls的值,重写afterPropertiesSet方法来实现。
3)注入SecurityProperties,读取配置文件
4)实例化AntPathMatcher工具类,这是一个匹配器
5)在browser项目的BrowserSecurityConfig里设置调用一下afterPropertiesSet方法。
6)在引用该模块的demo项目的application.properties里配置要过滤的url
ValidateCodeFilter
/** * 验证码过滤器 * @date 2020/2/24 **/ public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean { private AuthenticationFailureHandler authenticationFailureHandler; private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); private Logger logger = LoggerFactory.getLogger(getClass()); //需要校验的url都在这里面添加 private Set<String> urls = new HashSet<>(); private AntPathMatcher antPathMatcher = new AntPathMatcher(); //此处不用注解@Autowire 而是使用setter方法将在WebSecurityConfig设置 private SecurityProperties securityProperties; @Override public void afterPropertiesSet() throws ServletException { super.afterPropertiesSet(); //我们获取配置的ImageCodeProperties里面的url,转化为数据,添加到urls里面去 String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(), ","); for (String configUrl:configUrls) { urls.add(configUrl); } //"/authentication/form"一定会校验验证码的 urls.add("/authentication/form"); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { logger.info("验证码过滤器:doFilterInternal: requestURI:[{}] requestMethod:[{}]",request.getRequestURI(),request.getMethod()); /** * 如果是需要认证请求,我们进行家宴 * 如果校验失败,使用我们自定义的校验失败处理类处理 * 如果不需要认证,我们放行进入下一个Filter */ //在afterPropertiesSet执行之后,url初始化完毕之后,但是此时我们判断不能用StringUtils.equals,我们我们urls里面有 url: /user,/user/* 带星号的配置 // 用户请求有可能是/user/1、/user/2 我们需要使用Spring的 AntPathMatcher boolean action = false; for (String url:urls) { //如果配置的url和请求的url相同时候,需要校验 if(antPathMatcher.match(url,request.getRequestURI())){ action = true; } } if(action){ try{ validate(new ServletWebRequest(request)); }catch (ValidateCodeException e){ authenticationFailureHandler.onAuthenticationFailure(request,response,e); //抛出异常校验失败,不再走小面过滤器执行链 return; } } filterChain.doFilter(request,response); } private void validate(ServletWebRequest servletWebRequest) throws ServletRequestBindingException { //1.获取存放到session中的验证码 ImageCode codeInSession = (ImageCode)sessionStrategy.getAttribute(servletWebRequest, ValidateCodeController.SESSION_KEY); //2.获取请求中的验证码 String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "imageCode"); if(StringUtils.isBlank(codeInRequest)){ throw new ValidateCodeException("验证码的值不能为空"); } if(codeInSession == null){ throw new ValidateCodeException("验证码不存在"); } if(codeInSession.isExpried()){ sessionStrategy.removeAttribute(servletWebRequest,ValidateCodeController.SESSION_KEY); throw new ValidateCodeException("验证码已过期"); } if(!StringUtils.equals(codeInSession.getCode(),codeInRequest)){ throw new ValidateCodeException("验证码不匹配"); } sessionStrategy.removeAttribute(servletWebRequest,ValidateCodeController.SESSION_KEY); } public AuthenticationFailureHandler getAuthenticationFailureHandler() { return authenticationFailureHandler; } public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { this.authenticationFailureHandler = authenticationFailureHandler; } public SecurityProperties getSecurityProperties() { return securityProperties; } public void setSecurityProperties(SecurityProperties securityProperties) { this.securityProperties = securityProperties; } }
WebSecurityConfig
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SecurityProperties securityProperties; @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 定义web安全配置类:覆盖config方法 * 1.参数为HttpSecurity */ @Override protected void configure(HttpSecurity http) throws Exception { // 图片验证码过滤器 //创建验证码过滤器,验证器使用自定义错误处理 ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(myAuthenticationFailureHandler); validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler); //配置验证码过滤url validateCodeFilter.setSecurityProperties(securityProperties); validateCodeFilter.afterPropertiesSet(); /** * 定义了任何请求都需要表单认证 */ ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(); validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler); http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin()//表单登录---指定了身份认证方式 // .loginPage("/login.html") .loginPage("/authentication/require") .loginProcessingUrl("/authentication/form")//配置UsernamePasswordAuthenticationFilter需要拦截的请求 .successHandler(myAuthenticationSuccessHandler)//表单登录成功之后用自带的处理器 .failureHandler(myAuthenticationFailureHandler)//表单登录失败之后用自带的处理器 // http.httpBasic()//http的basic登录 .and() .authorizeRequests()//对请求进行授权 .antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage(),"/code/image").permitAll()//对匹配login.html的请求允许访问 .anyRequest()//任何请求 .authenticated() .and() .csrf().disable();//都需要认证 } }
配置url
yxm.security.code.image.length=6
yxm.security.code.image.width=100
yxm.security.code.image.url=/user,/user/*
验证码的生成逻辑可配置
生成验证码逻辑改为接口,有默认实现类,其它模块(demo)实现了该接口,生成验证码就使用模块生的产逻辑。
- 验证码生成提取成一个接口ValidateCodeGenerator,一个生成验证码的方法generator()
- 实现类ImageCodeGenerator,不加@Component注解
- 验证码bean的配置类ValidateCodeBeanConfig,使用@ConditionalOnMissingBean(name="imageCodeGenerator")注解,如果spring容器没有则使用默认的验证码生成器
- demo模块实现ValidateCodeGenerator(@Component("imageCodeGenerator")),替换默认验证码生成器
声明接口
声明一个接口(校验码生成器):ValidateCodeGenerator;将Controller里面生成的方法挪动到接口里面去。
public interface ValidateCodeGenerator { /** * 生成验证码 * @param request * @return */ ImageCode generate(ServletWebRequest request); }
实现类:ImageCodeGenerator
@Service public class ImageCodeGenerator implements ValidateCodeGenerator { private SecurityProperties securityProperties; @Override public ImageCode generate(ServletWebRequest request) { //从request前端获取不到值 就用默认配置,能获取到值就用前端传递过来的配置 int width = ServletRequestUtils.getIntParameter(request.getRequest(),"width",securityProperties.getCode().getImage().getWidth()); int height = ServletRequestUtils.getIntParameter(request.getRequest(),"height",securityProperties.getCode().getImage().getHeight()); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Random random = new Random(); g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); g.setFont(new Font("Times New Roman", Font.ITALIC, 20)); g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } String sRand = ""; for (int i = 0; i < securityProperties.getCode().getImage().getLength(); i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); g.drawString(rand, 13 * i + 6, 16); } g.dispose(); return new ImageCode(image, sRand, securityProperties.getCode().getImage().getExpireIn()); } /** * 生成随机背景条纹 * @param fc * @param bc * @return */ private Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) { fc = 255; } if (bc > 255) { bc = 255; } int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); } public SecurityProperties getSecurityProperties() { return securityProperties; } public void setSecurityProperties(SecurityProperties securityProperties) { this.securityProperties = securityProperties; } }
修改生成逻辑
ValidateCodeGenerator里面的实现逻辑注入到ValidateCodeController里面去
@RestController public class ValidateCodeController { public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE"; private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @Autowired private ValidateCodeGenerator imageCodeGenerator; @GetMapping("/code/image") public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException { /** * 1.根据随机数生成图片 * 2.将随机数存到session中 * 3.将生成图片写到接口的响应中 */ ImageCode imageCode = imageCodeGenerator.generate(new ServletWebRequest(request)); sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode); ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream()); } }
验证码bean的配置类ValidateCodeBeanConfig
@Configuration public class ValidateCodeBeanConfig { @Autowired private SecurityProperties securityProperties; /* * 这个配置与我们在ImageCodeGenerator上面加一个注解是类似的,但是这样配置灵活, * 可以添加注解:@ConditionalOnMissingBean 作用是:在初始化这个bean的时候, * 先到spring容器去查找imageCodeGenerator,如果有一个imageCodeGenerator时候, * 就不会再用下面代码去创建 **/ @Bean @ConditionalOnMissingBean(name="imageCodeGenerator") public ValidateCodeGenerator imageCodeGenerator(){//方法的名字就是放到Spring容器里bean的名字 ImageCodeGenerator imageCodeGenerator = new ImageCodeGenerator(); imageCodeGenerator.setSecurityProperties(securityProperties); return imageCodeGenerator; } }
应用层实现ValidateCodeGenerator
在spring-security-demo里面添加一个:DemoImageCodeGenrator,去实现ValidateCodeGenerator;Bean的名字和ValidateCodeBeanConfig里面配置的一直,这样的话在spring-security-web和spring-security-demo里面都有。
@Component("imageCodeGenrator") public class DemoImageCodeGenrator implements ValidateCodeGenerator { @Override public ImageCode generate(ServletWebRequest request) { System.out.println("更高级的图形验证码逻辑"); return null; } }
标签:int,request,验证码,private,SpringSecurity,new,public,图片 From: https://www.cnblogs.com/wangzhilei-src/p/17974560