小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Java Web基礎知識之安全:人生苦短,注意安全

 quasiceo 2018-08-14

關于web程序中的安全方面,想必大多數(shù)人都不甚了解,或者說感覺沒有必要了解,身邊開發(fā)網(wǎng)站的人主要就是注重后臺的功能和前臺的界面,不要說程序的安全問題,甚至后臺數(shù)據(jù)庫訪問的問題可能都沒有下大力氣解決。但是這又是和我們密切相關的一個問題,每天看到網(wǎng)站哪個系統(tǒng)或者網(wǎng)站又出現(xiàn)安全問題都感覺離自己很遙遠,其實這只是一個錯覺,還是那句話——人生苦短,注意安全(某些人不要理解錯了,說的就是你。。)。寫這篇文章的時候,恰好想起來本屌絲考大學報志愿的時候,那時候北郵新開了一門專業(yè)叫信息安全,那個年代還不是很火,但是憑借本屌絲敏銳的洞察力(其實是情懷啦)一眼就看出來了該專業(yè)的前景,但是遺憾的是剛剛開辦,還不招生。。。遺憾啊??!

保護我們的web程序可以通過聲明和編程兩種方式來完成,但是不管是哪種方式,都要滿足web安全性的這4個方面:

  • 驗證:這是我們最熟悉的,每個一開始開發(fā)web程序的人都會做一個登錄頁面,這其實就是在驗證web使用者的身份,這里的web使用者不一定是人,也可以是程序,比如某些爬蟲程序想要爬取一些頁面時,這就需要他們提供用戶名和密碼;
  • 授權:關于這個應該也比較熟悉,但是由于我們不太關注,導致忽略了這一點,它主要關注被驗證使用者的級別,是在上一步驗證成功之后進行的,它的作用就是用來限制某個用戶是否有權限進入web程序的某一個部分,直觀點說,一個網(wǎng)站有普通用戶,也有管理員,還有什么內(nèi)容編輯等等,雖然他們都能登錄成功,但是普通用戶和頁面編輯是不能進入網(wǎng)站的管理界面的,這就是他們沒有得到網(wǎng)站擁有者的授權,這里的實現(xiàn)方式是通過建立角色來完成的,給予每個人特定的角色,然后規(guī)定一種角色能夠訪問web程序的哪些部分,最近Facebook獎勵給發(fā)現(xiàn)Instagram漏洞的10歲兒童一萬美金,就是出現(xiàn)了授權的漏洞。
  • 加密:這就比較好理解了,因為數(shù)據(jù)自互聯(lián)網(wǎng)上進行傳輸?shù)臅r候是從一臺計算機傳到另一臺計算機,等到了服務器時,可能已經(jīng)經(jīng)過了不止一臺計算機,這就給別人攔截數(shù)據(jù)提供了極大地方便,因此我們需要對傳輸?shù)臄?shù)據(jù)進行加密,關于加密算法有很多,慕課網(wǎng)上有很多加密算法的講解,可以去看一下;
  • 完整:關于數(shù)據(jù)的完整性,簡單來說雖然你加密了傳輸?shù)臄?shù)據(jù),但是人家還是可以攔截,可能只是讀不懂是什么意思,但是可以隨便更改,到接收方就無法確認該數(shù)據(jù)是否還是從客戶端發(fā)出的數(shù)據(jù),這樣就無法保證數(shù)據(jù)的完整性了,像數(shù)字電路中還有奇偶校驗位來保證數(shù)據(jù)傳輸?shù)恼_,在web程序中可以通過建立一個安全通道來傳輸數(shù)據(jù)。
關于web應用程序的安全管理是使用聲明還是編程呢?其實這是取決于具體的業(yè)務要求,它們兩個各有各的優(yōu)點和缺點:
  • 編程:其實大多數(shù)web程序使用的都是這種方式,我們不用看這篇文章都知道要怎么做,將用戶輸入的用戶名和密碼與存儲在服務器上或者數(shù)據(jù)庫中的進行驗證,如果驗證成功,再看該客戶具體的角色。
  • 聲明:最大的好處是避免部分編程,因為驗證和授權的部分是servlet容器完成的,而且在聲明式安全中,瀏覽器可以在將用戶名和密碼發(fā)到服務器前對其進行加密,由于使用聲明,所以所有的安全性約束都不用寫到servlet類中,只要在部署描述符中進行聲明即可,有很大的靈活性;但是聲明這種方式也有缺點,支持數(shù)據(jù)加密的驗證方法只能使用servlet容器(Tomcat)提供的默認登錄框,不能定制,在這個看臉的社會無疑已經(jīng)被淘汰出局,另外如果要使用定制的登錄表單就不會對所傳輸?shù)臄?shù)據(jù)加密。

一、 聲明式安全

其實使用聲明式驗證功能使用的是Http協(xié)議中的內(nèi)容,而不是servlet中的內(nèi)容(Http功能簡直太強大了)。使用聲明式安全,首先要做的就是定義用戶和角色,不同的servlet容器,用戶和角色的保存位置也不同,使用tomcat可以將這些信息保存在配置文件tomcat-users.xml中,然后可以對一些資源的進行安全限制。在tomcat-users.xml中配置如下:
  1. <role rolename="manager-gui"/>
  2. <role rolename="admin"/>
  3. <role rolename="user"/>
  4. <user password="tomcat" roles="manager-gui" username="tomcat"/>
  5. <user password="lmy86263" roles="admin" username="lmy86263"/>
  6. <user password="guest" roles="user" username="guest"/>
在配置tomcat的用戶和角色時要注意,每次重啟tomcat的時候,你在之前配置的用戶和角色都會消失導致恢復到tomcat的默認狀態(tài),這是因為在eclipse中初次配置tomcat服務器時,eclipse會將tomcat的配置文件拷貝到自己的workspace下的server文件夾,每次啟動讀取的配置文件都是從這里讀取的,而且還會用這里的配置覆蓋tomcat目錄下的配置文件,所以為了避免出現(xiàn)這種麻煩,我們將配置好的文件拷貝到workspace下的server文件夾一份即可,如下:

在web程序中將文件放到WEB-INF下或者其子目錄下可以隱藏他們,但是在后臺可以通過servlet和JSP頁面跳轉(zhuǎn)到那里,簡單粗暴,不夠好。我們可以通過將這些資源放到應用程序目錄下,然后通過安全約束來對訪問這些資源的請求進行驗證,主要是通過在部署描述符中進行配置來實現(xiàn)對一些請求進行約束。
在部署描述符中使用security-constraint和login-config兩個元素來完成訪問的驗證,下面三種類型只是變換其中的幾個屬性而已。使用如下:
  1. <security-constraint>
  2. <web-resource-collection>
  3. <web-resource-name>HttpServlet</web-resource-name>
  4. <url-pattern>/myHttpServlet</url-pattern>
  5. <http-method>GET</http-method>
  6. <http-method>POST</http-method>
  7. </web-resource-collection>

  8. <auth-constraint>
  9. <role-name>admin</role-name>
  10. </auth-constraint>
  11. </security-constraint>

  12. <login-config></login-config>
關于上述幾個元素,解釋如下:
  • security-constraint:用來指定一個資源集合和可以訪問這些資源的一個或者多個角色
  • web-resource-collection:用來指定一組資源集合,這里面有幾個元素比較重要,url-pattern就不說了,和之前使用servlet和Filter時一樣,就是為了映射一個servlet資源,此處的映射只適用于直接訪問該資源,如果是后臺forward該資源或者使用JSP標簽來訪問時不受該安全機制約束的,這個元素可以有多個;http-method元素用來定義http方法,例如上述中使用了GET和POST方法說明安全性約束只適用于這兩種方法,也就是說如果使用了PUT或者DELETE方法訪問這些資源是不受該安全機制約束的,默認是所有方法都保護,這個元素也可以有多個;還有一個元素這里沒有寫出來是http-method-omission,這個正好和http-method相反,它是說明除了該屬性中的方法之外的所有方法訪問該資源時都會被限制,它不能和http-method一起使用;
  • auth-constraint:用來指定可以訪問該資源的角色名稱,如果沒有該元素則說明所有人都可以訪問該元素,這樣就沒有意義;如果該元素存在但是是空的,說明沒有人能夠直接訪問該資源(注意是直接);

1、 基本訪問驗證

基本訪問驗證是接受用戶名和密碼的HTTP驗證,如果用戶訪問一個受保護的資源,則會被服務器拒絕,登錄框會一直存在直到你輸入正確的用戶名和密碼,如果你取消認證則會返回401錯誤,但是要注意的一點是如果你登錄成功,但是你的role-name并不在<auth-constraint>中出現(xiàn),則會返回403錯誤,所以一定要注意這兩種錯誤是不同的。
  1. <login-config>
  2. <auth-method>BASIC</auth-method>
  3. <realm-name>Admin only</realm-name>
  4. </login-config>
使用這種Http驗證方式,是將用戶名和密碼按照"用戶名:密碼"這種形式組合并且使用Base64算法進行編碼傳輸?shù)椒掌鳎@種算法很弱,在網(wǎng)上隨便找一個解碼的網(wǎng)站都能知道你的用戶名和密碼。下面實現(xiàn)出現(xiàn)驗證錯誤和沒有授權時的截圖:
驗證錯誤:

注意狀態(tài)碼,還有注意Http協(xié)議中提供的首部WWW-Authenticate,可以看出這里面的值就是我們在上面配置的屬性,這個首部就是用來驗證程序使用者的身份的,在其中realm項中包含可以訪問該資源的角色名,在登錄時就會使用另一個首部Authorization包含使用Base64編碼的用戶名和密碼信息,如下圖所示,這里由于取消了驗證所以沒有發(fā)送該首部。注意這兩個首部的順序,當發(fā)出登錄請求時使用Authorization,然后服務器返回WWW-Authenticate,但是當你取消登錄時也會返回WWW-Authenticate,總之一句話,只要你沒登陸成功就會返回這個首部。
沒有授權:

這里可以看到雖然驗證成功了,但是由于該用戶對應的角色不在安全約束的范圍之內(nèi),所以也是被禁止訪問該資源的。

2、 摘要訪問驗證

關于摘要訪問驗證和基本訪問驗證類似,只是驗證方法使用DIGEST,這種方式不使用Base64算法。這種是使用MD5散列函數(shù)計算用戶名、密碼、realm值三者結(jié)合在一起的一個散列值,然后將這個散列值發(fā)送到服務器,其中具體的技術見如下鏈接。
  1. <login-config>
  2. <auth-method>DIGEST</auth-method>
  3. <realm-name>Admin only</realm-name>
  4. </login-config>

使用這種方式的請求和響應如下:


3、 表單訪問驗證 

由于上述兩種方式都不不能支持自定義的登錄頁面,使用基于表單的訪問驗證可以避免這種尷尬,但是由于這種方式傳輸?shù)臄?shù)據(jù)是沒有加密的,所以它的使用是和其他的加密方法比如SSL等一起使用的。使用這種方式時,首先要自定義登錄頁面和錯誤處理頁面,驗證方式使用FORM,通過下面的聲明來完成配置:

  1. <login-config>
  2. <auth-method>FORM</auth-method>
  3. <realm-name>Admin only</realm-name>
  4. <form-login-config>
  5. <form-login-page>/login.jsp</form-login-page>
  6. <form-error-page>/error.jsp</form-error-page>
  7. </form-login-config>
  8. </login-config>
當用戶名和密碼與數(shù)據(jù)庫或者文件中存儲的信息不同時就會返回error.jsp頁面,如果匹配成功是不會返回該頁面;如果成功匹配,但是該用戶的角色并沒有權限訪問該頁面,則不會返回error.jsp頁面,而是直接返回403沒有權限訪問的信息。
關于錯誤處理頁面比較簡單這里就不說明了,但是登錄頁面中有幾點要注意,登錄頁面如下:
  1. <%@ page language="java" contentType="text/html; charset=GBK"
  2. pageEncoding="GBk"%>
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www./TR/html4/loose.dtd">
  4. <html>
  5. <head>
  6. <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
  7. <title>Login</title>
  8. </head>
  9. <body>
  10. <form action="j_security_check" method="post">
  11. 用戶名: <input type="text" name="j_username" >
  12. 密碼: <input type="password" name="j_password">
  13. <input type="submit">
  14. </form>
  15. </body>
  16. </html>
在表單中,注意action是j_security_check,用戶名是j_username,密碼是j_password,這三個字段都是由servlet容器來實現(xiàn)的,這里使用的是tomcat,在tomcat中處理這部分的類是org.apache.catalina.authenticator.FormAuthenticator,對應的代碼在authenticate()方法中,如下:
  1. boolean loginAction = (requestURI.startsWith(contextPath)) && (requestURI.endsWith("/j_security_check"));
  2. if (!loginAction)
  3. {
  4. if ((request.getServletPath().length() == 0) && (request.getPathInfo() == null))
  5. {
  6. StringBuilder location = new StringBuilder(requestURI);
  7. location.append('/');
  8. if (request.getQueryString() != null)
  9. {
  10. location.append('?');
  11. location.append(request.getQueryString());
  12. }
  13. response.sendRedirect(response.encodeRedirectURL(location.toString()));
  14. return false;
  15. }
  16. session = request.getSessionInternal(true);
  17. if (log.isDebugEnabled()) {
  18. log.debug("Save request in session '" + session.getIdInternal() + "'");
  19. }
  20. try
  21. {
  22. saveRequest(request, session);
  23. }
  24. catch (IOException ioe)
  25. {
  26. log.debug("Request body too big to save during authentication");
  27. response.sendError(403, sm.getString("authenticator.requestBodyTooBig"));

  28. return false;
  29. }
  30. forwardToLoginPage(request, response, config);
  31. return false;
  32. }
  33. request.getResponse().sendAcknowledgement();
  34. Realm realm = this.context.getRealm();
  35. if (this.characterEncoding != null) {
  36. request.setCharacterEncoding(this.characterEncoding);
  37. }
  38. String username = request.getParameter("j_username");
  39. String password = request.getParameter("j_password");
  40. if (log.isDebugEnabled()) {
  41. log.debug("Authenticating username '" + username + "'");
  42. }
  43. principal = realm.authenticate(username, password);
  44. if (principal == null)
  45. {
  46. forwardToErrorPage(request, response, config);
  47. return false;
  48. }
  49. if (log.isDebugEnabled()) {
  50. log.debug("Authentication of '" + username + "' was successful");
  51. }
  52. if (session == null) {
  53. session = request.getSessionInternal(false);
  54. }
  55. if (session == null)
  56. {
  57. if (this.containerLog.isDebugEnabled()) {
  58. this.containerLog.debug("User took so long to log on the session expired");
  59. }
  60. if (this.landingPage == null)
  61. {
  62. response.sendError(408, sm.getString("authenticator.sessionExpired"));
  63. }
  64. else
  65. {
  66. String uri = request.getContextPath() + this.landingPage;
  67. SavedRequest saved = new SavedRequest();
  68. saved.setMethod("GET");
  69. saved.setRequestURI(uri);
  70. saved.setDecodedRequestURI(uri);
  71. request.getSessionInternal(true).setNote("org.apache.catalina.authenticator.REQUEST", saved);

  72. response.sendRedirect(response.encodeRedirectURL(uri));
  73. }
  74. return false;
  75. }

二、 編程式安全

現(xiàn)在大多數(shù)應用其實都是采用這種方式來實現(xiàn)自己的安全需求的,幸好在servlet中已經(jīng)有這方面的規(guī)范了,讓我們很容易使用。但有一點還是沒有做好就是雖然不用在配置文件中聲明了,但是驗證時仍要進行的登錄頁面還是要在web.xml中使用login-config配置。

1、 使用注解

使用注解時其實完成的就是web.xml中security-constraint的功能,與安全有關的主要是三個注解:
  • @ServletSecurity:包括以下兩個注解,對應于security-constraint元素;
  • @HttpConstraint:主要用來添加允許訪問該資源的角色,通過rolesAllowed配置;
  • @HttpMethodContraint:主要用來添加被該安全機制所限制的Http方法,它里面也有一個rolesAllowed屬性,和上面的注解中的屬性要表達的意義是相同的,但是它只作用于;
其中后兩個注解的使用可以演變出很多的組合,簡單的使用如下:
  1. @WebServlet(name="securityServlet", urlPatterns={"/securityServlet"})
  2. @ServletSecurity(value=@HttpConstraint(rolesAllowed="user"),
  3. httpMethodConstraints={@HttpMethodConstraint(value="GET", rolesAllowed="admin")})
  4. public class SecurityServlet extends HttpServlet {}
說明允許user角色使用所有除了GET方法的Http方法訪問該資源,但在通過GET方法訪問該資源的時候必須驗證客戶的角色是否是admin。
關于這幾個注解的多種組合方式以及具體的含義,可以參考下面的文章。

2、 使用API

除了使用注解,Servlet規(guī)范還提供了使用API實現(xiàn)編程式安全,這些API都是在HttpServletRequest中定義,使用如下:
  • getAuthType():返回保護該Servlet的驗證方法,對應的是web.xml中的<login-config>中的<auth-method>的值,如果沒有驗證方法則會返回null;
  • getRemoteUser():返回發(fā)出該請求的用戶的登錄名,如果該用戶沒有通過驗證則會返回null;
  • isUserInRole():標明該經(jīng)過驗證的用戶是否屬于指定的角色,如果沒有經(jīng)過驗證返回false;
  • getUserPrincipal():返回包含被驗證用戶信息的java.security.Principal,如果未經(jīng)過驗證則返回null;
  • authenticate():命令瀏覽器顯示登錄窗口用于對用戶進行驗證,驗證方法使用表單方式時登錄窗口為我們自定義的表單,否則使用servlet容器提供給我們的登錄窗口;
  • login():用于提供用戶名和密碼進行登錄,登錄成功不反返回任何值,登錄失敗則會拋出ServletException;
  • logout():重置用戶信息;
使用實例如下:
  1. @Override
  2. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  3. if(req.authenticate(resp)){
  4. System.out.println("success");
  5. }
  6. else {
  7. System.out.println("fail");
  8. }

  9. System.out.println("AuthType: " + req.getAuthType());
  10. System.out.println("RemoteUser: " + req.getRemoteUser());
  11. System.out.println("isUserInRole: " + req.isUserInRole("admin"));
  12. System.out.println("UserPrincipal: " + req.getUserPrincipal());

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多