下面我会给出关于“Spring Security 图片验证码功能的实例代码”的详细攻略。首先,我们需要思考一下问题,什么是图片验证码,为什么要使用它。
图片验证码就是在需要用户输入验证码时,生成一张随机的图片,用户需要识别图片中的验证码才能通过验证。由于图片验证码中的验证码是随机生成的,所以可以有效地避免机器人或爬虫等自动化程序的攻击。
在Spring Security中,我们可以使用项目中已经集成的验证码组件,或是自定义生成验证码的方法。下面给出两个示例,帮助大家更好地理解Spring Security中如何实现验证码功能。
示例一:使用项目中集成的验证码组件
首先,我们编写一个自定义的 AuthenticationProvider
,用来验证用户输入的用户名和密码是否正确。
public class MyAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// 省略从数据库中读取用户信息的代码
// 如果用户不存在,或者密码不正确,抛出异常
if(user == null || !password.equals(user.getPassword())) {
throw new BadCredentialsException("用户名或密码不正确");
}
// 验证通过,返回一个认证对象
return new UsernamePasswordAuthenticationToken(username, password, user.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
接着,在Spring Security的配置文件中,我们需要添加验证码过滤器。这个过滤器会在用户登录时生成一个验证码,并将验证码放到Session中。用户输入的验证码会与Session中的验证码进行对比,从而验证验证码的正确性。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationProvider authenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class) // 添加验证码过滤器
.authenticationProvider(authenticationProvider);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
最后,我们需要编写验证码生成和验证码校验的代码。这里我们使用Spring Security项目中集成的验证码组件,只需要在配置文件中添加下面的代码即可。需要注意的是,这里的 verifyCodeParameter
和 verifyCodeSessionAttribute
配置为了与默认值保持一致。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 省略其他配置代码
@Bean
public DefaultKaptcha captchaProducer() {
Properties properties = new Properties();
properties.setProperty("kaptcha.border", "no");
properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
properties.setProperty("kaptcha.textproducer.char.length", "4");
properties.setProperty("kaptcha.image.width", "100");
properties.setProperty("kaptcha.image.height", "40");
Config config = new Config(properties);
DefaultKaptcha captchaProducer = new DefaultKaptcha();
captchaProducer.setConfig(config);
return captchaProducer;
}
@Autowired
private DefaultKaptcha captchaProducer;
@Bean
public VerifyCodeFilter verifyCodeFilter() {
return new VerifyCodeFilter();
}
private class VerifyCodeFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(!"/login".equals(request.getRequestURI())) {
filterChain.doFilter(request, response);
return;
}
// 生成验证码
String text = captchaProducer.createText();
BufferedImage image = captchaProducer.createImage(text);
// 将验证码放到Session中
request.getSession().setAttribute("verifyCode", text);
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
// 输出验证码图片
try(ServletOutputStream out = response.getOutputStream()) {
ImageIO.write(image, "jpeg", out);
out.flush();
}
}
}
@Bean
public VerifyCodeService verifyCodeService() {
return new VerifyCodeService();
}
private class VerifyCodeService implements InitializingBean {
private AbstractHttpSessionApplicationInitializer httpSessionApplicationInitializer;
public VerifyCodeService() {}
public boolean validate(HttpServletRequest request, HttpServletResponse response) {
String verifyCode = (String)request.getSession().getAttribute("verifyCode");
String inputVerifyCode = request.getParameter("verifyCode");
request.getSession().removeAttribute("verifyCode");
return verifyCode != null && verifyCode.equalsIgnoreCase(inputVerifyCode);
}
@Override
public void afterPropertiesSet() throws Exception {
// 获取httpSessionApplicationInitializer实例
ApplicationContext context = ApplicationContextProvider.getApplicationContext();
httpSessionApplicationInitializer = context.getBean(AbstractHttpSessionApplicationInitializer.class); }
}
}
示例二:自定义生成验证码
如果我们不想使用Spring Security项目中集成的验证码组件,我们也可以编写自定义的验证码生成和验证码校验的代码。
首先,我们需要定义一个 CaptchaGenerator
类,用来生成验证码。这个类的实现方式可以很简单,比如下面的代码:
public class CaptchaGenerator {
public static final String DEFAULT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
private static final Random rand = new Random();
public static String generate(int length) {
char[] chars = DEFAULT_CHARACTERS.toCharArray();
StringBuilder sb = new StringBuilder();
for(int i = 0; i < length; i++) {
char c = chars[rand.nextInt(chars.length)];
sb.append(c);
}
return sb.toString();
}
}
接着,我们需要编写验证码生成和验证码校验的过滤器。这里的代码与示例一大致相同,只是验证码的生成和校验方式有所不同。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 省略其他配置代码
@Autowired
private CaptchaGenerator captchaGenerator;
@Bean
public VerifyCodeFilter verifyCodeFilter() {
return new VerifyCodeFilter();
}
private class VerifyCodeFilter extends OncePerRequestFilter {
public static final String SESSION_ATTRIBUTE_NAME = "captcha";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(!"/login".equals(request.getRequestURI())) {
filterChain.doFilter(request, response);
return;
}
// 生成验证码
String captcha = captchaGenerator.generate(4);
byte[] captchaBytes = captcha.getBytes();
request.getSession().setAttribute(SESSION_ATTRIBUTE_NAME, captchaBytes);
response.setContentType("image/png");
// 输出验证码图片
try (ServletOutputStream out = response.getOutputStream()) {
ImageIO.write(createImage(captchaBytes), "png", out);
out.flush();
}
}
private BufferedImage createImage(byte[] captcha) {
BufferedImage image = new BufferedImage(100, 40, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
Font font = new Font("Georgia", Font.PLAIN, 18);
// 绘制验证码文本
for(int i = 0, x = 10; i < captcha.length; i++, x += 20) {
g.setFont(font);
g.setColor(new Color((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255)));
g.drawString(String.valueOf((char)captcha[i]), x, 20);
}
// 绘制干扰线
for(int i = 0; i < 6; i++) {
g.setColor(new Color((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255)));
g.drawLine(rand.nextInt(80), rand.nextInt(30) + 5, rand.nextInt(80), rand.nextInt(30) + 5);
}
return image;
}
}
@Bean
public VerifyCodeService verifyCodeService() {
return new VerifyCodeService();
}
private class VerifyCodeService implements InitializingBean {
private AbstractHttpSessionApplicationInitializer httpSessionApplicationInitializer;
public VerifyCodeService() {}
public boolean validate(HttpServletRequest request, HttpServletResponse response) {
byte[] captcha = (byte[])request.getSession().getAttribute(VerifyCodeFilter.SESSION_ATTRIBUTE_NAME);
String inputCaptcha = request.getParameter("captcha");
return captcha != null && inputCaptcha != null && Arrays.equals(captcha, inputCaptcha.getBytes());
}
@Override
public void afterPropertiesSet() throws Exception {
// 获取httpSessionApplicationInitializer实例
ApplicationContext context = ApplicationContextProvider.getApplicationContext();
httpSessionApplicationInitializer = context.getBean(AbstractHttpSessionApplicationInitializer.class); }
}
}
最后需要注意的是,在配置文件中添加验证码过滤器时,需要将它加到用户名密码过滤器的前面。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationProvider authenticationProvider;
@Autowired
private VerifyCodeFilter verifyCodeFilter;
// 省略其他配置代码
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码过滤器
.authenticationProvider(authenticationProvider);
}
// 省略其他配置代码
}
以上就是关于“Spring Security 图片验证码功能的实例代码”的详细攻略,希望能够对你有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security 图片验证码功能的实例代码 - Python技术站