<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## @EnableResourceServer與@EnableAuthorizationServer 還記得我們在第一節中就介紹過了OAuth2的兩個核心概念,資源服務器與身份認證服務器。我們對兩個注解進行配置的同時,到底觸發了內部的什么相關配置呢? 上一篇文章重點介紹的其實是與身份認證相關的流程,即如果獲取token,而本節要分析的攜帶token訪問受限資源,自然便是與@EnableResourceServer相關的資源服務器配置了。 我們注意到其相關配置類是ResourceServerConfigurer,內部關聯了ResourceServerSecurityConfigurer和HttpSecurity。前者與資源安全配置相關,后者與http安全配置相關。(類名比較類似,注意區分,以Adapter結尾的是適配器,以Configurer結尾的是配置器,以Builder結尾的是建造器,他們分別代表不同的設計模式,對設計模式有所了解可以更加方便理解其設計思路) ~~~ public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer { @Override public void configure(ResourceServerSecurityConfigurer resources <1> ) throws Exception { } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated(); } } ~~~ <1> ResourceServerSecurityConfigurer顯然便是我們分析的重點了。 ## ResourceServerSecurityConfigurer(了解) 其核心配置如下所示: ~~~ public void configure(HttpSecurity http) throws Exception { AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http); resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();//<1> resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint); resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);//<2> if (eventPublisher != null) { resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher); } if (tokenExtractor != null) { resourcesServerFilter.setTokenExtractor(tokenExtractor);//<3> } resourcesServerFilter = postProcess(resourcesServerFilter); resourcesServerFilter.setStateless(stateless); // @formatter:off http .authorizeRequests().expressionHandler(expressionHandler) .and() .addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class) .exceptionHandling() .accessDeniedHandler(accessDeniedHandler)//<4> .authenticationEntryPoint(authenticationEntryPoint); // @formatter:on } ~~~ 這段是整個oauth2與HttpSecurity相關的核心配置,其中有非常多的注意點,順帶的都強調一下: <1> 創建OAuth2AuthenticationProcessingFilter,即下一節所要介紹的OAuth2核心過濾器。 <2> 為OAuth2AuthenticationProcessingFilter提供固定的AuthenticationManager即OAuth2AuthenticationManager,它并沒有將OAuth2AuthenticationManager添加到spring的容器中,不然可能會影響spring security的普通認證流程(非oauth2請求),只有被OAuth2AuthenticationProcessingFilter攔截到的oauth2相關請求才被特殊的身份認證器處理。 <3> 設置了TokenExtractor默認的實現—-BearerTokenExtractor,這個類在下一節介紹。 <4> 相關的異常處理器,可以重寫相關實現,達到自定義異常的目的。 還記得我們在一開始的配置中配置了資源服務器,是它觸發了相關的配置。 ~~~ @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {} ~~~ ## 核心過濾器 OAuth2AuthenticationProcessingFilter(掌握) 回顧一下我們之前是如何攜帶token訪問受限資源的: `http://localhost:8080/order/1?access_token=950a7cc9-5a8a-42c9-a693-40e817b1a4b0` 唯一的身份憑證,便是這個access_token,攜帶它進行訪問,會進入OAuth2AuthenticationProcessingFilter之中,其核心代碼如下: ~~~ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain){ final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) res; try { //從請求中取出身份信息,即access_token Authentication authentication = tokenExtractor.extract(request); if (authentication == null) { ... } else { request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal()); if (authentication instanceof AbstractAuthenticationToken) { AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication; needsDetails.setDetails(authenticationDetailsSource.buildDetails(request)); } //認證身份 Authentication authResult = authenticationManager.authenticate(authentication); ... eventPublisher.publishAuthenticationSuccess(authResult); //將身份信息綁定到SecurityContextHolder中 SecurityContextHolder.getContext().setAuthentication(authResult); } } catch (OAuth2Exception failed) { ... return; } chain.doFilter(request, response); } ~~~ 整個過濾器便是oauth2身份鑒定的關鍵,在源碼中,對這個類有一段如下的描述 ~~~ A pre-authentication filter for OAuth2 protected resources. Extracts an OAuth2 token from the incoming request and uses it to populate the Spring Security context with an {@link OAuth2Authentication} (if used in conjunction with an {@link OAuth2AuthenticationManager}). OAuth2保護資源的預先認證過濾器。如果與OAuth2AuthenticationManager結合使用,則會從到來的請求之中提取一個OAuth2 token,之后使用OAuth2Authentication來填充Spring Security上下文。 ~~~ 其中涉及到了兩個關鍵的類TokenExtractor,AuthenticationManager。相信后者這個接口大家已經不陌生,但前面這個類之前還未出現在我們的視野中。 ## OAuth2的身份管理器–OAuth2AuthenticationManager(掌握) 在之前的OAuth2核心過濾器中出現的AuthenticationManager其實在我們意料之中,攜帶access_token必定得經過身份認證,但是在我們debug進入其中后,發現了一個出乎意料的事,AuthenticationManager的實現類并不是我們在前面文章中聊到的常用實現類ProviderManager,而是OAuth2AuthenticationManager。 ![](https://box.kancloud.cn/282cff94ab04f373c109a36a97f54dbb_691x199.png) 回顧我們第一篇文章的配置,壓根沒有出現過這個OAuth2AuthenticationManager,并且它脫離了我們熟悉的認證流程(第二篇文章中的認證管理器UML圖是一張經典的spring security結構類圖),它直接重寫了容器的頂級身份認證接口,內部維護了一個ClientDetailService和ResourceServerTokenServices,這兩個核心類在 從零開始的Spring Security Oauth2(二)有分析過。在ResourceServerSecurityConfigurer的小節中我們已經知曉了它是如何被框架自動配置的,這里要強調的是OAuth2AuthenticationManager是密切與token認證相關的,而不是與獲取token密切相關的。 其判別身份的關鍵代碼如下: ~~~ public Authentication authenticate(Authentication authentication) throws AuthenticationException { ... String token = (String) authentication.getPrincipal(); //最終還是借助tokenServices根據token加載身份信息 OAuth2Authentication auth = tokenServices.loadAuthentication(token); ... checkClientDetails(auth); if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) { OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails(); ... } auth.setDetails(authentication.getDetails()); auth.setAuthenticated(true); return auth; } ~~~ 說到tokenServices這個密切與token相關的接口,這里要強調下,避免產生誤解。tokenServices分為兩類,一個是用在AuthenticationServer端,第二篇文章中介紹的 ~~~ public interface AuthorizationServerTokenServices { //創建token OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException; //刷新token OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest) throws AuthenticationException; //獲取token OAuth2AccessToken getAccessToken(OAuth2Authentication authentication); } ~~~ 而在ResourceServer端有自己的tokenServices接口: ~~~ public interface ResourceServerTokenServices { //根據accessToken加載客戶端信息 OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException; //根據accessToken獲取完整的訪問令牌詳細信息。 OAuth2AccessToken readAccessToken(String accessToken); } ~~~ 具體內部如何加載,和AuthorizationServer大同小異,只是從tokenStore中取出相應身份的流程有點區別,不再詳細看實現類了。 ## TokenExtractor(了解) 這個接口只有一個實現類,而且代碼非常簡單 ~~~ public class BearerTokenExtractor implements TokenExtractor { private final static Log logger = LogFactory.getLog(BearerTokenExtractor.class); @Override public Authentication extract(HttpServletRequest request) { String tokenValue = extractToken(request); if (tokenValue != null) { PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(tokenValue, ""); return authentication; } return null; } protected String extractToken(HttpServletRequest request) { // first check the header... String token = extractHeaderToken(request); // bearer type allows a request parameter as well if (token == null) { ... //從requestParameter中獲取token } return token; } /** * Extract the OAuth bearer token from a header. */ protected String extractHeaderToken(HttpServletRequest request) { Enumeration<String> headers = request.getHeaders("Authorization"); while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that) ... //從Header中獲取token } return null; } } ~~~ 它的作用在于分離出請求中包含的token。也啟示了我們可以使用多種方式攜帶token。 ### 1 在Header中攜帶 http://localhost:8080/order/1 Header: Authentication:Bearer f732723d-af7f-41bb-bd06-2636ab2be135 ### 2 拼接在url中作為requestParam http://localhost:8080/order/1?access_token=f732723d-af7f-41bb-bd06-2636ab2be135 ### 3 在form表單中攜帶 http://localhost:8080/order/1 form param: access_token=f732723d-af7f-41bb-bd06-2636ab2be135 ## 異常處理 OAuth2在資源服務器端的異常處理不算特別完善,但基本夠用,如果想要重寫異常機制,可以直接替換掉相關的Handler,如權限相關的AccessDeniedHandler。具體的配置應該在@EnableResourceServer中被覆蓋,這是適配器+配置器的好處。 ## 總結 到這兒,Spring Security OAuth2的整個內部流程就算是分析結束了。本系列的文章只能算是揭示一個大概的流程,重點還是介紹相關設計+接口,想要了解更多的細節,需要自己去翻看源碼,研究各個實現類。在分析源碼過程中總結出的一點經驗,與君共勉: * 先掌握宏觀,如研究UML類圖,搞清楚關聯 * 分析頂級接口,設計是面向接口的,不重要的部分,具體實現類甚至都可以忽略 * 學會對比,如ResourceServer和AuthenticationServer是一種對稱的設計,整個框架內部的類非常多,但分門別類的記憶,會加深記憶。如ResourceServerTokenServices ,AuthenticationServerTokenServices就一定是作用相關,但所屬領域不同的兩個接口 * 熟悉設計模式,spring中涉及了大量的設計模式,在框架的設計中也是遵循著設計模式的規范,如以Adapter結尾,便是運用了適配器模式;以Factory結尾,便是運用了適配器模式;Template結尾,便是運用了模板方法模式;Builder結尾,便是運用了建造者模式… * 一點自己的理解:對源碼的理解和靈感,這一切都建立自身的編碼經驗之上,自己遵循規范便能更好的理解別人同樣遵守規范的代碼。相對的,閱讀好的源碼,也能幫助我們自身提升編碼規范。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看