本節(jié)介紹如何將Spring Security?與servlet API集成。?servletapi-xml示例應(yīng)用程序演示了這些方法的用法。
10.3.1?Servlet 2.5+ Integration
HttpServletRequest.getRemoteUser()
HttpServletRequest.getRemoteUser() 將返回SecurityContextHolder.getContext().getAuthentication().getName()的結(jié)果智厌,后者通常是當(dāng)前用戶(hù)名。如果要在應(yīng)用程序中顯示當(dāng)前用戶(hù)名,這將非常有用娩梨。此外,可以使用檢查這是否為空來(lái)指示用戶(hù)是否已經(jīng)過(guò)身份驗(yàn)證或是匿名的览徒。知道用戶(hù)是否經(jīng)過(guò)身份驗(yàn)證對(duì)于確定是否應(yīng)顯示某些UI元素很有用(即狈定,只有在用戶(hù)經(jīng)過(guò)身份驗(yàn)證時(shí)才應(yīng)顯示注銷(xiāo)鏈接)。
HttpServletRequest.getUserPrincipal()
HttpServletRequest.getUserPrincipal()?將返回SecurityContextHolder.getContext().getAuthentication()的結(jié)果习蓬。這意味著它是一個(gè)身份驗(yàn)證纽什,在使用用戶(hù)名和基于密碼的身份驗(yàn)證時(shí),它通常是UsernamePasswordAuthenticationToken的實(shí)例躲叼。如果您需要有關(guān)用戶(hù)的其他信息稿湿,這將非常有用。例如押赊,您可能創(chuàng)建了一個(gè)自定義的UserDetailsService饺藤,該服務(wù)返回一個(gè)包含您的用戶(hù)名字和姓氏的自定義用戶(hù)詳細(xì)信息包斑。您可以通過(guò)以下方式獲得此信息:
Authentication auth = httpServletRequest.getUserPrincipal();
// assume integrated custom UserDetails called MyCustomUserDetails
// by default, typically instance of UserDetails
MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();
String firstName = userDetails.getFirstName();
String lastName = userDetails.getLastName();
應(yīng)該注意的是,在整個(gè)應(yīng)用程序中執(zhí)行如此多的邏輯通常是不好的做法涕俗。相反罗丰,應(yīng)該集中它以減少Spring Security?和servlet API之間的任何耦合。
HttpServletRequest.isUserInRole(String)
HttpServletRequest.isUserInRole(String)?將確定SecurityContextHolder.getContext().getAuthentication().getAuthorities() 是否包含傳遞給 isUserInRole(string)的角色的授權(quán)再姑。通常萌抵,用戶(hù)不應(yīng)將“ROLE_”前綴傳入此方法,因?yàn)樗亲詣?dòng)添加的元镀。例如绍填,如果要確定當(dāng)前用戶(hù)是否具有“ROLE_ADMIN”權(quán)限,可以使用以下內(nèi)容:
boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");
這可能有助于確定是否應(yīng)顯示某些UI組件栖疑。例如讨永,只有當(dāng)當(dāng)前用戶(hù)是管理員時(shí),才能顯示管理員鏈接遇革。
10.3.2?Servlet 3+ Integration
以下部分介紹Spring Security集成的servlet 3方法卿闹。
HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)
HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)?方法可以用來(lái)確保用戶(hù)已經(jīng)完成了身份驗(yàn)證。如果它們未經(jīng)過(guò)身份驗(yàn)證萝快,則配置的 AuthenticationEntryPoint?將用于請(qǐng)求用戶(hù)進(jìn)行身份驗(yàn)證(即重定向到登錄頁(yè))锻霎。
HttpServletRequest.login(String,String)
HttpServletRequest.login(String,String)方法使用當(dāng)前的?AuthenticationManager?對(duì)用戶(hù)進(jìn)行身份驗(yàn)證。例如揪漩,下面將嘗試使用用戶(hù)名“user”和密碼“password”進(jìn)行身份驗(yàn)證:
try{
????httpServletRequest.login("user","password");
}catch(ServletException e) {
????// fail to authenticate
}
如果希望Spring Security?處理失敗的身份驗(yàn)證嘗試旋恼,則不必捕獲ServletException。
HttpServletRequest.logout()
HttpServletRequest.logout()?方法可用于注銷(xiāo)當(dāng)前用戶(hù)奄容。
通常這意味著?SecurityContextHolder?將被清除冰更,HttpSession?將失效,任何“Remember Me”身份驗(yàn)證將被清除嫩海,等等冬殃。但是,配置的Logoothandler實(shí)現(xiàn)將根據(jù)您的Spring安全配置而變化叁怪。需要注意的是审葬,在調(diào)用HttpServletRequest.logout() 之后,您仍然在改寫(xiě)響應(yīng)奕谭。通常會(huì)重定向到歡迎頁(yè)面涣觉。
AsyncContext.start(Runnable)
AsynchContext.start(Runnable)?方法,用于確保將憑據(jù)傳播到新線(xiàn)程血柳。使用Spring Security 的并發(fā)支持官册,Spring Security?將重寫(xiě)AsyncContext.Start(Runnable),以確保在處理Runnable時(shí)使用當(dāng)前的SecurityContext难捌。例如膝宁,以下內(nèi)容將輸出當(dāng)前用戶(hù)的身份驗(yàn)證:
final AsyncContext async = httpServletRequest.startAsync();
async.start(new Runnable() {
? ? public void run() {
? ? ? ? Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
? ? ? ? try {
? ? ? ? ? ? final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();
? ? ? ? ? ? asyncResponse.setStatus(HttpServletResponse.SC_OK);
? ? ? ? ? ? asyncResponse.getWriter().write(String.valueOf(authentication));
? ? ? ? ? ? async.complete();
? ? ? ? } catch(Exception e) {
? ? ? ? ? ? throw new RuntimeException(e);
? ? ? ? }
? ? }
});
Async Servlet Support
如果您使用的是基于Java的配置鸦难,那么您已經(jīng)準(zhǔn)備好了。如果您使用的是XML配置员淫,則需要進(jìn)行一些更新合蔽。第一步是確保已更新web.xml以至少使用3.0架構(gòu),如下所示:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
接下來(lái)介返,您需要確保為處理異步請(qǐng)求設(shè)置了SpringSecurityFilterChain拴事。
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
? ? org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
就是這樣!現(xiàn)在圣蝎,Spring安全性將確保SecurityContext也在異步請(qǐng)求上傳播刃宵。
那么它是如何工作的呢?如果你真的不感興趣徘公,可以跳過(guò)這一部分的其余部分牲证,否則請(qǐng)繼續(xù)閱讀。其中大部分都內(nèi)置于servlet規(guī)范中步淹,但是Spring Security?做了一些調(diào)整从隆,以確保異步請(qǐng)求能夠正常工作诚撵。在Spring Security3.2之前缭裆,只要提交HttpServletResponse,就會(huì)自動(dòng)保存SecurityContextHolder中的SecurityContext寿烟。這可能導(dǎo)致異步環(huán)境中出現(xiàn)問(wèn)題澈驼。例如,考慮以下內(nèi)容:
httpServletRequest.startAsync();
new Thread("AsyncThread") {
? ? @Override
? ? public void run() {
? ? ? ? try {
? ? ? ? ? ? // Do work
? ? ? ? ? ? TimeUnit.SECONDS.sleep(1);
? ? ? ? ? ? // Write to and commit the httpServletResponse
? ? ? ? ? ? httpServletResponse.getOutputStream().flush();
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}.start();
問(wèn)題是這個(gè)線(xiàn)程不知道Spring Security筛武,所以SecurityContext不會(huì)傳播給它缝其。這意味著當(dāng)我們提交HttpServletResponse?時(shí)沒(méi)有SecuriytContext。當(dāng)Spring Security在提交httpServletResponse時(shí)自動(dòng)保存SecurityContext時(shí)徘六,它將丟失我們的登錄用戶(hù)内边。
從3.2版開(kāi)始,Spring Security就足夠智能待锈,一旦調(diào)用了HttpServletRequest.StartAsync()漠其,就不會(huì)在提交HttpServletResponse時(shí)自動(dòng)保存SecurityContext。
10.3.3?Servlet 3.1+ Integration
以下部分介紹Spring Security集成的servlet 3.1方法竿音。
HttpServletRequest#changeSessionId()
HttpServletRequest.changeSessionId()是servlet 3.1及更高版本中防止會(huì)話(huà)固定攻擊的默認(rèn)方法和屎。