javascript
Spring Security应用程序中的su和sudo
很久以前,我從事的項(xiàng)目具有很強(qiáng)大的功能。 有兩個角色:用戶和主管。 主管可以以任何方式更改系統(tǒng)中的任何文檔,而用戶則更受工作流約束的限制。 當(dāng)普通用戶對當(dāng)前正在編輯和存儲在HTTP會話中的文檔有疑問時(shí),主管可以介入,切換到特殊的主管模式并繞過所有約束。 完全自由。 相同的計(jì)算機(jī),相同的鍵盤,相同的HTTP會話。 通過輸入秘密密碼,只有管理員可以設(shè)置的特殊標(biāo)志。 主管完成后,他或她可以清除該標(biāo)志并再次啟用常規(guī)約束。
此功能運(yùn)行良好,但實(shí)施效果不佳。 每個單個輸入字段的可用性取決于該超級用戶模式標(biāo)志。 使用isSupervisorMode()檢查在數(shù)十個地方污染了業(yè)務(wù)方法。 請記住,如果管理員只是使用常規(guī)憑據(jù)登錄,則此模式是隱式的,因此安全約束基本上是重復(fù)的。
當(dāng)我們的應(yīng)用程序可以高度自定義并具有大量安全角色時(shí),就會出現(xiàn)另一個有趣的用例。 遲早您將面臨異常(確定, 錯誤 ),您無法復(fù)制具有不同權(quán)限的異常。 能夠以該特定用戶身份登錄并環(huán)顧四周可能是一個很大的勝利。 當(dāng)然,您不知道用戶的密碼( 不是嗎? )。 類似UNIX的系統(tǒng)找到了解決此問題的方法: su ( 切換用戶 )和sudo命令。 出乎意料的是, Spring Security附帶了內(nèi)置的SwitchUserFilter ,它在原則上模仿Web應(yīng)用程序中的su 。 試一試吧!
您需要聲明自定義過濾器:
<bean id="switchUserProcessingFilter"class="org.springframework.security.web.authentication.switchuser.SwitchUserFilter"><property name="userDetailsService" ref="userDetailsService"/><property name="targetUrl" value="/"/> </b:bean>并在<http>配置中指向它:
<http auto-config="true" use-expressions="true"><custom-filter position="SWITCH_USER_FILTER" ref="switchUserProcessingFilter" /><intercept-url pattern="/j_spring_security_switch_user" access="hasRole('ROLE_SUPERVISOR')"/>...而已! 請注意,我保護(hù)了/j_spring_security_switch_user URL模式。 您猜對了,這就是您以其他用戶身份登錄的方式,因此我們希望此資源得到良好的保護(hù)。 默認(rèn)情況下,使用j_username參數(shù)名稱。 將上述更改應(yīng)用于您的Web應(yīng)用程序并以具有ROLE_SUPERVISOR的用戶ROLE_SUPERVISOR登錄后,只需瀏覽以下內(nèi)容即可:
/j_spring_security_switch_user?j_username=bob假設(shè)存在這樣的用戶,您將自動以bob身份登錄。 此處不需要密碼。 模擬完他后,瀏覽至/j_spring_security_exit_user將還原您以前的憑據(jù)。 當(dāng)然,所有這些URL是可配置的。 參考文檔中未記錄SwitchUserFilter ,但謹(jǐn)慎使用時(shí)它是非常有用的工具。
確實(shí)具有強(qiáng)大的力量…… 。 像任何其他任意用戶一樣,甚至讓最受信任的用戶登錄也具有很大的風(fēng)險(xiǎn)。 想象一下在Facebook上的這種功能,這是不可能的! ( 很好…… )因此,跟蹤和審核成為一項(xiàng)主要要求。
我通常首先要做的是在Spring Security過濾器之后添加一個小的servlet過濾器,該過濾器將用戶名添加到MDC :
import org.slf4j.MDC;public class UserNameFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();final String userName = authentication.getName();final String fullName = userName + (realName != null ? " (" + realName + ")" : "");MDC.put("user", fullName);try {chain.doFilter(request, response);} finally {MDC.remove("user");}}private String findSwitchedUser(Authentication authentication) {for (GrantedAuthority auth : authentication.getAuthorities()) {if (auth instanceof SwitchUserGrantedAuthority) {return ((SwitchUserGrantedAuthority)auth).getSource().getName();}}return null;}//... }只要記住在 Spring Security 之后將其添加到web.xml 。 此時(shí),您可以在logback.xml引用"user"鍵:
<pattern>%d{HH:mm:ss.SSS} | %-5level | %X{user} | %thread | %logger{1} | %m%n%rEx</pattern>看到%X{user}代碼段? 每次登錄的用戶在系統(tǒng)中執(zhí)行某些觸發(fā)日志語句的操作時(shí),都會看到該用戶的名稱:
21:56:55.074 | DEBUG | alice | http-bio-8080-exec-9 | ... //... 21:56:57.314 | DEBUG | bob (alice) | http-bio-8080-exec-3 | ...第二個日志語句很有趣。 如果您查看上面的findSwitchedUser()調(diào)用,很明顯,作為管理員的alice切換到用戶bob ,現(xiàn)在代表他瀏覽。
有時(shí)您需要更強(qiáng)大的審核系統(tǒng)。 幸運(yùn)的是,Spring框架具有內(nèi)置的事件基礎(chǔ)結(jié)構(gòu),我們可以利用當(dāng)有人切換用戶并退出此模式時(shí)發(fā)送的AuthenticationSwitchUserEvent :
@Service public class SwitchUserListenerimplements ApplicationListener<AuthenticationSwitchUserEvent> {private static final Logger log = LoggerFactory.getLogger(SwitchUserListener.class);@Overridepublic void onApplicationEvent(AuthenticationSwitchUserEvent event) {log.info("User switch from {} to {}",event.getAuthentication().getName(),event.getTargetUser().getUsername());} }當(dāng)然,您可以使用所需的任何業(yè)務(wù)邏輯來替換簡單的日志記錄,例如,將此類事件存儲在數(shù)據(jù)庫中或向安全員發(fā)送電子郵件。
因此,我們知道如何以其他用戶身份登錄一段時(shí)間,然后退出這種模式。 但是,如果我們需要“ sudo ”,即代表其他用戶僅發(fā)出一個HTTP請求,該怎么辦? 當(dāng)然,我們可以切換到該用戶,運(yùn)行該請求,然后退出。 但這似乎太繁重且麻煩。 當(dāng)客戶端程序訪問我們的API并希望以其他用戶身份查看數(shù)據(jù)時(shí)(考慮測試復(fù)雜的ACL),可能會彈出這樣的要求。
添加自定義HTTP標(biāo)頭以表示這樣的特殊模擬請求聽起來很合理。 假設(shè)客戶端已經(jīng)在進(jìn)行身份驗(yàn)證(例如使用JSESSIONID cookie),則該功能僅在一個請求期間有效。 不幸的是,Spring Security不支持此功能,但是很容易在SwitchUserFilter之上SwitchUserFilter :
public class SwitchUserOnceFilter extends SwitchUserFilter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;final String switchUserHeader = request.getHeader("X-Switch-User-Once");if (switchUserHeader != null) {trySwitchingUserForThisRequest(chain, request, res, switchUserHeader);} else {super.doFilter(req, res, chain);}}private void trySwitchingUserForThisRequest(FilterChain chain, HttpServletRequest request, ServletResponse response, String switchUserHeader) throws IOException, ServletException {try {proceedWithSwitchedUser(chain, request, response, switchUserHeader);} catch (AuthenticationException e) {throw Throwables.propagate(e);}}private void proceedWithSwitchedUser(FilterChain chain, HttpServletRequest request, ServletResponse response, String switchUserHeader) throws IOException, ServletException {final Authentication targetUser = attemptSwitchUser(new SwitchUserRequest(request, switchUserHeader));SecurityContextHolder.getContext().setAuthentication(targetUser);try {chain.doFilter(request, response);} finally {final Authentication originalUser = attemptExitUser(request);SecurityContextHolder.getContext().setAuthentication(originalUser);}}}與原始SwitchUserFilter的唯一區(qū)別是,如果存在"X-Switch-User-Once" ,則將憑據(jù)切換到由此標(biāo)頭的值表示的用戶-但是僅在一個HTTP請求期間。 SwitchUserFilter假定要切換到的用戶名在j_username參數(shù)下,因此我不得不使用SwitchUserRequest包裝器作弊:
private class SwitchUserRequest extends HttpServletRequestWrapper {private final String switchUserHeader;public SwitchUserRequest(HttpServletRequest request, String switchUserHeader) {super(request);this.switchUserHeader = switchUserHeader;}@Overridepublic String getParameter(String name) {switch (name) {case SPRING_SECURITY_SWITCH_USERNAME_KEY:return switchUserHeader;default:return super.getParameter(name);}} }我們的自定義“ sudo ”就位了! 您可以使用curl進(jìn)行測試:
$ curl localhost:8080/books/rest/book \-H "X-Switch-User-Once: bob" \-b "JSESSIONID=..." 當(dāng)然,如果沒有JSESSIONID cookie,系統(tǒng)將不允許我們進(jìn)入。我們必須首先登錄,并具有訪問sudo功能的特殊特權(quán)。 切換用戶是一個方便且功能強(qiáng)大的工具。 如果您想在實(shí)踐中嘗試,請查看GitHub上的工作示例 。 
翻譯自: https://www.javacodegeeks.com/2013/07/su-and-sudo-in-spring-security-applications.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Spring Security应用程序中的su和sudo的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 联想ThinkPad E14/E16开启
- 下一篇: 电视端可用:B站超级大会员年卡 128
