Spring Boot 整合 Shiro(三)Kaptcha验证码 附源码

前言

本文是根据上篇《Spring Boot 整合Shiro(二)加密登录与密码加盐处理》进行修改,如有不明白的转上篇文章了解。

1.导入依赖


 
 
  1. <!-- https://mvnrepository.com/artifact/com.github.penggle/kaptcha -->
  2. <dependency>
  3. <groupId>com.github.penggle </groupId>
  4. <artifactId>kaptcha </artifactId>
  5. <version>2.3.2 </version>
  6. </dependency>

2.配置

创建KaptchaConfig.java


 
 
  1. import com.google.code.kaptcha.impl.DefaultKaptcha;
  2. import com.google.code.kaptcha.util.Config;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.stereotype.Component;
  5. import java.util.Properties;
  6. @Component
  7. public class KaptchaConfig {
  8. @Bean
  9. public DefaultKaptcha getDefaultKaptcha() {
  10. com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
  11. Properties properties = new Properties();
  12. // 图片边框
  13. properties.setProperty( "kaptcha.border", "yes");
  14. // 边框颜色
  15. properties.setProperty( "kaptcha.border.color", "105,179,90");
  16. // 字体颜色
  17. properties.setProperty( "kaptcha.textproducer.font.color", "red");
  18. // 图片宽
  19. properties.setProperty( "kaptcha.image.width", "110");
  20. // 图片高
  21. properties.setProperty( "kaptcha.image.height", "40");
  22. // 字体大小
  23. properties.setProperty( "kaptcha.textproducer.font.size", "30");
  24. // session key
  25. properties.setProperty( "kaptcha.session.key", "code");
  26. // 验证码长度
  27. properties.setProperty( "kaptcha.textproducer.char.length", "4");
  28. // 字体
  29. properties.setProperty( "kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
  30. //可以设置很多属性,具体看com.google.code.kaptcha.Constants
  31. // kaptcha.border 是否有边框 默认为true 我们可以自己设置yes,no
  32. // kaptcha.border.color 边框颜色 默认为Color.BLACK
  33. // kaptcha.border.thickness 边框粗细度 默认为1
  34. // kaptcha.producer.impl 验证码生成器 默认为DefaultKaptcha
  35. // kaptcha.textproducer.impl 验证码文本生成器 默认为DefaultTextCreator
  36. // kaptcha.textproducer.char.string 验证码文本字符内容范围 默认为abcde2345678gfynmnpwx
  37. // kaptcha.textproducer.char.length 验证码文本字符长度 默认为5
  38. // kaptcha.textproducer.font.names 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
  39. // kaptcha.textproducer.font.size 验证码文本字符大小 默认为40
  40. // kaptcha.textproducer.font.color 验证码文本字符颜色 默认为Color.BLACK
  41. // kaptcha.textproducer.char.space 验证码文本字符间距 默认为2
  42. // kaptcha.noise.impl 验证码噪点生成对象 默认为DefaultNoise
  43. // kaptcha.noise.color 验证码噪点颜色 默认为Color.BLACK
  44. // kaptcha.obscurificator.impl 验证码样式引擎 默认为WaterRipple
  45. // kaptcha.word.impl 验证码文本字符渲染 默认为DefaultWordRenderer
  46. // kaptcha.background.impl 验证码背景生成器 默认为DefaultBackground
  47. // kaptcha.background.clear.from 验证码背景颜色渐进 默认为Color.LIGHT_GRAY
  48. // kaptcha.background.clear.to 验证码背景颜色渐进 默认为Color.WHITE
  49. // kaptcha.image.width 验证码图片宽度 默认为200
  50. // kaptcha.image.height 验证码图片高度 默认为50
  51. Config config = new Config(properties);
  52. defaultKaptcha.setConfig(config);
  53. return defaultKaptcha;
  54. }
  55. }

3.验证码生成/验证


 
 
  1. import java.awt.image.BufferedImage;
  2. import java.io.ByteArrayOutputStream;
  3. import javax.imageio.ImageIO;
  4. import javax.servlet.ServletOutputStream;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Controller;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.servlet.ModelAndView;
  11. import com.google.code.kaptcha.impl.DefaultKaptcha;
  12. @Controller
  13. @RequestMapping(value = "/kaptcha" )
  14. public class KaptchaController {
  15. /**
  16. * 1、验证码工具
  17. */
  18. @Autowired
  19. DefaultKaptcha defaultKaptcha;
  20. /**
  21. * 2、生成验证码
  22. * @param httpServletRequest
  23. * @param httpServletResponse
  24. * @throws Exception
  25. */
  26. @RequestMapping( "/defaultKaptcha")
  27. public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
  28. throws Exception {
  29. byte[] captchaChallengeAsJpeg = null;
  30. ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
  31. try {
  32. // 生产验证码字符串并保存到session中
  33. String createText = defaultKaptcha.createText();
  34. httpServletRequest.getSession().setAttribute( "rightCode", createText);
  35. // 使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
  36. BufferedImage challenge = defaultKaptcha.createImage(createText);
  37. ImageIO.write(challenge, "jpg", jpegOutputStream);
  38. } catch (IllegalArgumentException e) {
  39. httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
  40. return;
  41. }
  42. // 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
  43. captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
  44. httpServletResponse.setHeader( "Cache-Control", "no-store");
  45. httpServletResponse.setHeader( "Pragma", "no-cache");
  46. httpServletResponse.setDateHeader( "Expires", 0);
  47. httpServletResponse.setContentType( "image/jpeg");
  48. ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
  49. responseOutputStream.write(captchaChallengeAsJpeg);
  50. responseOutputStream.flush();
  51. responseOutputStream.close();
  52. }
  53. /**
  54. * 3、校对验证码
  55. * @param httpServletRequest
  56. * @param httpServletResponse
  57. * @return
  58. */
  59. @RequestMapping( "/imgvrifyControllerDefaultKaptcha")
  60. public ModelAndView imgvrifyControllerDefaultKaptcha(HttpServletRequest httpServletRequest,
  61. HttpServletResponse httpServletResponse) {
  62. ModelAndView andView = new ModelAndView();
  63. String rightCode = (String) httpServletRequest.getSession().getAttribute( "rightCode");
  64. String tryCode = httpServletRequest.getParameter( "tryCode");
  65. System.out.println( "rightCode:"+rightCode+ " ———— tryCode:"+tryCode);
  66. if (!rightCode.equals(tryCode)) {
  67. andView.addObject( "info", "错误的验证码");
  68. andView.setViewName( "/login");
  69. } else {
  70. andView.addObject( "info", "登录成功");
  71. andView.setViewName( "success");
  72. }
  73. return andView;
  74. }
  75. }

4.修改ShiroConfig

开放相应的验证码路径,避免拦截


 
 
  1. @Bean(name = "shiroFilter")
  2. public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
  3. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  4. // Shiro的核心安全接口,这个属性是必须的
  5. shiroFilterFactoryBean.setSecurityManager(securityManager);
  6. // 身份认证失败,则跳转到登录页面的配置
  7. shiroFilterFactoryBean.setLoginUrl( "/login");
  8. // 权限认证失败,则跳转到指定页面
  9. shiroFilterFactoryBean.setUnauthorizedUrl( "/notRole");
  10. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
  11. // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
  12. filterChainDefinitionMap.put( "/webjars/**", "anon");
  13. filterChainDefinitionMap.put( "/login", "anon");
  14. filterChainDefinitionMap.put( "/loginOut", "anon");
  15. filterChainDefinitionMap.put( "/", "anon");
  16. filterChainDefinitionMap.put( "/front/**", "anon");
  17. filterChainDefinitionMap.put( "/api/**", "anon");
  18. filterChainDefinitionMap.put( "/kaptcha/**", "anon");
  19. filterChainDefinitionMap.put( "/success/**", "anon");
  20. filterChainDefinitionMap.put( "/admin/**", "authc");
  21. filterChainDefinitionMap.put( "/user/**", "authc");
  22. //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
  23. filterChainDefinitionMap.put( "/**", "authc");
  24. shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
  25. return shiroFilterFactoryBean;
  26. }

5.修改LoginController


 
 
  1. @Controller
  2. @RequestMapping
  3. public class LoginController {
  4. private Logger logger = Logger.getLogger( this.getClass().getName());
  5. /**
  6. * 界面
  7. * @return
  8. */
  9. @RequestMapping(value = "/login", method = RequestMethod.GET)
  10. public String defaultLogin() {
  11. return "login";
  12. }
  13. /**
  14. * 退出
  15. * @return
  16. */
  17. @RequestMapping(value = "/loginOut", method = RequestMethod.GET)
  18. public String loginOut() {
  19. Subject subject = SecurityUtils.getSubject();
  20. subject.logout();
  21. return "login";
  22. }
  23. /**
  24. * 登录提交
  25. * @param username
  26. * @param tryCode
  27. * @param password
  28. * @param redirectAttributes
  29. * @return
  30. */
  31. @RequestMapping(value = "/login", method = RequestMethod.POST)
  32. public String login(@RequestParam("username") String username,
  33. @RequestParam("tryCode") String tryCode,
  34. @RequestParam("password") String password,
  35. RedirectAttributes redirectAttributes) {
  36. //判断验证码
  37. if(StringUtils.isBlank(tryCode)){
  38. logger.info( "验证码为空了!");
  39. redirectAttributes.addFlashAttribute( "message", "验证码不能为空!");
  40. return "redirect:login";
  41. }
  42. Session session = SecurityUtils.getSubject().getSession();
  43. String code = (String) session.getAttribute( "rightCode");
  44. System.out.println(code+ "*************"+tryCode);
  45. if(!tryCode.equalsIgnoreCase(code)){
  46. logger.info( "验证码错误!");
  47. redirectAttributes.addFlashAttribute( "message", "验证码错误!");
  48. return "redirect:login";
  49. }
  50. // 从SecurityUtils里边创建一个 subject
  51. Subject subject = SecurityUtils.getSubject();
  52. // 在认证提交前准备 token(令牌)
  53. UsernamePasswordToken token = new UsernamePasswordToken(username, password);
  54. String attributeValue = null;
  55. // 执行认证登陆
  56. try {
  57. subject.login(token);
  58. } catch (UnknownAccountException uae) {
  59. attributeValue= "未知账户!";
  60. } catch (IncorrectCredentialsException ice) {
  61. attributeValue= "密码不正确!";
  62. } catch (LockedAccountException lae) {
  63. attributeValue= "账户已锁定";
  64. } catch (ExcessiveAttemptsException eae) {
  65. attributeValue= "用户名或密码错误次数过多";
  66. } catch (AuthenticationException ae) {
  67. attributeValue= "用户名或密码不正确!";
  68. } finally {
  69. redirectAttributes.addFlashAttribute( "message", attributeValue);
  70. if (subject.isAuthenticated()) {
  71. return "success";
  72. } else {
  73. token.clear();
  74. return "redirect:login";
  75. }
  76. }
  77. }
  78. }

6.界面

6.1整合thymeleaf模板

添加依赖;


 
 
  1. <!--thymeleaf-->
  2. <dependency>
  3. <groupId>org.springframework.boot </groupId>
  4. <artifactId>spring-boot-starter-thymeleaf </artifactId>
  5. </dependency>

编辑application.yml


 
 
  1. server:
  2. port: 80
  3. servlet:
  4. context-path: /
  5. #thymeleaf模板
  6. spring:
  7. thymeleaf:
  8. cache: true
  9. prefix:
  10. classpath: /templates/
  11. suffix: .html
  12. mode: HTML5
  13. encoding: UTF -8
  14. servlet:
  15. content-type: text/html

 

6.2创建

login.html


 
 
  1. <!DOCTYPE html>
  2. <!-- thymeleaf 提示功能 -->
  3. <html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
  4. <head lang="en">
  5. <meta charset="UTF-8"> </meta>
  6. <title>验证码 </title>
  7. </head>
  8. <style type="text/css">
  9. body {
  10. padding: 10px
  11. }
  12. </style>
  13. <body>
  14. <!-- 提示 -->
  15. <h3 th:text="${message}"> </h3>
  16. <div>
  17. <!-- 后面添加参数起到清除缓存作用 -->
  18. <img alt="验证码" οnclick="this.src='/kaptcha/defaultKaptcha?d='+new Date()*1" src="/kaptcha/defaultKaptcha" />
  19. </div>
  20. <form action="/login" method="post">
  21. 账户: <input type="text" name="username" /> </br>
  22. 密码: <input type="text" name="password" /> </br>
  23. 验证码: <input type="text" name="tryCode" />
  24. <input type="submit" value="提交" > </input>
  25. </form>
  26. </body>
  27. </html>

success.html


 
 
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8"> </meta>
  5. <title>成功 </title>
  6. </head>
  7. <body>
  8. <h1>登录成功 </h1>
  9. <a href="/loginOut">退出 </a>
  10. </body>
  11. </html>

 

7. 实战演练

 

8.项目源码

《源码下载》

下篇介绍Spring Boot 整合 Shiro(四)thymeleaf模板权限控制 附源码

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值