转载

Spring Boot Shiro权限管理--自定义 FormAuthenticationFilter验证码整合

  验证码

思路
shiro使用FormAuthenticationFilter进行表单认证,验证校验的功能应该加在FormAuthenticationFilter中,在认证之前进行验证码校验。

需要写FormAuthenticationFilter的子类,继承FormAuthenticationFilter,改写它的认证方法,在认证之前进行验证码校验。

自定义FormAuthenticationFilter

  1. package com.example.config.shiro;
  2. import javax.servlet.ServletRequest;
  3. import javax.servlet.ServletResponse;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpSession;
  6. import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
  7. public class CustomFormAuthenticationFilter extends FormAuthenticationFilter{
  8. @Override
  9. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
  10. // 在这里进行验证码的校验
  11. HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  12. HttpSession session = httpServletRequest.getSession();
  13. // 取出验证码
  14. String validateCode = (String) session.getAttribute("validateCode");
  15. // 取出页面的验证码
  16. // 输入的验证和session中的验证进行对比
  17. String randomcode = httpServletRequest.getParameter("randomcode");
  18. if (randomcode != null && validateCode != null && !randomcode.equals(validateCode)) {
  19. // 如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
  20. httpServletRequest.setAttribute("shiroLoginFailure", "kaptchaValidateFailed");//自定义登录异常
  21. // 拒绝访问,不再校验账号和密码
  22. return true;
  23. }
  24. return super.onAccessDenied(request, response);
  25. }
  26. }


在ShiroConfiguration.java中的shiroFilter方法注入自定义FormAuthenticationFilter

  1. @Bean
  2. public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
  3. System.out.println("ShiroConfiguration.shiroFilter()");
  4. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  5. Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters
  6. filters.put("authc", new CustomFormAuthenticationFilter());//将自定义 的FormAuthenticationFilter注入shiroFilter中
  7. // 必须设置SecuritManager
  8. shiroFilterFactoryBean.setSecurityManager(securityManager);
  9. // 拦截器
  10. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
  11. // 配置退出过滤器,其中的具体代码Shiro已经替我们实现了
  12. filterChainDefinitionMap.put("/logout", "logout");
  13. //验证码可以匿名访问
  14. filterChainDefinitionMap.put("/validatecodeServlet", "anon");
  15. //配置记住我或认证通过可以访问的地址
  16. filterChainDefinitionMap.put("/index", "user");
  17. filterChainDefinitionMap.put("/", "user");
  18. // <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
  19. // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
  20. filterChainDefinitionMap.put("/**", "authc");
  21. // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
  22. shiroFilterFactoryBean.setLoginUrl("/login");
  23. // 登录成功后要跳转的链接
  24. shiroFilterFactoryBean.setSuccessUrl("/index");
  25. // 未授权界面;
  26. shiroFilterFactoryBean.setUnauthorizedUrl("/403");
  27. shiroFilterFactoryBean
  28. .setFilterChainDefinitionMap(filterChainDefinitionMap);
  29. return shiroFilterFactoryBean;
  30. }


ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters
filters.put("authc", new CustomFormAuthenticationFilter());//将自定义 的FormAuthenticationFilter注入shiroFilter中  


登录方法加入自定义的异常kaptchaValidateFailed

  1. @RequestMapping(value = "/login", method = RequestMethod.POST)
  2. public String login(HttpServletRequest request, Map<String, Object> map) {
  3. System.out.println("HomeController.login");
  4. // 登录失败从request中获取shiro处理的异常信息
  5. // shiroLoginFailure:就是shiro异常类的全类名
  6. String exception = (String) request.getAttribute("shiroLoginFailure");
  7. String msg = "";
  8. if (exception != null) {
  9. if (UnknownAccountException.class.getName().equals(exception)) {
  10. System.out.println("UnknownAccountException -->帐号不存在:");
  11. msg = "UnknownAccountException -->帐号不存在:";
  12. } else if (IncorrectCredentialsException.class.getName().equals(exception)) {
  13. System.out.println("IncorrectCredentialsException -- > 密码不正确:");
  14. msg = "IncorrectCredentialsException -- > 密码不正确:";
  15. } else if ("kaptchaValidateFailed".equals(exception)) {
  16. System.out.println("kaptchaValidateFailed -- > 验证码错误");
  17. msg = "kaptchaValidateFailed -- > 验证码错误";
  18. } else {
  19. msg = "else >> " + exception;
  20. System.out.println("else -- >" + exception);
  21. }
  22. }
  23. map.put("msg", msg);
  24. // 此方法不处理登录成功,由shiro进行处理.
  25. return "/login";
  26. }

login.html

  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
  3. xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
  4. <head>
  5. <meta charset="UTF-8" />
  6. <title>Insert title here</title>
  7. </head>
  8. <body>
  9. 错误信息:<h4 th:text="${msg}"></h4>
  10. <form action="" method="post">
  11. <p>账号:<input type="text" name="username" value="admin"/></p>
  12. <p>密码:<input type="text" name="password" value="123456"/></p>
  13. <p>验证码:<input type="text" name="randomcode"/>
  14. <img th:src="@{/validatecodeServlet}" height="20px" width="60px" onclick="random(this)"/></p>
  15. <P><input type="checkbox" name="rememberMe" />记住我</P>
  16. <p><input type="submit" value="登录"/></p>
  17. </form>
  18. <script th:inline="javascript">
  19. function random(tmp){
  20. tmp.src="/validatecodeServlet?rnd="+Math.random();
  21. }
  22. </script>
  23. </body>
  24. </html>


验证码Servlet

  1. package com.example.servlet;
  2. import java.awt.Color;
  3. import java.awt.Font;
  4. import java.awt.Graphics;
  5. import java.awt.image.BufferedImage;
  6. import java.io.IOException;
  7. import java.io.OutputStream;
  8. import java.util.Random;
  9. import javax.imageio.ImageIO;
  10. import javax.servlet.ServletException;
  11. import javax.servlet.annotation.WebServlet;
  12. import javax.servlet.http.HttpServlet;
  13. import javax.servlet.http.HttpServletRequest;
  14. import javax.servlet.http.HttpServletResponse;
  15. import javax.servlet.http.HttpSession;
  16. @WebServlet(urlPatterns="/validatecodeServlet")
  17. public class ValidatecodeServlet extends HttpServlet{
  18. /**
  19. *
  20. */
  21. private static final long serialVersionUID = 1L;
  22. @Override
  23. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  24. System.out.println(">>>>>>>>>>doGet()<<<<<<<<<<<");
  25. doPost(req, resp);
  26. }
  27. @Override
  28. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  29. System.out.println(">>>>>>>>>>doPost()<<<<<<<<<<<");
  30. int width = 60;
  31. int height = 32;
  32. //create the image
  33. BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  34. Graphics g = image.getGraphics();
  35. // set the background color
  36. g.setColor(new Color(0xDCDCDC));
  37. g.fillRect(0, 0, width, height);
  38. // draw the border
  39. g.setColor(Color.black);
  40. g.drawRect(0, 0, width - 1, height - 1);
  41. // create a random instance to generate the codes
  42. Random rdm = new Random();
  43. String hash1 = Integer.toHexString(rdm.nextInt());
  44. System.out.print(hash1);
  45. // make some confusion
  46. for (int i = 0; i < 50; i++) {
  47. int x = rdm.nextInt(width);
  48. int y = rdm.nextInt(height);
  49. g.drawOval(x, y, 0, 0);
  50. }
  51. // generate a random code
  52. String capstr = hash1.substring(0, 4);
  53. HttpSession session = req.getSession(true);
  54. //将生成的验证码存入session
  55. session.setAttribute("validateCode", capstr);
  56. g.setColor(new Color(0, 100, 0));
  57. g.setFont(new Font("Candara", Font.BOLD, 24));
  58. g.drawString(capstr, 8, 24);
  59. g.dispose();
  60. //输出图片
  61. resp.setContentType("image/jpeg");
  62. OutputStream strm = resp.getOutputStream();
  63. ImageIO.write(image, "jpeg", strm);
  64. strm.close();
  65. }
  66. }


Application.java

  1. package com.example;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.web.servlet.ServletComponentScan;
  5. @ServletComponentScan
  6. @SpringBootApplication
  7. public class Application {
  8. public static void main(String[] args) {
  9. SpringApplication.run(Application.class, args);
  10. }
  11. }

正文到此结束
Loading...