下面是登录时用户名密码检查页面的代码:
loginCheck.jsp
<%@ page contentType="text/html;charset=GB2312" %>
<%@ page import="java.sql.*,java.security.MessageDigest" %>
<%!
public String codeToString(String str)
{//处理中文字符串的函数
String s=str;
try{
byte tempB[]=s.getBytes("ISO-8859-1");
s=new String(tempB);
return s;
}catch(Exception e){
return s;
}
}
%>
<%//接收客户端提交的数据
String username=codeToString(request.getParameter("username"));
if(username==null)//无内容则设为空串
username="";
String password=codeToString(request.getParameter("password"));
if(password==null)//无内容则设为空串
password="";
//为密码计算摘要
try{
//------生成MessageDigest对象MD------
MessageDigestMD=MessageDigest.getInstance("MD5");
//------传入要计算的字符串------
MD.update(password.getBytes("UTF8"));
//------计算消息摘要------
byte[] passwordMD5Byte=MD.digest();
password=new String(passwordMD5Byte);
}catch(Exception e){
e.printStackTrace();
}%>
<%//构造追加记录SQL语句
String sqlString=null;//SQL语句
sqlString="select * from userTable where user_name='"+username
+"' and user_password='"+password+"'";
%>
<%//执行SQL语句
ResultSet rs=null;
try {
Connection con;
Statement sql;
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); con=DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=testDatabase","sa","6599996");
sql=con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
rs=sql.executeQuery(sqlString);
}catch(SQLException e1) {
out.print("SQL异常!");
}
%>
<html>
<head><title>登录检查页面</title></head>
<body>
<%
if(rs!=null&&rs.next())
out.println("用户名和密码验证通过!");
else
out.println("用户名和密码验证没有通过!");
%>
</body>
</html>
MessageDigest类是一个工厂类,必须使用其静态方法getInstance()来生成对象,传入参数是String类型,用于指出计算消息摘要使用的算法。
MessageDigest类的update()方法用于设置要计算摘要的字符串,但其参数是一个字节数组,因此需要用getBytes()方法作转换。调用digest()方法即计算摘要,返回值是一个字节数组。
本例中使用这种方法保存的密码还是有被尝试出来的可能性,如编写一个循环程序,自动用可能的密码取值作一一尝试,一旦计算出的值与保存的摘要相同,则找到了密码。密码的位数越短,被破解的可能性越大,因为穷举的个数较少了。为方便用常用的密码作测试,可采用字典式的攻击,也就是说事先把一些常用的密码写到文件中,程序自动从中取出密码与系统数据库核对,如果成功则表示取到了密码。
为此,有人可能感觉还是不安全,这时可采用如:“用户名+密码”来计算MD5消息摘要。