验证表单重复提交(防止钓鱼,密码加密,自定义标签,过滤器)

这是本工程所有包与类。

1.创建好登录、注册页面。

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.chinasofti.com/token" prefix="csi" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'regist.jsp' starting page</title>
    
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
    <form action="regist" method="post">
        此处用到自定义标签
        <csi:token/>
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="注册">
    </form>
  </body>
</html>
    
<body>
    <form action="login" method="post">
        用户名:<input type="text" name="username" value="${user }"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
  </body>

这里是登录界面,主体内容

2.创建登录、注册的servlet类

package com.chinasofti.um.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.chinasofti.common.service.DomainProtectedService;
import com.chinasofti.util.sec.Passport;

public class LoginServlet extends HttpServlet {

    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        doPost(request, response);
    }

    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();

        Passport passport = new Passport();
        

        DomainProtectedService dps = new DomainProtectedService();
        if (dps.isFromSameDomain()) {
            String username = request.getParameter("username");
            String password = passport.md5(request.getParameter("password"));
            
            HttpSession session = request.getSession();
            session.setAttribute("user", username);
            
            try {
                Class.forName("com.mysql.jdbc.Driver");
                Connection conn = DriverManager.getConnection(
                        "jdbc:mysql://localhost:3306/ordersys", "root", "1");

                PreparedStatement pstmt = conn
                        .prepareStatement("select * from user_info where username=?");
                pstmt.setString(1, username);
                ResultSet rs = pstmt.executeQuery();
                if (rs.next()) {
                    String passdb = rs.getString(3);
                    if (passdb.equals(password)) {
                        out.println("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">");
                        out.println("<HTML>");
                        out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
                        out.println("  <BODY>");
                        out.println("登录成功!");
                        out.println("  </BODY>");
                        out.println("</HTML>");
                    } else {
                        out.println("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">");
                        out.println("<HTML>");
                        out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
                        out.println("  <BODY>");
                        out.println("密码错误!");
                        out.println("  </BODY>");
                        out.println("</HTML>");
                    }
                } else {
                    out.println("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">");
                    out.println("<HTML>");
                    out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
                    out.println("  <BODY>");
                    out.println("用户名错误!");
                    out.println("  </BODY>");
                    out.println("</HTML>");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else{
            out.println("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">");
            out.println("<HTML>");
            out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
            out.println("  <BODY>");
            out.println("非法登录!");
            out.println("  </BODY>");
            out.println("</HTML>");
        }

        out.flush();
        out.close();
    }

}
package com.chinasofti.um.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.chinasofti.um.common.taglib.TokenTag;
import com.chinasofti.util.sec.Passport;

public class RegistServlet extends HttpServlet {

    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        doPost(request, response);
    }

    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        Passport passport = new Passport();
        
        //判断令牌是否有效
        if(TokenTag.isTokenValid()){
            String username = request.getParameter("username");
            //String password = request.getParameter("password");
            String password = passport.md5(request.getParameter("password"));
            //通过UUID来生产唯一的用户ID,作为主键
            String userID = UUID.randomUUID().toString();
            
            try {
                Class.forName("com.mysql.jdbc.Driver");
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ordersys", "root", "1");
                
                PreparedStatement pstmt = conn.prepareStatement("insert into user_info values(?,?,?)");
                pstmt.setString(1, userID);
                pstmt.setString(2, username);
                pstmt.setString(3, password);
                
                pstmt.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
            } 
            
            //释放令牌
            TokenTag.releaseToken();
            out.println("注册成功!<br>");
        }else{
            out.println("令牌无效,非法注册!<br>");
        }
    }

}

3.创建防止表单重复提交的标签

package com.chinasofti.um.common.taglib;

import java.io.IOException;
import java.util.UUID;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import com.chinasofti.um.common.httprequest.HttpRequestContext;
/**
 * <p>
 * Title:TokenTag
 * </p>
 * <p>
 * Decription:防止表单重复提交的标签
 * </p>
 * @author csi
 * @version 1.0
 *
 */
public class TokenTag extends TagSupport {

    /**
     * 令牌属性:在session中的属性名
     */
    public static final String TOKEN_SESSION_ATTR_NAME = "SUBMIT_TOKEN_ATTR_NAME_SESSION";
    /**
     * 令牌属性:在request中的属性名
     */
    public static final String TOKEN_REQUEST_ATTR_NAME = "SUBMIT_TOKEN_ATTR_NAME_REQUEST";
    
    /**
     * 判断当前请求中是否包含合法令牌值的方法
     * 
     * @return true-请求中包含合法令牌值,false-请求中不包含合法令牌值
     */
    public static boolean isTokenValid(){
        //获取请求中的令牌值
        String requestToken = HttpRequestContext.getRequest().
                getParameter(TOKEN_REQUEST_ATTR_NAME);
        //获取会话中的令牌值
        Object sessionToken = HttpRequestContext.getRequest().getSession().
                getAttribute(TOKEN_SESSION_ATTR_NAME);
    System.out.println(requestToken + "--->" + sessionToken);
        //判断令牌是否合法
        return sessionToken != null && sessionToken.toString().equals(requestToken);
    }
    
    /**
     * 释放会话中令牌值
     */
    public static void releaseToken(){
        HttpRequestContext.getRequest().getSession().
                setAttribute(TOKEN_SESSION_ATTR_NAME, "");
    }
    
    @Override
    public int doEndTag() throws JspException {
        
        //利用UUID获取唯一的令牌值
        String token = UUID.randomUUID().toString();
        //在会话session中保存令牌值
        pageContext.getSession().setAttribute(TOKEN_SESSION_ATTR_NAME, token);
        //创建表单令牌的HTML字符串
        String tokenTag = "<input type="hidden" name="SUBMIT_TOKEN_ATTR_NAME_REQUEST" value="" + 
                                        token + ""/>";
        try {
            //在页面中输出令牌域字符串
            pageContext.getOut().print(tokenTag);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //本标签结束后继续执行页面的其他内容
        return EVAL_PAGE;
    }

    @Override
    public int doStartTag() throws JspException {
        //跳过标签体
        return SKIP_BODY;
    }

    
}

4.创建过滤器(在处理每次请求前捕获请求,响应对象的过滤器)

package com.chinasofti.um.common.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.chinasofti.um.common.httprequest.HttpRequestContext;
/**
 * 在处理每次请求前捕获请求,响应对象的过滤器
 * @author csi
 * @version 1.0
 *
 */
public class HttpRequestContextFilter implements Filter {

    private ServletContext context;
    
    @Override
    public void destroy() {
        

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        //在ThreadLocal中共享本次请求、响应对象
        HttpRequestContext.setHttpRequestContext((HttpServletRequest)request, 
                (HttpServletResponse)response, context);
        
        chain.doFilter(request, response);
    }

    @Override
    /**
     * 过滤器初始化是执行的回调方法
     * @param config
     *             过滤器配置对象,可以读取配置文件中的特殊的配置信息
     */
    public void init(FilterConfig config) throws ServletException {
        //使用过滤器配置对象获取ServletContext对象
        context = config.getServletContext();    

    }

}

5.创建  请求,响应,ServletContext对象的包装对象

package com.chinasofti.um.common.httprequest;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 请求,响应,ServletContext对象的包装对象
 * @author csi
 * @version 1.0
 *
 */
public class HttpRequestContext {

    /**
     * 请求对象
     */
    private HttpServletRequest request;
    /**
     * 响应对象
     */
    private HttpServletResponse response;
    /**
     * ServletContext对象
     */
    private ServletContext servletContext;
    /**
     * 构造器,构造包装对象
     * @param request
     *             请求对象
     * @param response
     *             响应对象
     * @param servletContext
     *             Servlet上下文对象
     */
    public HttpRequestContext(HttpServletRequest request,HttpServletResponse response,
            ServletContext servletContext){
        //初始化request对象
        this.request = request;
        //初始化response对象
        this.response = response;
        //初始化ServletContext对象
        this.servletContext = servletContext;
    }
    
    /**
     * ThreadLocal对象,用于在单一线程中共享数据
     */
    private static ThreadLocal<HttpRequestContext> currentContext = new ThreadLocal<HttpRequestContext>();
    
    /**
     * 设置共享数据的方法
     * @param request  请求对象
     * @param response 响应对象
     * @param servletContext ServletContext对象
     */
    public static void setHttpRequestContext(HttpServletRequest request,HttpServletResponse response,
            ServletContext servletContext){
        //构造包装对象
        HttpRequestContext context = new HttpRequestContext(request, response, servletContext);
        //在ThreadLocal中存放包装对象
        currentContext.set(context);
    }

    /**
     * 获取请求对象
     * @return 请求对象
     */
    public static HttpServletRequest getRequest() {
        return currentContext.get() == null ? null 
                : currentContext.get().request; 
    
    }

    /**
     * 获取响应对象
     * @return 响应对象
     */
    public static HttpServletResponse getResponse() {
        return currentContext.get() == null ? null 
                : currentContext.get().response; 
    }

    /**
     * 获取ServletContext对象
     * @return ServletContext对象
     */
    public static ServletContext getServletContext() {
        return currentContext.get() == null ? null 
                : currentContext.get().servletContext; 
    }
}

6.创建 防止盗链、防止外站提交数据的服务对象

/**
 *  Copyright 2015 ChinaSoft International Ltd. All rights reserved.
 */
package com.chinasofti.common.service;

import javax.servlet.http.HttpServletRequest;

import com.chinasofti.um.common.httprequest.HttpRequestContext;

/**
 * <p>
 * Title: DomainProtectedService
 * </p>
 * <p>
 * Description: 防止盗链、防止外站提交数据的服务对象
 * </p>
 * <p>
 * Copyright: Copyright (c) 2015
 * </p>
 * <p>
 * Company: ChinaSoft International Ltd.
 * </p>
 * 
 * @author etc
 * @version 1.0
 */
public class DomainProtectedService {

    /**
     * 判定是否盗链或是否外站提交数据的方法
     * 
     * @return 判定结果,ture表示本站合法请求,false表示外站请求
     * */
    public boolean isFromSameDomain() {
        // 获取本次的请求对象
        HttpServletRequest request = HttpRequestContext.getRequest();
        // 获取本站的context root
        String path = request.getContextPath();
        // 获取本站截至到context root的域名信息
        String basePath = request.getScheme() + "://" + request.getServerName()
                + ":" + request.getServerPort() + path + "/";
        // 获取上一个页面的地址
        String fromUrl = request.getHeader("referer");

        System.out.println(fromUrl + "--->" + basePath);

        // 判定是否外站请求并返回结果
        return fromUrl != null && fromUrl.startsWith(basePath) ? true : false;

    }

}

7.创建一个算法对密码进行加密

包含几种加密方法
package com.chinasofti.util.sec;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * <p>
 * Title: Passport
 * </p>
 * <p>
 * Description:自定义的可逆加密算法,主要用于非超大容量的字符串加密,可以生成网络传输数据的令牌,不同时间执行加密算法获取到的加密结果不同
 * </p>
 * <p>
 * Copyright: Copyright (c) 2015
 * </p>
 * <p>
 * Company: ChinaSoft International Ltd.
 * </p>
 * 
 * @author etc
 * @version 1.0
 */
public class Passport {

    public Passport() {
        // TODO 自动生成构造函数存根
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO 自动生成方法存根
        Passport passport = new Passport();
        // String txt = "中文文本";
        // String key = "chinasofti";
        // String jia_str = passport.passport_encrypt(txt, key);
        // String jie_str = passport.passport_decrypt(jia_str, key);
        // System.out.println("加密函数测试:" + jia_str);
        // System.out.println("解密函数测试:" + jie_str);

        System.out.println(passport.md5("admin"));

    }

    /**
     * Md5加密
     * 
     * @param x
     *            需要加密的字符串
     * @return md5加密结果
     * @throws Exception
     */
    public String md5(String x) {
        // 获取摘要工具
        MessageDigest m = null;
        try {
            // MD5摘要工具
            m = MessageDigest.getInstance("MD5");
            // 更新被文搞描述的位元组
            m.update(x.getBytes("UTF8"));
            // 捕获不支持摘要异常
        } catch (NoSuchAlgorithmException e) {
            // 创建一个MD5消息文搞 的时候出错
            e.printStackTrace();
            // 捕获不支持字符集异常
        } catch (UnsupportedEncodingException e) {
            // 更新被文搞描述的位元组 的时候出错
            e.printStackTrace();
        }
        // 最后更新使用位元组的被叙述的排列,然后完成文摘计算
        byte s[] = m.digest();
        // System.out.println(s); // 输出加密后的位元组
        // 创建结果字符串缓冲
        StringBuilder result = new StringBuilder("");
        // 遍历文摘
        for (int i = 0; i < s.length; i++) {
            // 进行十六进制转换
            result.append(Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00)
                    .substring(6));
        }
        // 返回加密结果
        return result.toString();

    }

    /**
     * 本方法将字符串以 MIME BASE64 编码。此编码方式可以让中文字或者图片也能在网络上顺利传输。在 BASE64
     * 编码后的字符串只包含英文字母大小写、阿拉伯数字、加号与反斜线,共 64 个基本字符,不包含其它特殊的字符, 因而才取名
     * BASE64。编码后的字符串比原来的字符串长度再加 1/3 左右。更多的 BASE64 编码信息可以参考 RFC2045 文件之 6.8 节
     * 
     * @param txt
     *            等待编码的原字串
     * @return 编码后的结果
     */
    public String base64_decode(String txt) {
        // 定义编码器
        BASE64Decoder base64_decode = new BASE64Decoder();
        // 定义结果字符串
        String str = "";
        try {
            // 获取加密结果
            str = new String(base64_decode.decodeBuffer(txt));
        } catch (IOException e) {
            // 如果有异常则输出异常信息
            e.printStackTrace();
        }
        // 返回编码结果
        return str;
    }

    /**
     * Base64编码的方法
     * 
     * @param txt
     *            要编码的字符串
     * @return 编码结果
     * */
    public String base64_encode(String txt) {
        // 创建编码器
        BASE64Encoder base64_encode = new BASE64Encoder();
        // 返回编码结果
        return base64_encode.encode(txt.getBytes());
    }

    /**
     * Passport 加密方法
     * 
     * @param string
     *            等待加密的原字串
     * @param string
     *            私有密匙(用于解密和加密)
     * 
     * @return string 原字串经过私有密匙加密后的结果
     */
    public String passport_encrypt(String txt, String key) {
        // 创建随机工具
        Random random = new Random();
        // 使用随机数发生器产生 0~32000 的值
        String rad = String.valueOf(random.nextInt(32000));
        // 获取随机值的md5码
        String encrypt_key = md5(rad);

        // 变量初始化
        int ctr = 0;
        // 定义结果字符串缓冲
        StringBuilder tmp = new StringBuilder("");

        // 获取md5码的字符数组形式
        char encrypt_key_char[] = encrypt_key.toCharArray();
        // 获取初始文本的字符数组形式
        char txt_char[] = txt.toCharArray();
        // for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
        for (int i = 0; i < txt.length(); i++) {
            // 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零
            ctr = ctr == encrypt_key_char.length ? 0 : ctr;
            // $tmp 字串在末尾增加两位,其第一位内容为 $encrypt_key 的第 $ctr 位,
            // 第二位内容为 $txt 的第 $i 位与 $encrypt_key 的 $ctr 位取异或。然后 $ctr = $ctr + 1
            char tmp1 = txt_char[i];
            // 编码字符
            char tmp4 = encrypt_key_char[ctr];
            // 编码第二个字符
            char tmp2 = encrypt_key_char[ctr++];
            // 进行位运算
            char tmp3 = (char) (tmp1 ^ tmp2);
            // 添加结果数据
            tmp.append(tmp4 + "" + tmp3);
        }
        // 返回结果,结果为 passport_key() 函数返回值的 base65 编码结果
        return base64_encode(passport_key(tmp.toString(), key));

    }

    /**
     * Passport 解密方法
     * 
     * @param string
     *            加密后的字串
     * @param string
     *            私有密匙(用于解密和加密)
     * 
     * @return string 字串经过私有密匙解密后的结果
     */
    public String passport_decrypt(String txt, String key) {

        // $txt 的结果为加密后的字串经过 base64 解码,然后与私有密匙一起,
        // 经过 passport_key() 函数处理后的返回值
        txt = passport_key(base64_decode(txt), key);
        // 变量初始化
        StringBuilder tmp = new StringBuilder("");
        // 获取字符串数组形式
        char txt_char[] = txt.toCharArray();
        // for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
        for (int i = 0; i < txt.length(); i++) {
            // $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,
            // 与 $txt 的第 $i + 1 位取异或。然后 $i = $i + 1
            tmp.append((char) (txt_char[i] ^ txt_char[++i]));
        }

        // 返回 $tmp 的值作为结果
        return tmp.toString();

    }

    /**
     * Passport 密匙处理方法
     * 
     * @param string
     *            待加密或待解密的字串
     * @param string
     *            私有密匙(用于解密和加密)
     * 
     * @return string 处理后的密匙
     */
    String passport_key(String txt, String encrypt_key) {

        // 将 $encrypt_key 赋为 $encrypt_key 经 md5() 后的值
        encrypt_key = md5(encrypt_key);
        // 变量初始化
        int ctr = 0;
        // 创建结果字符串缓冲
        StringBuilder tmp = new StringBuilder("");

        // 获取md5码字符数组形式
        char encrypt_key_char[] = encrypt_key.toCharArray();
        // 获取原文本字符数组表现形式
        char txt_char[] = txt.toCharArray();
        // for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
        for (int i = 0; i < txt.length(); i++) {
            // 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零
            ctr = ctr == encrypt_key.length() ? 0 : ctr;
            // $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,
            // 与 $encrypt_key 的第 $ctr + 1 位取异或。然后 $ctr = $ctr + 1
            char c = (char) (txt_char[i] ^ encrypt_key_char[ctr++]);
            // 追加结果
            tmp.append(c);
        }

        // 返回 $tmp 的值作为结果
        return tmp.toString();

    }

}

8.配置xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name>
  
  <filter>
      <filter-name>httpRequestContext</filter-name>
      <filter-class>com.chinasofti.um.common.filter.HttpRequestContextFilter</filter-class>
  </filter>
  
  <filter-mapping>
      <filter-name>httpRequestContext</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <servlet>
    <servlet-name>RegistServlet</servlet-name>
    <servlet-class>com.chinasofti.um.servlet.RegistServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.chinasofti.um.servlet.LoginServlet</servlet-class>
  </servlet>


  <servlet-mapping>
    <servlet-name>RegistServlet</servlet-name>
    <url-pattern>/regist</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

9.配置自定义标签的.tld文件

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
  <description>csi 1.0 token library</description>
  <display-name>token core</display-name>
  <tlib-version>1.2</tlib-version>
  <short-name>csi</short-name>
  <uri>http://www.chinasofti.com/token</uri>
  
  <tag>
      <name>token</name>
      <tag-class>com.chinasofti.um.common.taglib.TokenTag</tag-class>
      <body-content>empty</body-content>
  </tag>
</taglib>

10.此处用到了数据库,我们将数据库的jar包放到lib文件下,配置好这几个文件,就ok了,

验证表单重复提交(防止钓鱼,密码加密,自定义标签,过滤器)等,

原文地址:https://www.cnblogs.com/lixiaopan/p/6230353.html