AOP —— 可以动态的增减程序而不改变其他功能源码

    • 优化自动登录过滤器处理
      访问登录页时,触发了自动登录,则直接访问主页面
      访问其他资源时,触发了自动登录,则继续访问目标资源
      ```java //LoginController.java package com.duyi.qxgl.controller;

    import com.duyi.qxgl.domain.Token; import com.duyi.qxgl.domain.User; import com.duyi.qxgl.service.UserService; import org.apache.poi.ss.formula.functions.T;

    import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.UUID;

    // 服务器和浏览器的对接 public class LoginController extends HttpServlet {

    1. @Override
    2. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    3. //验证此次登录的账号和密码是否正确
    4. //要想验证,需要先获得账号密码
    5. //如何获得账号密码
    6. //根据请求时传递的参数key,获得对应的参数值。
    7. // 这个key就是网页中input.name值,获得的参数值就是网页中input输入的值
    8. String uname = req.getParameter("uname");
    9. String upass = req.getParameter("upass");
    10. String autoflag = req.getParameter("autoflag");
    11. //service查找
    12. UserService service = new UserService();
    13. User user =service.checkLogin(uname,upass);
    14. if(user == null){
    15. //根据账号密码,没有找到数据--失败,重新显示登录界面
    16. resp.sendRedirect("login.view?f=9");
    17. }else {
    18. //自动登录处理
    19. if(autoflag != null && !"".equals(autoflag)){
    20. //要实现自动登录
    21. // 生成一个uuid的随机数
    22. String tokenId = UUID.randomUUID().toString();
    23. // 将该令牌存到cookie里
    24. Cookie c = new Cookie("tokenId",tokenId);
    25. // cookie在浏览器的存活时间,以秒为单位
    26. c.setMaxAge(60);
    27. // 将cookie响应到浏览器
    28. resp.addCookie(c);
    29. //将信息存起来,产生一个Token对象,Token对象需存在服务器端
    30. // 将令牌和登录成功的对象存到里面
    31. Token token = new Token(
    32. tokenId,
    33. user,
    34. //可以获得局域网ip,广域网使用代理后还要其他方式获得
    35. req.getRemoteAddr(),
    36. System.currentTimeMillis(),//当前的系统时间
    37. System.currentTimeMillis()+1000L*60// 七天:1000L*60*60*24*7
    38. );
    39. //token在服务端如何存储
    40. // request不行,session不行
    41. // 数据库浪费空间或影响效率
    42. //application(服务器运行期缓存),redis缓存中√
    43. // 在服务器运行时间当中,都是可以的
    44. //tokenid在浏览器端一段时间后就清除了
    45. //一旦tokenid在客户端清除,服务端对应的那个token对象也就没有意义了。
    46. //但服务器不知道tokenid不知,如果一直保留,会产生内存垃圾,假设足够多,会出现内存泄露
    47. //如果使用redis缓存数据库存储,在存储时可以设置存活时间
    48. //如果存在服务器中,可以自定义线程,一段时间扫描所有过期token
    49. req.getServletContext().setAttribute(tokenId,token);
    50. }
    51. //登录成功。显示主界面(重定向--间接响应)
    52. // 将登录成功信息存在session里
    53. // 这个session是浏览器和服务器交互的缓存,存储的信息不一样
    54. req.getSession().setAttribute("loginUser",user);
    55. resp.sendRedirect("main.html");
    56. //直接响应
    57. //resp.getWriter().write("main.html");
    58. }
    59. }

    }

    1. * 扩展:<br /> * 自定义多线程,每隔一段时间扫描服务器,清除过期的token<br /> * 使用自定义监听器/过滤器,在服务器启动时开启扫描线程<br /> <br />监听器启动的最快
    2. ```java
    3. //ServerStartListener.java
    4. package com.duyi.qxgl.util;
    5. import javax.servlet.ServletContextEvent;
    6. import javax.servlet.ServletContextListener;
    7. //一个可以监听服务器启动和销毁的监听器
    8. public class ServerStartListener implements ServletContextListener {
    9. @Override
    10. public void contextInitialized(ServletContextEvent sce) {
    11. //服务器启动执行
    12. new TokenScanThread( sce.getServletContext() ).start();
    13. }
    14. }

    补充:
    servletContext接口是Servlet中最大的一个接口,呈现了web应用的Servlet视图。ServletContext实例是通过 getServletContext()方法获得的,由于HttpServlet继承GenericServlet的关系,GenericServlet类和HttpServlet类同时具有该方法。

    1. //TokenScanThread.java
    2. package com.duyi.qxgl.util;
    3. import com.duyi.qxgl.domain.Token;
    4. import javax.servlet.ServletContext;
    5. import java.util.Enumeration;
    6. //扫描服务器过期token的线程 (可以并发执行)
    7. // is - a Thread
    8. // 接口可以多实现,但是java只能单继承(使用线程池)
    9. public class TokenScanThread extends Thread implements Runnable{
    10. private ServletContext application;
    11. // 服务器
    12. TokenScanThread(ServletContext application) {
    13. this.application = application;
    14. }
    15. @Override
    16. public void run() {
    17. //每隔一段时间循环
    18. int second = 0;
    19. while (true){
    20. try {
    21. // Thread.sleep(1000);//1秒
    22. // second += 1000;
    23. Thread.sleep(30000);
    24. if(second >= 30000){
    25. //每隔30s,扫描服务器(需要application对象)
    26. //枚举集合(类似于迭代器) 获得application中的属性名
    27. Enumeration<String> enums = application.getAttributeNames();
    28. while (enums.hasMoreElements()){
    29. String name = enums.nextElement();
    30. Object value = application.getAttribute(name);
    31. if(value instanceof Token){//判断类型
    32. //这是一个令牌对象
    33. Token token = (Token) value;
    34. if(token.getEnd() < System.currentTimeMillis()){
    35. //已经过期了
    36. application.removeAttribute(name);
    37. }
    38. }
    39. }
    40. second = 0;
    41. continue; //进行下一轮
    42. }
    43. }catch (InterruptedException e){
    44. e.printStackTrace();
    45. }
    46. }
    47. }
    48. }

    * 验证码
    在服务端可以使用java技术(GUI)画出一个验证码的图片
    将图片的中的验证码保存起来
    * 将图片响应给浏览器

    * 在登录页面中增加验证码输入<br />  html.append("                   <td colspan='2'><input style='width:100px;height:30px;' type=\"text\" name=\"code\" class=\"layui-input-inline\"/><img src='generatcheckcode'/></td>");<br />       
    
    * 验证码请求也需要排除在登录认证之外。    //过滤器
    
    //LoginView.java
    package com.duyi.qxgl07.view;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    //是一个用来动态拼装登录页面的servlet
    public class LoginView extends HttpServlet {
        /*
            405 : 表示处理请求的方法没有找到
                service可以处理get和post请求
                doGet可以处理get请求
                doPost可以处理post请求
         */
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //拼页面
            String f = req.getParameter("f");
            resp.setCharacterEncoding("utf-8"); //服务器响应时使用utf-8处理
            StringBuilder html = new StringBuilder();
            html.append("<html>");
            html.append("   <head>");
            html.append("       <title>登录</title>");
            html.append("       <meta charset=\"utf-8\"/>"); //告诉浏览器显示时按照 utf-8处理
            html.append("       <link rel=\"stylesheet\" type=\"text/css\" href=\"layui/css/layui.css\" />");
            html.append("       <script src=\"layui/layui.all.js\"></script>");
            html.append("       <style>");
            html.append("           body{");
            html.append("               width:100%;");
            html.append("               height:100%;");
            html.append("               background-image: url(imgs/login_bg.jpg);");
            html.append("           }");
            html.append("           .layui-card{");
            html.append("               width:350px;");
            html.append("               margin:100px auto ;");
            html.append("               opacity: 0.8;");
            html.append("           }");
            html.append("           tr{");
            html.append("               height:50px;");
            html.append("           }");
            html.append("       </style>");
            html.append("       <script>");
            //需要判断,在显示
            if(f != null && "9".equals(f)){
                html.append("       alert('用户名密码错误');");
            }
            if(f != null && "8".equals(f)){
                html.append("       alert('验证码不正确');");
            }
            html.append("       </script>");
            html.append("   </head>");
            html.append("   <body>");
            html.append("       <div class=\"layui-card\">");
            html.append("           <div class=\"layui-card-header\">用户登录</div>");
            html.append("           <div class=\"layui-card-body\">");
            html.append("               <form action=\"login\" method=\"post\">");
            html.append("                   <table>");
            html.append("                       <tr>");
            html.append("                           <td>用户名:</td>");
            html.append("                           <td>");
            html.append("                               <input type=\"text\" name=\"uname\" required lay-verify=\"required\"  class=\"layui-input\">");
            html.append("                           </td>");
            html.append("                       </tr>");
            html.append("                       <tr>");
            html.append("                           <td>密码:</td>");
            html.append("                           <td><input type=\"password\" name=\"upass\" class=\"layui-input\" /></td>");
            html.append("                       </tr>");
            html.append("                       <tr>");
            html.append("                           <td colspan='2'><input style='width:100px;height:30px;' type=\"text\" name=\"code\" class=\"layui-input-inline\" /> <img src='generatcheckcode' /></td>");
            html.append("                       </tr>");
            html.append("                       <tr>");
            html.append("                           <td colspan='2'><input type=\"checkbox\" name=\"autoflag\" /> 1周内自动登录</td>");
            html.append("                       </tr>");
            html.append("                       <tr>");
            html.append("                           <td> <button>登录</button> </td>");
            html.append("                       </tr>");
            html.append("                   </table>");
            html.append("               </form>");
            html.append("           </div>");
            html.append("       </div>");
            html.append("   </body>");
            html.append("</html>");
    
            resp.getWriter().write(html.toString());
    
            /*
                servlet 和 jsp都可以拼装动态网页
                servlet = java(主场) + html
                jsp = java +  html(主场)
    
             */
        }
    }
    
    //GeneratCheckcodeController.java
    package com.duyi.qxgl07.controller;
    
    import javax.imageio.ImageIO;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Random;
    
    /**
     * 生成验证码图片,并响应
     */
    public class GeneratCheckcodeController extends HttpServlet {
    
        private static final String source = "0123456789ABCDEFG" ;//验证码来源
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String code = "" ;//装载验证码
    
            Random r = new Random() ;
            for(int i=0;i<4;i++){
                //产生4位验证码,每次从source中随机获得一个符号
                int index = r.nextInt(source.length());
                char c = source.charAt(index);
                code += c ;    //这里的相加是连接四个字符的意思
            }
            //先存起来,一会登录的时候使用。
            req.getSession().setAttribute("code",code);
    
            //---------使用GUI画一个验证码图片-----------
            //一个jvm中的图片    File - 本地文件
            BufferedImage image = new BufferedImage( 80,30,BufferedImage.TYPE_INT_RGB );
    
            //画图笔
            Graphics g = image.createGraphics() ;
    
            //设置字体 -- 红色
            Font font = new Font("黑体", Font.BOLD,20);
            g.setFont(font);
            g.setColor(new Color(250,0,0));
    
            //设置背景 -- 黄色
            g.fillRect(0,0,80,30);
            g.setColor(new Color(255,255,0));
    
            //写入内容
            g.drawString(code,10,20);
    
            //将jvm中的图片写到指定位置 -- 该图片是动态,现生成的
            //FileOutputStream , 将图片写入本地
            //resp.getOutputStream 将图片写给浏览器
            ImageIO.write(image,"jpg",resp.getOutputStream());
    
        }
    }