接上一个文档,我们附加一个定期清除失效token功能
    自定义多线程,每隔一段时间扫描服务器,清楚过期的token,使用自定义监听器/过滤器,在服务器启动时开启扫描线程

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <welcome-file-list>
    7. <welcome-file>login.jsp</welcome-file>
    8. </welcome-file-list>
    9. <listener>
    10. <listener-class>util.ServerStartListener</listener-class>
    11. </listener>
    12. <filter>
    13. <filter-name>AutoLoginFilter</filter-name>
    14. <filter-class>util.AutoLoginFilter</filter-class>
    15. </filter>
    16. <filter-mapping>
    17. <filter-name>AutoLoginFilter</filter-name>
    18. <url-pattern>/*</url-pattern>
    19. </filter-mapping>
    20. <filter>
    21. <filter-name>LoginFilter</filter-name>
    22. <filter-class>util.LoginFilter</filter-class>
    23. </filter>
    24. <filter-mapping>
    25. <filter-name>LoginFilter</filter-name>
    26. <url-pattern>/*</url-pattern>
    27. </filter-mapping>
    28. </web-app>
    package util;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    
    public class ServerStartListener implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent sce){
            new TokenScanThread(sce.getServletContext()).start();
        }
    }
    
    package util;
    
    import domain.Token;
    
    import javax.servlet.ServletContext;
    import java.util.Enumeration;
    
    //扫描服务器过期cookie的线程
    public class TokenScanThread extends Thread implements Runnable {
        private ServletContext application;
        public TokenScanThread(ServletContext application){
            this.application=application;
        }
        @Override
        public void run(){
            int second=0;
            while (true){
                try {
                    Thread.sleep(1000);
                    second+=1000;
                    if(second>=20000){//每隔20秒扫描服务器
                        Enumeration<String> enums= application.getAttributeNames();
                        while (enums.hasMoreElements()){
                            String name=enums.nextElement();
                            Object value=application.getAttribute(name);
                            if(value instanceof Token){
                                //这是一个令牌对象
                                Token token=(Token) value;
                                if(token.getEnd()<System.currentTimeMillis()){//已经过期了
                                    application.removeAttribute(name);
                                }
                            }
                        }
                        second=0;
                        continue;
                    }
                }catch (InterruptedException  e){
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    添加验证码功能
    在服务端可以使用Java技术(GUI)画出一个验证码的图片
    将图片中的验证码保存起来,再将图片响应给浏览器
    注意:**验证码请求也需要排除在登录认证之外**

    package controller;
    
    import javax.imageio.ImageIO;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    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;
    
    @WebServlet("/checkcode")
    public class CheckCodeController extends HttpServlet {
    
        private static final String source="0123456789ABCDEFG";//验证码来源
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String code="";
            Random random=new Random();
            for(int i=0;i<4;i++){//产生4位随机验证码
                code+=source.charAt(random.nextInt(source.length()));
            }
            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());
    
        }
    }
    
    package controller;
    
    import domain.Token;
    import domain.User;
    import service.UserService;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.*;
    import java.io.IOException;
    import java.util.UUID;
    
    @WebServlet("/login")
    public class LoginController extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("UTF-8");
            String uname = req.getParameter("uname");
            String upass = req.getParameter("upass");
            String autoflag=req.getParameter("autoflag");
            String code=req.getParameter("code");
            String codeCheck="true";
            if(code==null||"".equals(code)){
                //验证码为空
                codeCheck="false";
            }
            String checkcode=(String) req.getSession().getAttribute("code");
            if(checkcode.equals(code)==false){
                codeCheck="false";
            }
            if("false".equals(codeCheck)){
                req.setAttribute("result","验证码错误!");
                req.getRequestDispatcher("login.jsp").forward(req,resp);
                return;
            }
            UserService userService = new UserService();
            User user = userService.checkLogin(uname, upass);
            resp.setCharacterEncoding("UTF-8");
            if (user==null&&"true".equals(codeCheck)) {
                req.setAttribute("result","用户名或密码错误!");
                req.getRequestDispatcher("login.jsp").forward(req,resp);
            }else{
                //自动登录实现
                if(autoflag!=null&&!"".equals(autoflag)){
                    String tokenId= UUID.randomUUID().toString();
                    Cookie c=new Cookie("tokenId",tokenId);
                    c.setMaxAge(60);//cookie在浏览器的存活时间,秒为单位,时间应与后面一致
                    resp.addCookie(c);//传递令牌
    
                    Token token=new Token(tokenId,user,req.getRemoteAddr(),
                            System.currentTimeMillis(),
                            System.currentTimeMillis()+1000L*20);//七天就是加上1000L*60*60*24*7
                    //req.getRemoteAddr();可以获得局域网ip,广域网使用代理后还要其他方式获得
    
                    //token在服务器中如何存储?
                    //request和session都不行,数据库则有些浪费空间影响效率
                    //application(服务器运行期缓存),redis缓存中
                    req.getServletContext().setAttribute(tokenId,token);
    
    
                }
    
                HttpSession session= req.getSession();
                session.setAttribute("loginUser",user);
                req.getRequestDispatcher("main.jsp").forward(req,resp);
            }
        }
    }
    

    LoginController修改部分

    String code=req.getParameter("code");
            String codeCheck="true";
            if(code==null||"".equals(code)){
                //验证码为空
                codeCheck="false";
            }
            String checkcode=(String) req.getSession().getAttribute("code");
            if(checkcode.equals(code)==false){
                codeCheck="false";
            }
            if("false".equals(codeCheck)){
                req.setAttribute("result","验证码错误!");
                req.getRequestDispatcher("login.jsp").forward(req,resp);
                return;
            }
            UserService userService = new UserService();
            User user = userService.checkLogin(uname, upass);
            resp.setCharacterEncoding("UTF-8");
            if (user==null&&"true".equals(codeCheck)) {
                req.setAttribute("result","用户名或密码错误!");
                req.getRequestDispatcher("login.jsp").forward(req,resp);
            }
    

    login.jsp修改部分

    <c:if test="${requestScope.result!=null}">
        <script>
          alert("${requestScope.result}");
        </script>
      </c:if>