关于BBS的一些功能性补充以及踩坑记录(持续补充)

JavaJspSsh 2019-06-26

声明:以下记录了本人实验性地探索过程,不代表正确,请谨慎食用。
也欢迎提出各种批评建议,帮助我改正错误。谢谢!

1.注册

注册时在注册的jsp页面使用js函数进行合法性验证(包括空值、两次输入密码是否相同等),并设置为onclick或onsubmit触发。
具体触发顺序如下
1) onclick: Y();

2) onsubmit: X();

3) submit();

第一种:onsubmit

在表单submit之前会调用onsubmit(),注意调用时为onsubmit="return CheckPost();"如果直接写"CheckPost()"则无法生效。如果返回false则不会调用submit,返回true才会执行到submit。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
 <script language="javascript" type="text/javascript"> 
    function check()
    {
    if (document.sign.username.value==""){
        alert("请输入登录账号!");
        return false;
    }
    if (document.sign.password1.value==""){
        alert("请输入登录密码!");
        return false;
    }
    if (document.sign.password2.value==""){
        alert("请输入重复密码!");
        return false;
    }
    var pd1=document.sign.password1.value;
    var pd2=document.sign.password2.value;
    if (pd1!=pd2){
        alert("对不起!重复密码不等于登录密码");
        return false;
    }
    return true;
    }
</script>  
<title>注册</title>
</head>
<body>

    <form action="Signup" method="post" name ="sign" onSubmit="return check()">
    <input type="hidden" name="action" value="signup"/>
        用户名:<input type="text" name="username" />
        密码:<input type="password" name="password1" />
        确认密码:<input type="password" name="password2" />
         <input type="submit" value="注册"  />
    </form>

</body>
</html>

第二种:onclick

点击提交按钮时触发,此时需要在函数中手动submit提交。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
 <script language="javascript" type="text/javascript"> 
    function check()
    {
    if (document.sign.username.value==""){
        alert("请输入登录账号!");
        return false;
    }
    if (document.sign.password1.value==""){
        alert("请输入登录密码!");
        return false;
    }
    if (document.sign.password2.value==""){
        alert("请输入重复密码!");
        return false;
    }
    var pd1=document.sign.password1.value;
    var pd2=document.sign.password2.value;
    if (pd1!=pd2){
        alert("对不起!重复密码不等于登录密码");
        return false;
    }
    document.sign.submit();
    return true;
    }
</script>  
<title>注册</title>
</head>
<body>

    <form action="Signup" method="post" name ="sign" >
    <input type="hidden" name="action" value="signup"/>
        用户名:<input type="text" name="username" />
        密码:<input type="password" name="password1" />
        确认密码:<input type="password" name="password2" />
         <input type="button" value="注册" onClick="check()" />
    </form>
</body>
</html>

2.加密

1.前端到后台的安全性

在用户登录以及注册时,有必要对数据进行加密。任何语言最终都会形成html,事实上前端也只能处理html,css,js代码,其他如java,php,c#都是在后端工作的,在html通过web服务器发送给访问者的时候已经脱离了后端的控制。因此前端的唯一加密手段就是js,但是js是明文的,也就是说你的加密过程是透明的,自然完全没有破解难度。当然,md5之类的单向加密依然无法破解,问题是后端拿到单向加密的数据完全没用,因为它推导不出原始数据是什么。所以,要安全就用https。
设置Tomcat的HTTPS配置方法如下:
① keytool工具生成证书
打开 JDK 自带的 keytool 目录。

关于BBS的一些功能性补充以及踩坑记录(持续补充)

按住 Shift 键,同时右键点击空白处。

关于BBS的一些功能性补充以及踩坑记录(持续补充)

此时,进入cmd窗口。输入下面命令。

keytool -genkeypair -alias "tomcat" -keyalg "RSA" -keystore "F:\tomcat.keystore"

接着会让你填写一些基本信息。

关于BBS的一些功能性补充以及踩坑记录(持续补充)

下面简要介绍一下。

密钥库口令:123456(这个密码非常重要)
名字与姓氏:localhost(以后访问的域名或IP地址,非常重要,证书和域名或IP绑定)
组织单位名称:anything(随便填)
组织名称:anything(随便填)
城市:anything(随便填)
省市自治区:anything(随便填)
国家地区代码:anything(随便填)

② 应用证书到Tomcat
打开 Tomcat 配置文件 confserver.xml。
取消注释,并添加两个属性 keystoreFile,keystorePass。

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" keystoreFile="F:/tomcat.keystore" keystorePass="123456" />

2.后台数据的加密

后台获得数据(用户名和密码)后,需要对密码数据进行加密。由于username在mysql中被设置成了unique的,所以并不适合加密存入。这里只对密码进行加密。
加密方法为:
加密工具类:

import java.security.MessageDigest;
public class MD5Util {
    public final static String MD5(String s) {
        char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};       
        try {
            byte[] btInput = s.getBytes();
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

加密过程:

String salt =  MD5Util.MD5(username);
if(username.length()%2==0)
    password = password+salt;
else password = salt+password;
password = MD5Util.MD5(password);

通过unique的username通过MD5加密作为每个user独有的salt,再将password与salt连接后再MD5加密。MD5算法hash碰撞的可能性很小,因此基本可以保证salt和password加密后都是独一无二的,防止黑客用彩虹表爆表。
不建议将salt与用户信息存在一起,防止数据库被黑后黑客可以轻易破解用户密码。

存储结果如下:

关于BBS的一些功能性补充以及踩坑记录(持续补充)

可以看到密码已经被加密。

3.防止SQL注入

1、检查变量数据类型和格式

如果你的SQL语句是类似where id={$id}这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。

2、绑定变量,使用预编译语句

使用PreparedStatement代替Statement是最有效也是最简单的防止SQL注入的方式。

3、用户数据加密

正如上面所说,对用户数据进行加密是一个必要的手段。虽说不能完全防止SQL注入攻击,但是能增大黑客暴力破解的难度。

总之

1、不要随意开启生产环境中Webserver的错误显示(避免1=1 1=2刺探漏洞以及爆字段)
2、永远不要信任来自用户端的变量输入,有固定格式的变量一定要严格检查对应的格式,没有固定格式的变量需要对引号等特殊字符进行必要的过滤转义。
3、使用预编译绑定变量的SQL语句。
4、做好数据库帐号权限管理。
5、严格加密处理用户的机密信息。

4.数据库表的结构选择(存疑)

原来是用户表用MyISAM 帖子表用InnoDB提高并发性。

1、用户信息

我们将用户id、用户名、密码存在一张表上,同时要确保id和用户名是唯一的,这里我将id作为主键索引,用户名作为唯一索引。选择MyISAM存储引擎,有以下原因:

  1. 对MyISAM表的读操作(登录、注册前验证),不会阻塞其他用户对同一表的读请求(其他用户依然可以登录),但会阻塞对同一表的写(注册)请求,对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作(注册完成前,其他用户不能进行注册验证,否则会重复注册);
  2. 虽然MyISAM写的优先度比较高,但是注册的频率远远小于登录和验证的频率,所以不会出现用户读操作的饥饿状态。
  3. 实现简单

理论上也可以把用户名作为主键。但不推荐,理由大概有:

  1. 用户名可以是千奇百怪的字符串,而用ID(一般用guid或自增int)是比较规则字母数字序列,这可能导致性能上有所差别
  2. 假如以用户名作为主键并与其他表关联,当删除用户时,再创建一个同名的用户,可能导致这些关联紊乱。而绝对唯一的ID则不会。
  3. 后期可能会允许修改用户名,如果以用户名为主键,将带来很多麻烦。而如果以ID为主键进行关联,则没有此问题。

2、帖子信息

主题帖包括以下字段:
id(主键索引) title(标题) cont(内容) pdata(发帖时间) user(作者)isleaf
回复贴包括以下字段:
id pid(父贴) rootid(主题帖id,建立索引) title cont(内容) pdata(发帖时间) user(作者)isleaf

主题帖存储引擎使用MyISAM,有以下原因:

  1. 只需要在主页上显示,读多写少,对并发要求低。
  2. 实现简单,非聚簇索引占用空间小

回复帖存储引擎使用InnoDB,有以下原因:

  1. 所有主题帖的回复都存储在这一张表上,对并发要求高,尽管聚簇索引会占用大量空间,但是为了并发性能必须有所取舍。
  2. InnoDB会在insert delete update语句默认加排它锁,但是select不加锁。此时也不需要手动给select加共享锁或排它锁。
  3. 不能用MyISAM的另一个原因是,MyISAM的写会阻塞所有的读操作,不适用于大量写的情况,容易降低性能

5.细节问题

1、在JS代码中调用jsp的变量

<script type="text/javascript">
    var url = "<%=url%>";
    delayURL(url);
</script>

2、设置操作成功后N秒后跳转

<span id="time" style="background:red">3</span>秒钟后跳转,或者点击下面跳转。

<script language="javascript" type="text/javascript">
function delayURL(url){//每隔一秒递归调用一次该函数,刷新秒数,直到秒数小于0跳转
    var delay = document.getElementById("time").innerHTML;//获得目前秒数
    if (delay>0){
        delay--;
        document.getElementById("time").innerHTML = delay
    }else{
        window.top.location.href= url;
    }
    setTimeout("delayURL('"+url+"')",1000);//每隔一秒,调用一次delayURL
}
</script>

相关推荐