自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

寫了這么多年代碼,這樣的登錄方式還是頭一回見(jiàn)!

開發(fā) 前端
Spring Security 系列還沒(méi)搞完,最近還在研究。有的時(shí)候我不禁想,如果從 Spring Security 誕生的第一天開始,我們就一直在追蹤它,那么今天再去看它的源碼一定很簡(jiǎn)單,因?yàn)槲覀兞私獾矫恳恍写a的緣由。

 Spring Security 系列還沒(méi)搞完,最近還在研究。

[[332172]]

有的時(shí)候我不禁想,如果從 Spring Security 誕生的第一天開始,我們就一直在追蹤它,那么今天再去看它的源碼一定很簡(jiǎn)單,因?yàn)槲覀兞私獾矫恳恍写a的緣由。

然而事實(shí)上我們大部分人都是中途接觸到它的,包括松哥自己。所以在閱讀源碼的時(shí)候,有時(shí)候會(huì)遇到一些不是那么容易理解的東西,并不是說(shuō)這個(gè)有多難,只是我們不了解 N 年前的開發(fā)環(huán)境,因此也就不容易理解某一行代碼出現(xiàn)的意義。

所以為了搞透徹這個(gè)框架,有時(shí)候我們還得去了解之前發(fā)生了什么。

這就跟學(xué) Spring Boot 一樣,很多小伙伴問(wèn)要不要跳過(guò) SSM ,我說(shuō)不要,甚至還專門寫了一篇文章(Spring Boot 要怎么學(xué)?要學(xué)哪些東西?要不要先學(xué) SSM?),跳過(guò)了 SSM ,Spring Boot 中的很多東西就無(wú)法真正理解。

扯遠(yuǎn)了。。。

Spring Security 中對(duì) HttpServletRequest 請(qǐng)求進(jìn)行了封裝,重寫了 HttpServletRequest 中的幾個(gè)和安全管理相關(guān)的方法,想要理解 Spring Security 中的重寫,就要先從 HttpServletRequest 開始看起。

有小伙伴可能會(huì)說(shuō),HttpServletRequest 能跟安全管理扯上什么關(guān)系?今天松哥就來(lái)和大家捋一捋,我們不講 Spring Security,就來(lái)單純講講 HttpServletRequest 中的安全管理方法。

1.HttpServletRequest

在 HttpServletRequest 中,我們常用的方法如:

  • public String getHeader(String name);
  • public String getParameter(String name);
  • public ServletInputStream getInputStream()
  • ...

這些常見(jiàn)的方法可能大家都有用過(guò),還有一些不常見(jiàn)的,和安全相關(guān)的方法:

  1. public String getRemoteUser(); 
  2. public boolean isUserInRole(String role); 
  3. public java.security.Principal getUserPrincipal(); 
  4. public boolean authenticate(HttpServletResponse response) 
  5.             throws IOException, ServletException; 
  6. public void login(String username, String password) throws ServletException; 
  7. public void logout() throws ServletException; 

前面三個(gè)方法,在之前的 Servlet 中就有,后面三個(gè)方法,則是從 Servlet3.0 開始新增加的方法。從方法名上就可以看出,這些都是和認(rèn)證相關(guān)的方法,但是這些方法,我估計(jì)很多小伙伴都沒(méi)用過(guò),因?yàn)椴惶珜?shí)用。

在 Spring Security 框架中,對(duì)這些方法進(jìn)行了重寫,進(jìn)而帶來(lái)了一些好玩并且方便的特性,這個(gè)松哥在后面的文章中再和大家分享。

要理解 Spring Security 中的封裝,就得先來(lái)看看,不用框架,這些方法該怎么用!

2.實(shí)踐出真

知我們創(chuàng)建一個(gè)普普通通的 Web 項(xiàng)目,不使用任何框架(后面的案例都基于此),然后在 doGet 方法中打印出 HttpServletRequest 的類型,代碼如下:

  1. @Override 
  2. protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { 
  3.     System.out.println("request.getClass() = " + request.getClass()); 

代碼運(yùn)行打印結(jié)果如下:

  1. request.getClass() = class org.apache.catalina.connector.RequestFacade 

HttpServletRequest 是一個(gè)接口,而 RequestFacade 則是一個(gè)正兒八經(jīng)的 class。

HttpServletRequest 是 Servlet 規(guī)范中定義的 ServletRequest,這相當(dāng)于是標(biāo)準(zhǔn)的 Request;但是在 Tomcat 中的 Request 則是 Tomcat 自己自定義的 Request,自定義的 Request 實(shí)現(xiàn)了 HttpServletRequest 接口并且還定義了很多自己的方法,這些方法還是 public 的,如果直接使用 Tomcat 自定義的 Request,開發(fā)者只需要向下轉(zhuǎn)型就能調(diào)用這些 Tomcat 內(nèi)部方法,這是有問(wèn)題的,所以又用 RequestFacade 封裝了一下,以至于我們實(shí)際上用到的就是 RequestFacade 對(duì)象。

那么毫無(wú)疑問(wèn),HttpServletRequest#login 方法具體實(shí)現(xiàn)就是在 Tomcat 的 Request#login 方法中完成的。經(jīng)過(guò)源碼追蹤,我們發(fā)現(xiàn),登錄的數(shù)據(jù)源是由 Tomcat 中的 Realm 提供的,注意這個(gè) Realm 不是 Shiro 中的 Realm。

Tomcat 中提供了 6 種 Realm,可以支持與各種數(shù)據(jù)源的對(duì)接:

  • JDBCRealm:很明顯,這個(gè) Realm 可以對(duì)接到數(shù)據(jù)庫(kù)中的用戶信息。
  • DataSourceRealm:它通過(guò)一個(gè) JNDI 命名的 JDBC 數(shù)據(jù)源在關(guān)系型數(shù)據(jù)庫(kù)中查找用戶。
  • JNDIRealm:通過(guò)一個(gè) JNDI 提供者1在 LDAP 目錄服務(wù)器中查找用戶。
  • UserDatabaseRealm:這個(gè)數(shù)據(jù)源在 Tomcat 的配置文件中 conf/tomcat-users.xml。
  • MemoryRealm:這個(gè)數(shù)據(jù)源是在內(nèi)存中,內(nèi)存中的數(shù)據(jù)也是從 conf/tomcat-users.xml 配置文件中加載的。
  • JAASRealm:JAAS 架構(gòu)來(lái)實(shí)現(xiàn)對(duì)用戶身份的驗(yàn)證。

如果這些 Realm 無(wú)法滿足需求,當(dāng)然我們也可以自定義 Realm,只不過(guò)一般我們不這樣做,為啥?因?yàn)檫@這種登錄方式用的太少了!今天這篇文章純粹是和小伙伴們開開眼界。

如果自定義 Realm 的話,我們只需要實(shí)現(xiàn) org.apache.catalina.Realm 接口,然后將編譯好的 jar 放到 $CATALINA_HOME/lib 下即可,具體的配置則和下面介紹的一致。

接下來(lái)我和大家介紹兩種配置方式,一個(gè)是 UserDatabaseRealm,另一個(gè)是 JDBCRealm。

2.1 基于配置文件登錄

我們先來(lái)定義一個(gè) LoginServlet:

  1. @WebServlet(urlPatterns = "/login"
  2. public class LoginServlet extends HttpServlet { 
  3.     @Override 
  4.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  5.         doPost(req, resp); 
  6.     } 
  7.  
  8.     @Override 
  9.     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  10.         String username = req.getParameter("username"); 
  11.         String password = req.getParameter("password"); 
  12.         try { 
  13.             req.login(username, password); 
  14.         } catch (ServletException e) { 
  15.             req.getRequestDispatcher("/login.jsp").forward(req, resp); 
  16.             return
  17.         } 
  18.         boolean login = req.getUserPrincipal() != null && req.isUserInRole("admin"); 
  19.         if (login) { 
  20.             resp.sendRedirect("/hello"); 
  21.             return
  22.         } else { 
  23.             req.getRequestDispatcher("/login.jsp").forward(req, resp); 
  24.         } 
  25.     } 

當(dāng)請(qǐng)求到達(dá)后,先提取出用戶名和密碼,然后調(diào)用 req.login 方法進(jìn)行登錄,如果登錄失敗,則跳轉(zhuǎn)到登錄頁(yè)面。

登錄完成后,通過(guò)獲取登錄用戶信息以及判斷登錄用戶角色,來(lái)確保用戶是否登錄成功。

如果登錄成功,就跳轉(zhuǎn)到項(xiàng)目應(yīng)用首頁(yè),否則就跳轉(zhuǎn)到登錄頁(yè)面。

接下來(lái)定義 HelloServlet:

  1. @WebServlet(urlPatterns = "/hello"
  2. public class HelloServlet extends HttpServlet { 
  3.     @Override 
  4.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  5.         doPost(req,resp); 
  6.     } 
  7.  
  8.     @Override 
  9.     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  10.         Principal userPrincipal = req.getUserPrincipal(); 
  11.         if (userPrincipal == null) { 
  12.             resp.setStatus(401); 
  13.             resp.getWriter().write("please login"); 
  14.         } else if (!req.isUserInRole("admin")) { 
  15.             resp.setStatus(403); 
  16.             resp.getWriter().write("forbidden"); 
  17.         }else
  18.             resp.getWriter().write("hello"); 
  19.         } 
  20.     } 

在 HelloServlet 中,先判斷用戶是否已經(jīng)登錄,沒(méi)登錄的話,就返回 401,已經(jīng)登錄但是不具備相應(yīng)的角色,就返回 403,否則就返回 hello。

接下來(lái)再定義 LogoutServlet,執(zhí)行注銷操作:

  1. @WebServlet(urlPatterns = "/logout"
  2. public class LogoutServlet extends HttpServlet { 
  3.     @Override 
  4.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  5.         doPost(req,resp); 
  6.     } 
  7.  
  8.     @Override 
  9.     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  10.         req.logout(); 
  11.         resp.sendRedirect("/hello"); 
  12.     } 

logout 方法也是 HttpServletRequest 自帶的。

最后再簡(jiǎn)單定義一個(gè) login.jsp 頁(yè)面,如下:

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %> 
  2. <html> 
  3. <head> 
  4.     <title>Title</title> 
  5. </head> 
  6. <body> 
  7. <form action="/login" method="post"
  8.     <input type="text" name="username"
  9.     <input type="text" name="password"
  10.     <input type="submit" value="登錄"
  11. </form> 
  12. </body> 
  13. </html> 

所有工作都準(zhǔn)備好了,接下來(lái)就是數(shù)據(jù)源了,默認(rèn)情況下加載的是 conf/tomcat-users.xml 中的數(shù)據(jù),找到 Tomcat 的這個(gè)配置文件,修改之后內(nèi)容如下:

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <tomcat-users> 
  3.     <role rolename="admin"/> 
  4.     <user username="javaboy" password="123" roles="admin"/> 
  5. </tomcat-users> 

配置完成后,啟動(dòng)項(xiàng)目進(jìn)行測(cè)試。登錄用戶名是 javaboy,登錄密碼是 123,具體的測(cè)試過(guò)程我就不再演示了。

2.2 基于數(shù)據(jù)庫(kù)登錄

如果想基于數(shù)據(jù)庫(kù)登錄,我們需要先準(zhǔn)備好數(shù)據(jù)庫(kù)和表,需要兩張表,user 表和 role 表,如下:

  1. CREATE TABLE `user` ( 
  2.   `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
  3.   `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
  4.   `passwordvarchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
  5.   PRIMARY KEY (`id`) 
  6. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 
  7. CREATE TABLE `role` ( 
  8.   `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
  9.   `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
  10.   `role_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
  11.   PRIMARY KEY (`id`) 
  12. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 

然后向表中添加兩行模擬數(shù)據(jù):

接下來(lái),找到 Tomcat 的 conf/server.xml 文件,修改配置,如下:

  1. <Realm className="org.apache.catalina.realm.LockOutRealm"
  2.   <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99" 
  3.         driverName="com.mysql.jdbc.Driver" 
  4.         connectionURL="jdbc:mysql://localhost:3306/basiclogin" 
  5.         connectionName="root" connectionPassword="123" 
  6.         userTable="user" userNameCol="username"     
  7.         userCredCol="password" 
  8.         userRoleTable="role" roleNameCol="role_name" /> 
  9. </Realm> 

在這段配置中:

  • 指定 JDBCRealm。
  • 指定數(shù)據(jù)庫(kù)驅(qū)動(dòng)。
  • 指定數(shù)據(jù)庫(kù)連接地址。
  • 指定數(shù)據(jù)庫(kù)連接用戶名/密碼。
  • 指定用戶表名稱;用戶名的字段名以及密碼字段名。
  • 指定角色表名稱;以及角色字段名。

配置完成后,再次登錄測(cè)試,此時(shí)的登錄數(shù)據(jù)就是來(lái)自數(shù)據(jù)庫(kù)的數(shù)據(jù)了。

3.優(yōu)化

前面的 HelloServlet,我們是在代碼中手動(dòng)配置的,要是每個(gè) Servlet 都這樣配置,這要搞到猴年馬月了~

所以我們對(duì)此可以在 web.xml 中進(jìn)行手動(dòng)配置。

首先我們創(chuàng)建一個(gè) AdminServlet 進(jìn)行測(cè)試,如下:

  1. @WebServlet(urlPatterns = "/admin/hello"
  2. public class AdminServlet extends HttpServlet { 
  3.     @Override 
  4.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  5.         resp.getWriter().write("hello admin!"); 
  6.     } 

然后在 web.xml 中進(jìn)行配置:

  1. <security-constraint
  2.     <web-resource-collection> 
  3.         <web-resource-name>admin</web-resource-name
  4.         <url-pattern>/admin/*</url-pattern> 
  5.     </web-resource-collection> 
  6.     <auth-constraint
  7.         <role-name>admin</role-name
  8.     </auth-constraint
  9. </security-constraint
  10. <security-role> 
  11.     <role-name>admin</role-name
  12. </security-role> 

這個(gè)配置表示 /admin/* 格式的請(qǐng)求路徑,都需要具有 admin 角色才能訪問(wèn),否則就訪問(wèn)不到,這樣,每一個(gè) Admin 相關(guān)的 Servlet 就被保護(hù)起來(lái)了,不用在 Servlet 中寫代碼判斷了。

4.小結(jié)

好啦,經(jīng)過(guò)本文的介紹,相信小伙伴們對(duì)于 HttpServletRequest 中關(guān)于認(rèn)證的幾個(gè)方法基本上都了解了,接下來(lái)的文章松哥將繼續(xù)和大家介紹這些方法在 Spring Security 框架中是如何進(jìn)行演化的,看懂了本文,后面的文章就很好理解了~

本文案例下載地址:https://github.com/lenve/javaboy-code-samples

本文轉(zhuǎn)載自微信公眾號(hào)「江南一點(diǎn)雨」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系江南一點(diǎn)雨公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2018-10-06 21:51:37

代碼SOLID編程

2018-10-07 06:30:40

代碼設(shè)計(jì)模式面向?qū)ο笤瓌t

2017-11-30 07:30:27

程序員代碼軟件世界觀

2024-02-20 08:09:51

Java 8DateUtilsDate工具類

2021-02-03 08:24:32

JavaScript技巧經(jīng)驗(yàn)

2015-03-27 10:20:41

谷歌地圖谷歌偉大

2021-09-13 10:03:54

藍(lán)牙連接藍(lán)牙藍(lán)牙設(shè)備

2020-03-30 16:18:02

代碼開發(fā)工具

2020-07-28 15:18:52

Gartner信息安全網(wǎng)絡(luò)安全

2023-11-13 08:49:54

2022-04-21 07:52:08

JS線程GUI渲染

2024-03-01 17:01:15

GraphQL后端

2020-07-21 18:37:14

代碼條件變量

2023-05-31 16:40:01

2020-11-20 10:22:34

代碼規(guī)范設(shè)計(jì)

2017-08-21 15:10:30

筆記本塑料材質(zhì)工藝

2021-04-27 15:13:20

Java開發(fā)語(yǔ)言

2021-12-16 10:16:18

硬盤WindowsiPhone

2024-05-23 10:34:15

CSS 3CSS技術(shù)

2021-01-14 09:40:22

AI 虛擬人工智能
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)