声明:以下记录了本人实验性地探索过程,不代表正确,请谨慎食用。
也欢迎提出各种批评建议,帮助我改正错误。谢谢!
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 目录。
按住 Shift 键,同时右键点击空白处。
此时,进入cmd窗口。输入下面命令。
keytool -genkeypair -alias "tomcat" -keyalg "RSA" -keystore "F:\tomcat.keystore"
接着会让你填写一些基本信息。
下面简要介绍一下。
密钥库口令: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与用户信息存在一起,防止数据库被黑后黑客可以轻易破解用户密码。
存储结果如下:
可以看到密码已经被加密。
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存储引擎,有以下原因:
- 对MyISAM表的读操作(登录、注册前验证),不会阻塞其他用户对同一表的读请求(其他用户依然可以登录),但会阻塞对同一表的写(注册)请求,对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作(注册完成前,其他用户不能进行注册验证,否则会重复注册);
- 虽然MyISAM写的优先度比较高,但是注册的频率远远小于登录和验证的频率,所以不会出现用户读操作的饥饿状态。
- 实现简单
理论上也可以把用户名作为主键。但不推荐,理由大概有:
- 用户名可以是千奇百怪的字符串,而用ID(一般用guid或自增int)是比较规则字母数字序列,这可能导致性能上有所差别
- 假如以用户名作为主键并与其他表关联,当删除用户时,再创建一个同名的用户,可能导致这些关联紊乱。而绝对唯一的ID则不会。
- 后期可能会允许修改用户名,如果以用户名为主键,将带来很多麻烦。而如果以ID为主键进行关联,则没有此问题。
2、帖子信息
主题帖包括以下字段:
id(主键索引) title(标题) cont(内容) pdata(发帖时间) user(作者)isleaf
回复贴包括以下字段:
id pid(父贴) rootid(主题帖id,建立索引) title cont(内容) pdata(发帖时间) user(作者)isleaf
主题帖存储引擎使用MyISAM,有以下原因:
- 只需要在主页上显示,读多写少,对并发要求低。
- 实现简单,非聚簇索引占用空间小
回复帖存储引擎使用InnoDB,有以下原因:
- 所有主题帖的回复都存储在这一张表上,对并发要求高,尽管聚簇索引会占用大量空间,但是为了并发性能必须有所取舍。
- InnoDB会在insert delete update语句默认加排它锁,但是select不加锁。此时也不需要手动给select加共享锁或排它锁。
- 不能用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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。