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 {
@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//验证此次登录的账号和密码是否正确//要想验证,需要先获得账号密码//如何获得账号密码//根据请求时传递的参数key,获得对应的参数值。// 这个key就是网页中input.name值,获得的参数值就是网页中input输入的值String uname = req.getParameter("uname");String upass = req.getParameter("upass");String autoflag = req.getParameter("autoflag");//service查找UserService service = new UserService();User user =service.checkLogin(uname,upass);if(user == null){//根据账号密码,没有找到数据--失败,重新显示登录界面resp.sendRedirect("login.view?f=9");}else {//自动登录处理if(autoflag != null && !"".equals(autoflag)){//要实现自动登录// 生成一个uuid的随机数String tokenId = UUID.randomUUID().toString();// 将该令牌存到cookie里Cookie c = new Cookie("tokenId",tokenId);// cookie在浏览器的存活时间,以秒为单位c.setMaxAge(60);// 将cookie响应到浏览器resp.addCookie(c);//将信息存起来,产生一个Token对象,Token对象需存在服务器端// 将令牌和登录成功的对象存到里面Token token = new Token(tokenId,user,//可以获得局域网ip,广域网使用代理后还要其他方式获得req.getRemoteAddr(),System.currentTimeMillis(),//当前的系统时间System.currentTimeMillis()+1000L*60// 七天:1000L*60*60*24*7);//token在服务端如何存储// request不行,session不行// 数据库浪费空间或影响效率//application(服务器运行期缓存),redis缓存中√// 在服务器运行时间当中,都是可以的//tokenid在浏览器端一段时间后就清除了//一旦tokenid在客户端清除,服务端对应的那个token对象也就没有意义了。//但服务器不知道tokenid不知,如果一直保留,会产生内存垃圾,假设足够多,会出现内存泄露//如果使用redis缓存数据库存储,在存储时可以设置存活时间//如果存在服务器中,可以自定义线程,一段时间扫描所有过期tokenreq.getServletContext().setAttribute(tokenId,token);}//登录成功。显示主界面(重定向--间接响应)// 将登录成功信息存在session里// 这个session是浏览器和服务器交互的缓存,存储的信息不一样req.getSession().setAttribute("loginUser",user);resp.sendRedirect("main.html");//直接响应//resp.getWriter().write("main.html");}}
}
* 扩展:<br /> * 自定义多线程,每隔一段时间扫描服务器,清除过期的token<br /> * 使用自定义监听器/过滤器,在服务器启动时开启扫描线程<br /> <br />监听器启动的最快```java//ServerStartListener.javapackage com.duyi.qxgl.util;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;//一个可以监听服务器启动和销毁的监听器public class ServerStartListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {//服务器启动执行new TokenScanThread( sce.getServletContext() ).start();}}
补充:
servletContext接口是Servlet中最大的一个接口,呈现了web应用的Servlet视图。ServletContext实例是通过 getServletContext()方法获得的,由于HttpServlet继承GenericServlet的关系,GenericServlet类和HttpServlet类同时具有该方法。
//TokenScanThread.javapackage com.duyi.qxgl.util;import com.duyi.qxgl.domain.Token;import javax.servlet.ServletContext;import java.util.Enumeration;//扫描服务器过期token的线程 (可以并发执行)// is - a Thread// 接口可以多实现,但是java只能单继承(使用线程池)public class TokenScanThread extends Thread implements Runnable{private ServletContext application;// 服务器TokenScanThread(ServletContext application) {this.application = application;}@Overridepublic void run() {//每隔一段时间循环int second = 0;while (true){try {// Thread.sleep(1000);//1秒// second += 1000;Thread.sleep(30000);if(second >= 30000){//每隔30s,扫描服务器(需要application对象)//枚举集合(类似于迭代器) 获得application中的属性名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)画出一个验证码的图片
将图片的中的验证码保存起来
* 将图片响应给浏览器
* 在登录页面中增加验证码输入<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());
}
}
