安全問題實際上是不少程序員容易忽略的問題但須要咱們重視起來,提升應用程序的安全性。常出現的安全問題包括,程序接受數據可能來源於未經驗證的用戶,網絡鏈接和其餘不受信任的來源,若是未對程序接受數據進行校驗,則可能會引起安全問題等等,具體也能夠分紅如下幾方面:html
在文章安全開發規範:開發人員必須瞭解開發安全規範(一)(涉及安全問題,以及解決方法和代碼實現) 中咱們闡述了一些關於數據檢驗的安全問題,接下來咱們繼續其餘部分的安全性問題分析與解決。前端
垂直越權漏洞: 稱爲權限提高,是一種「基於URL的訪問控制」設計缺陷引發的漏洞。因爲Web應用程序沒有作權限控制或者僅在菜單上作了權限控制,致使惡意用戶只要猜想其餘管理頁面的URL,就能夠訪問或控制其餘角色擁有的數據或頁面,達到權限提高的目的。java
水平越權漏洞: 一種「基於數據的訪問控制」設計缺陷引發的漏洞。因爲服務器端在接收到請求數據進行操做時沒有判斷數據的所屬人而致使的越權數據訪問漏洞。如服務器端從客戶端提交的request參數(用戶可以控制的數據)中獲取用戶id,惡意攻擊者經過變換請求ID的值,查看或修改不屬於本人的數據。mysql
反例:程序員
@RequestMapping(value = "delete")
public String delete(HttpServletRequest request, @RequestParam long id) throws Exception{
try {
userManage.delete(id);
request.setAttribute("msg","delete user success");
}catch (Exception e){
request.setAttribute("msg","delete user failure");
}
return list(request);
}
@RequestMapping(value = "/delete/{addrId}")
public Object remove(@RequestParam long addrId) {
Map<String,Object> resMap=new HashMap<>();
if(WebUtils.isLogged){
this.addressService.removeUserAddress(addrId);
resMap.put(Constans.RESP_STATUS_CODE_KEY,Constans.RESP_STATUS_CODE_SUCCESS);
resMap.put(Constans.MESSAGE,"remove user address success");
}else {
resMap.put(Constans.RESP_STATUS_CODE_KEY,Constans.RESP_STATUS_CODE_FAIL);
resMap.put(Constans.MESSAGE,"user is not login ,remove user address failure");
}
return resMap;
}
複製代碼
正例:垂直越權漏洞:在調用功能以前,驗證當前用戶身份是否有權限調用相關功能(推薦使用過濾器,進行統一權限驗證)web
public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException ,IOException{
if(request.getSession(true).getAttribute("manager")==null){
response.sendRedirect("noright.html");
return;
}
UserManagerService userManagerService=new UserManagerService();
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
String action=request.getParameter("action");
if("add".equals(action)){
String id=request.getParameter("userId");
String name=request.getParameter("userName");
String sex=request.getParameter("userSex");
}
//todo do somethings
}
複製代碼
public class PriviegeFilter implements Filter{ @Autowired private UserManagerService userManagerService;算法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
List<UserAuthorization> userAuthorizationS=userManagerService.getUserAuthorizationInfo();
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
for(UserAuthorization userAuthorization: userAuthorizationS){
// 從數據庫中獲取用戶受權信息
if(!authen){
throw new RuntimeException("您無權訪問頁面,請以合適身份登錄後查看");
}
filterChain.doFilter(servletRequest,servletResponse);
}
}
@Override
public void destroy() {
}
}
複製代碼
SpringMVC:Spring Security提供了「基於URL的訪問控制」和「基於Method的訪問控制」。sql
在用戶進行操做時,從session中獲取用戶id,將傳入的參數與用戶的身份作綁定校驗。數據庫
<sec:http>
<sec:intercept-url parttern="/persident_portal/*" access="RILE_PERSIDENT"/>
<sec:intercept-url parttern="/manager_portal/*" access="RILE_MANAGER"/>
<sec:intercept-url parttern="/**" access="RILE_USER"/>
<sec:form-login />
<sec:logout />
</sec:http>
複製代碼
http協議屬於明文傳輸協議,交互過程以及數據傳輸都沒有進行加密,通訊雙方也沒有進行任何認證,通訊過程很是容易遭遇劫持、監聽、篡改,嚴重狀況下,會形成惡意的流量劫持等問題,甚至形成我的隱私泄露(好比銀行卡卡號和密碼泄露)等嚴重的安全問題。
HTTPS在HTTP的基礎上加入了SSL協議,SSL依靠證書來驗證服務器的身份,併爲瀏覽器和服務器之間的通訊加密。json
****對稱加密
非對稱加密
https協議(http+ssl協議),以下圖所示爲其鏈接過程:
數字證書是一個經證書受權中心數字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件 客戶端拿到證書後,根據證書用第三方的私鑰進行上的方法本身生成一個證書編號,若是本身生成的證書編號與證書上的證書編號相同,那 麼說明這個證書是真實的。同時,爲避免證書編號自己又被調包,因此使加密。
總結
HTTPS要使客戶端與服務器端的通訊過程獲得安全保證,必須使用的對稱加密算法,可是協商對稱加密算法的過程,須要使用非對稱加密算法 來保證安全,然而直接使用非對稱加密的過程自己也不安全,會有中間人篡改公鑰的可能性,因此客戶端與服務器不直接使用公鑰,而是使用 數字證書籤發機構(CA)頒發的證書來保證非對稱加密過程自己的安全,爲了保證證書不被篡改,引入數字簽名,客戶端使用相同的對稱加 密算法,來驗證證書的真實性,如此,最終解決了客戶端與服務器端之間的通訊安全問題。
敏感數據傳輸過程當中要防止竊取和惡意篡改。使用安全的加密算法加密傳輸對象能夠保護數據。這就是所謂的對對象進行密封。而對密封的對象進行數字簽名則能夠防止對象被非法篡改,保持其完整性
public static void main(String[] args) throwsIOException,ClassNotFoundException{
// Build map
SerializableMap<String, Integer> map = buildMap();
// Serialize map
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data"));
out.writeObject(map);
out.close();
// Deserialize map
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data"));
map = (SerializableMap<String, Integer>) in.readObject();
in.close();
// Inspect map
InspectMap(map);
}
複製代碼
反例:
public static void main(String[] args) throwsIOException,GeneralSecurityException, ClassNotFoundException{
// Build map
SerializableMap<String, Integer> map = buildMap();
// Generate sealing key & seal map
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(newSecureRandom());
Key key= generator.generateKey();
Cipher cipher= Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
SealedObject sealedMap= new SealedObject(map, cipher);
// Serialize map
ObjectOutputStreamout = new ObjectOutputStream(newFileOutputStream("data"));
out.writeObject(sealedMap);
out.close();
// Deserialize map
ObjectInputStream in = newObjectInputStream(newFileInputStream("data"));
sealedMap= (SealedObject) in.readObject();
in.close();
// Unseal map
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
map = (SerializableMap<String, Integer>) sealedMap.getObject(cipher);
// Inspect map
InspectMap(map);
}
public static void main(String[] args) throwsIOException, GeneralSecurityException, ClassNotFoundException{
SerializableMap<String, Integer> map = buildMap();
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(newSecureRandom());
Key key= generator.generateKey();
Cipher cipher= Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
SealedObjectsealedMap= newSealedObject(map, cipher);
KeyPairGeneratorkpg= KeyPairGenerator.getInstance("RSA");
KeyPair kp= kpg.generateKeyPair();
Signature sig = Signature.getInstance("SHA256withRSA");
SignedObject signedMap= newSignedObject(sealedMap, kp.getPrivate(), sig);
ObjectOutputStreamout = newObjectOutputStream(newFileOutputStream("data"));
out.writeObject(signedMap);
out.close();
ObjectInputStream in = newObjectInputStream(newFileInputStream("data"));
signedMap= (SignedObject)
in.readObject();
in.close();
if(!signedMap.verify(kp.getPublic(), sig)){
throw new GeneralSecurityException("Map failed verification");
}
sealedMap= (SealedObject) signedMap.getObject();
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
map = (SerializableMap<String, Integer>) sealedMap.getObject(cipher);
InspectMap(map);
}
複製代碼
正例:
public static void main(String[] args) throws IOException, GeneralSecurityException, ClassNotFoundException{
SerializableMap<String, Integer> map = buildMap();
KeyPairGenerator kpg= KeyPairGenerator.getInstance("RSA");
KeyPair kp= kpg.generateKeyPair();
Signature sig = Signature.getInstance("SHA256withRSA");
SignedObject signedMap= new SignedObject(map, kp.getPrivate(), sig);
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(new SecureRandom());
Key key= generator.generateKey();
Cipher cipher= Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
SealedObject sealedMap = new SealedObject(signedMap, cipher);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data"));
out.writeObject(sealedMap);
out.close();
// Deserialize map
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data"));
sealedMap= (SealedObject) in.readObject();
in.close();
// Unseal map
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
signedMap= (SignedObject) sealedMap.getObject(cipher);
// Verify signature and retrieve map
if(!signedMap.verify(kp.getPublic(), sig)){
throw new GeneralSecurityException("Map failed verification");
}
map = (SerializableMap<String, Integer>) signedMap.getObject();
// Inspect map
InspectMap(map);
}
複製代碼
日誌中如明文保存用戶敏感數據,容易泄露給運維人員或者攻破系統的攻擊者
若是將敏感信息(包括口令和加密密鑰)硬編碼在程序中,可能會將敏感信息暴露給攻擊者。任何可以訪問到class 文件的人均可以反編譯class文件並發現這些敏感信息
...
DriverManager.getConnection(url,"soctt","tiger")
...
複製代碼
Java反編譯
javap c Connmngr.class
ldc #36://String jdbc:mysql://ixne.com/rxsql
ldc #38://String scott
ldc #17://String tiger
反例:
public class IPaddress{
private String ipAddress= "172.16.254.1";
public static voidmain(String[] args){
//...
}
}
複製代碼
正例:
public class IPaddress{
public static void main(String[] args) throws IOException{
char[] ipAddress= new char[100];
BufferedReader br= new BufferedReader(newInputStreamReader(newFileInputStream("serveripaddress.txt")));
// Reads the server IP address into the char array,
// returns the number of bytes read
intn = br.read(ipAddress);
// Validate server IP address
// Manually clear out the server IP address
// immediately after use
for(inti= n -1; i>= 0; i--){
ipAddress[i] = 0;
}
br.close();
}
}
複製代碼
規則2.3:加密傳輸郵件-郵件傳輸時需使用安全協議SSL/TLS加密傳輸,避免攻擊者在網絡上嗅探到用戶數據 反例:郵件傳輸時未使用TLS協議
public class SendMailTLS{
public static void main(String[] args) {
final String username="username@gmail.com";
final String password="password";
Properties props=new Properties();
//使用TLS
//props.put("mail.smtp.auth","true");
//props.put("mail.smtp.startls.enable","true");
//使用SSL
//props.put("mail.smtp.socketFactory,port","465");
//props.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
//props.put("mail.smtp.auth","true");
props.put("mail.smtp.host","smtp.gmail.com");
props.put("mail.smtp.port","587");
Session session=Session.getInstance(props,new javax.mail.Authenticator){
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(username,password);
}
});
}
}
複製代碼
正例:加密使用TLS協議
public class SendMailTLS{
public static void main(String[] args) {
final String username="username@gmail.com";
final String password="password";
Properties props=new Properties();
//使用TLS
props.put("mail.smtp.auth","true");
props.put("mail.smtp.startls.enable","true");
//作服務器證書校驗
props.put("mail.smtp.ssl.checkserveridentity","true");
//添加信任的服務器地址,多個地址之間用空格分開
props.put("mail.smtp.ssl.trust","smtp.gmail.com");
props.put("mail.smtp.host","smtp.gmail.com");
props.put("mail.smtp.port","25");
Session session=Session.getInstance(props,new javax.mail.Authenticator){
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(username,password);
}
});
}
}
複製代碼
若是不加鹽值,會致使相同的密碼獲得相同的Hash值
public class PasswordHash{
public static final String PBKDF2_ALGORITHM="PBKDF2WithHmacSHA1";
public static final int SALT_BYTE_SIZE=24;
public static final int HASH_BYTE_SIZE=24;
public static final int PBKDF2_ITERATIONS=1000;
public static String createHash(char[] password)
throws NoSuchAlgorithmException,InvalidKeySpecException{
//Generate a random salt
SecureRandom random =new SecureRandom();
byte[] salt =new btye[SALT_BYTE_SIZE];
random.nextBytes(salt);
// Hash the password
byte[] hash =pbkdf2(password,salt,PBKDF2_ITERATIONS,HASH_BYTE_SIZE);
//format iterations:salt:hash
return PBKDF2_ITERATIONS+":"+toHex(hash);
}
}
複製代碼
加密算法應該使用安全的加密算法AES攻擊者可以破解不安全的加密算法,獲取到敏感信息
反例:使用了不安全算法DES
byte[] result = DES.encrypt(str.getBytes(),password);
//直接將如上內容解密
try{
byte[] decryResult =DES.decrypt(result,password);
System.out.println("解密後:"+new String(decryResult));
}catch(Excption e1){
e1.printStackTrace();
}
複製代碼
AES分組密碼模式還有:AES-CBC\AES-CFB\AES-OFB\AES-CTR(AES-CTR因爲能並行計算,效率最高)
反例:使用了AES中不安全的分組模式ECB
pubilc class AES{
//加密
public static String Encrypt(String sSrc,String sKey) throws Excetion{
if(sKey==null){
System.out.print("key爲空null");
return null;
}
//判斷key是否爲16位
if(key.length()!=16){
System.out.print("key長度不是16位");
return null;
}
byte[] raw =sKey.getBytes("UTF-8");
SecreKeySpec skeySpec=new SecreKeySpec(raw,"AES");
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");//算法/模式/補碼方式
cipher.init(Cipher.ENCRYPT_MODE,skeySpec);
byte[] encrypted= cipher.doFinal(sScr.getByte("utf-8"));
return new Base64().encodeToString(encrypted);//此處使用Base64作轉碼功能,同時能起到2次加密做用
}
}
複製代碼
正例:使用AES算法中安全的分組模式CBC
pubilc class AES{
//加密
public static String Encrypt(String sSrc,String sKey) throws Excetion{
if(sKey==null){
System.out.print("key爲空null");
return null;
}
//判斷key是否爲16位
if(key.length()!=16){
System.out.print("key長度不是16位");
return null;
}
byte[] raw =sKey.getBytes("UTF-8");
SecreKeySpec skeySpec=new SecreKeySpec(raw,"AES");
Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");//算法/模式/補碼方式
IvParameterSpec iv =new IvParameterSpec(sKey.getByte());//使用CBC模式,須要一個向量iv
//可增長加密算法的強度
cipher.init(Cipher.ENCRYPT_MODE,skeySpec,iv);
byte[] encrypted= cipher.doFinal(sScr.getByte("utf-8"));
return new Base64().encodeToString(encrypted);//此處使用Base64作轉碼功能,同時能起到2次加密做用
}
}
複製代碼
RSA的密鑰長度若是低於2048位,達不到安全標準 反例:RSA算法的密鑰長度只有1024位
public static HashMap<String,Object>getKeys()throws NoSuchAlgorithmException{
HashMap<String,Object> map=new HashMap<String,Object>;
KeyPairGenerator keyPairGen=KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair=keyPairGen.generateKeyPair();
RSAPublicKey pubilcKey=(RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey =(RSAPrivateKey) keyPair.getPrivate();
map.put("public",pubilcKey);
map.put("private",privateKey);
return map;
}
複製代碼
正例:RSA算法的密鑰長度至少2048位
public static HashMap<String,Object>getKeys()throws NoSuchAlgorithmException{
HashMap<String,Object> map=new HashMap<String,Object>;
KeyPairGenerator keyPairGen=KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair keyPair=keyPairGen.generateKeyPair();
RSAPublicKey pubilcKey=(RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey =(RSAPrivateKey) keyPair.getPrivate();
map.put("public",pubilcKey);
map.put("private",privateKey);
return map;
}
複製代碼
僞隨機數生成器具備可移植性和可重複性,攻擊者能夠在系統的一些安全脆弱點上監聽,並構建相應的查詢表預測將要使用的seed值,從而去預測相關的敏感數據
僞隨機數示例:
import java.util.Random;
public class RandomDemo{
Random random1=new Random(100);
System.out.println(random1.nextInt());
System.out.println(random1.nextFloat());
System.out.println(random1.nextBoolean());
Random random2=new Random(100);
System.out.println(random2.nextInt());
System.out.println(random2.nextFloat());
System.out.println(random2.nextBoolean());
}
import java.io.UnsupporedEncodeingException;
import java.util.Random;
public class SecureRandom{
public static void main(String[] args)throws UnsupporedEncodeingException{
Random ranGen=new Random();
byte[] aesKey=new byte[20];
ranKey.nextBytes(aesKey);
StringBuffer hexString =new StringBuffer();
for (int i=0;i<aesKey.length;i++){
String hex=Integer.toHexString(0xff&aesKey[i]);
if(hex.length()==1)
hexString.append(''0);
hexString.append(hex);
}
System.out.println(hexString);
}
}
複製代碼
強隨機數示例:
import java.io.UnsupporedEncodeingException;
import java.util.Random;
import java.security.SecureRandom;
public class SecureRandom{
public static void main(String[] args)throws UnsupporedEncodeingException{
Random ranGen=new SecureRandom();
byte[] aesKey=new byte[20];
ranKey.nextBytes(aesKey);
StringBuffer hexString =new StringBuffer();
for (int i=0;i<aesKey.length;i++){
String hex=Integer.toHexString(0xff&aesKey[i]);
if(hex.length()==1)
hexString.append(''0);
hexString.append(hex);
}
System.out.println(hexString);
}
}
複製代碼
Java 序列化是指把Java 對象轉換爲字節序列的過程便於保存在內存、文件、數據庫中,ObjectOutputStream類的writeObject()方法能夠實現序列化。
Java 反序列化是指把字節序列恢復爲Java 對象的過程,ObjectInputStream 類的readObject() 方法用於反序列化。
public class Test{
public static void main(String[] args) throws Exception{
//定義myObj對象
MyObject myObj=new MyObject();
//建立一個包含對象進行反序列化信息的「object」數據文件
FileOutputStream fos=new FileOutputStream("object");
ObjectOutputStream os =new ObjectOutputStream(fos);
//writeObject()方法將myObj對象寫入objct文件中
os.writeObject(myObj);
os.close();
//從文件中反序列化obj對象
FileInputStream fis=new FileInputStream("object");
ObjectInputStream ois =new ObjectInputStream(fis);
//恢復對象
MyObject objectFromDisk=(MyObject)ois.readObject();
System.out.println(objectFromDisk.name);
ois.close;
}
}
class MyObject implements Serializable{
public String name;
//重寫readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOExeption{
//執行默認的readObject()方法
in.defaultReadObject();
//執行打開計算器程序命令
Runtime.getRuntime().exec("open /Application/Calcultor.app")
}
}
複製代碼
類ObjectInputStream在反序列化時,應對生成的對象的類型作限制
反例:不安全的反序列化操做,致使執行任意命令、控制服務器
ServerSocket serverSocket =new ServerSocket(Integer,parseInt("9999"));
while(true){
Socket socket=serverSocket.accpet();
ObjectInputStream objectInputStream=new ObjectInputStream(socket.getInputStream());
try{
Object object=objectInputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
複製代碼
public final class SecureObjectInputStram extends ObjectInputStream{
public SecureObjectInputStram() throws IOException{
super();
}
public SecureObjectInputStram( InputStream in) throws IOException{
super(in);
}
portected Class<?>resolveClass(ObjectStreamClass desc) throws ClassNotFoundException,IOException{
if(!desc.getName().equals("java.security.Person")){
throws new ClassNotFoundException(desc.getName()+"not found");
}
return super.resolveClass(desc);
}
}
複製代碼
解決方案:
反例:未限定格式
@RequestMapping(value="/upload",method=RequestMethod.POST)
public String upload(HttpServletRequest request, @RequestParam("description") String description,
@RequestParam("file") MultipartFile file)throws Exception{
if(!file.isEmpty()){
String path=request.getServletContext().getRealPath("/images/");
//上傳文件名
String filename=file.getOriginalFilename();
File filepath=new File(path,filename);
//判斷路徑是否存在,若是不存在就建立一個
if(!filepath.getParentFile().exists()){
filepath.getParentFile.mkdirs();
}
//將文件上傳保存到一個目標文件中
file.transferTo(new File(path+File.separator+filename));
return "success"
}else{
return "error";
}
}
複製代碼
正例:限定文件的上傳的格式
@RequestMapping(value="/upload",method=RequestMethod.POST)
public String upload(HttpServletRequest request, @RequestParam("description") String description,
@RequestParam("file") MultipartFile file)throws Exception{
if(!file.isEmpty()){
String path=request.getServletContext().getRealPath("/images/");
//上傳文件名
String filename=file.getOriginalFilename();
//還有一種方式filenameUtils.getExtension();
String suffix=filename.substring(filename.lastIndexOf(".")+1)
if(suffix!="jpg"){
File filepath=new File(path,filename);
//判斷路徑是否存在,若是不存在就建立一個
if(!filepath.getParentFile().exists()){
filepath.getParentFile.mkdirs();
}
//將文件上傳保存到一個目標文件中
file.transferTo(new File(path+File.separator+filename));
}
return "success" ;
}else{
return "error";
}
}
複製代碼
反例:
protected void doGet(HttpServletRequest request,HttpServeltResponse response)
throws ServeltExceptio,IOException{
//獲取項目部署絕對路徑下的upload文件夾路徑,下載upload目錄下的文件
String root =request.getServeltContext().getRealPath("/upload");
//獲取文件名
String filename=request.getParameter("filename");
//根據文件路徑建立輸入流
File file=new File(root+"/"+filename);
FileInputStream fis= new FileInputStream(file);
//設置響應頭
response.addHeader("Content-Disposition","attachment;filename="+new String(filename.getBytes()));
response.addHeader("Content-Length",""+file.length());
byte[] b =new byte[fis.availabe()];
fis.read(b);
response.getOutStream().write(b);
}
複製代碼
正例:使用白名單校驗文件名
protected void doGet(HttpServletRequest request,HttpServeltResponse response)
throws ServeltExceptio,IOException{
//獲取項目部署絕對路徑下的upload文件夾路徑,下載upload目錄下的文件
String root =request.getServeltContext().getRealPath("/upload");
//獲取文件名
String filename=request.getParameter("filename");
if(filename==FILENAME){
//根據文件路徑建立輸入流
File file=new File(root+"/"+filename);
FileInputStream fis= new FileInputStream(file);
//設置響應頭
response.addHeader("Content-Disposition","attachment;filename="+new String(filename.getBytes()));
response.addHeader("Content-Length",""+file.length());
byte[] b =new byte[fis.availabe()];
fis.read(b);
response.getOutStream().write(b);
}
}
複製代碼
使用文件、IO流、數據庫鏈接等不會自動釋放的資源時,未在使用完畢後立刻將其關閉,關閉資源的代碼應在try...catch...finally{if (fos != null) {fos.close();}}的finally內執行
private static void TestCloseFileStream(){
String fileName="";
//聲明引用
InputStream inputStream=null;
try{
inputStream=new FileInputStream(filename);
}catch(IOException e){
//do something
}finally{
if(inputStream!=null){
try{
//關閉流
inputStream.close();
}catch(IOException e){
//do something
}
}
}
}
複製代碼
反例:
public class TempFile{
public static void main(STring[] args) throws IOExcption{
File f =new File("tempnam.tmp");
if(f.exists()){
System.out.println("This file already exists");
return;
}
FileOutputStream fop=null;
try{
fop=new FileOutputStream(f);
String str="Data";
fop.write(str.getBytes());
}finally{
if(fop!=null){
try{
fop.close();
}catch(IOException e){
// handle error
}
}
}
}
}
複製代碼
正例:
public class TempFile{
public static void main(STring[] args) throws IOExcption{
File f =new File("tempnam.tmp");
if(f.exists()){
System.out.println("This file already exists");
return;
}
FileOutputStream fop=null;
try{
fop=new FileOutputStream(f);
String str="Data";
fop.write(str.getBytes());
}finally{
if(fop!=null){
try{
fop.close();
}catch(IOException e){
// handle error
}
//delete file when finished
if(!f.delete()){
//log the error
}
}
}
}
}
複製代碼
雙重鎖機制(多線程下不安全)
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton==null){
synchronized (Singleton.class){
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
singleton = new Singleton()非原子性
1. memory=allocate();//1:分配對象的內存空間
2. ctorInstance(memory);//2:初始化對象
3. singleton=memory;//3:設置instance指向剛分配的內存地址
指令重排後:
1. memory=allocate();//1:分配對象的內存空間
2. singleton=memory;//3:設置instance指向剛分配的內存地址//注意,此時對象尚未被初始化!
3. ctorInstance(memory);//2:初始化對象
複製代碼
多線程安全(Double-Checked Locking)
對方法使用synchronized關鍵字
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getSingleton(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
複製代碼
提早初始化
public class Singleton{
private static class SingletonHolder{
private static final Singleton singleton=new Singleton();
}
private Singleton(){}
public static final Singleton getSingleton(){
return SingletonHolder.INSTANCE;
}
}
複製代碼
好比: --WebLogic、Struts二、Nginx --Fastjson、ImageMagick、Log4j
try{
FileInputStream fis =new FileInputStream(System.getenv("APPDATA")+args[0]);
}catch(FileNotFoundException e){
//Log the exception
throws new IOException ("Unable to retrieve file",e);
}
複製代碼
解析:產生固定的錯誤信息,未泄露異常信息到外部
最後可關注公衆號,一塊兒學習,天天會分享乾貨,還有學習視頻領取!