接上一个文档,我们附加一个定期清除失效token功能
自定义多线程,每隔一段时间扫描服务器,清楚过期的token,使用自定义监听器/过滤器,在服务器启动时开启扫描线程
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>util.ServerStartListener</listener-class>
</listener>
<filter>
<filter-name>AutoLoginFilter</filter-name>
<filter-class>util.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>util.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</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>