反向代理分離資源服務器分析

參考:https://github.com/spring-guides/tut-spring-security-and-angular-js/blob/master/proxy/README.adoc
和前文相似,使用SpringSession實現HttpSession共享,UI服務器同時是反向代理服務器,根據需求將請求轉發到資源服務器,這裏同時將請求頭轉發,資源服務器從請求頭得到Token,進行Token解碼,得到認證授權信息.這種思路省去了跨域問題.主要原理看下圖:
原理圖
1.瀏覽器向UI服務器進行認證授權
2.將認證授權信息存儲在Redis服務器
3.瀏覽器向UI服務器發出請求
4.UI服務器將請求轉發到資源服務器
5.Resource服務器從請求頭得到Token,向Redis服務器獲取認證授權信息
最後返回資源到UI服務器,再返回資源給瀏覽器.

使用spring要創建一個反向代理是件簡單的事情.
1.加入pom.xml

 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Angel.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
    <dependencies>

2.在application.yml配置路由映射信息

zuul:
  routes:
    resource:
      path: /resource/**
      url: http://localhost:9000

3.在主應用類加入註解@EnableZuulProxy,啓動就可以測試了.這種思路可以解決上節的跨域問題.

上面是不需要權限控制的代理服務器配置,如果要進行權限控制,還要往下走:
1.在在UI服務器和Resource服務器都配置spring session.
2.在UI服務器加入以下配置,意思是轉發時,同時轉發請求頭,

zuul:
  routes:
    resource:
      sensitive-headers:

3.在Resource服務器加入安全控制,禁止彈出認證對話框.@SpringBootApplication

public class ResourceApplication extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().disable();
        http.authorizeRequests().anyRequest().authenticated();
    }

}

同時,資源服務器沒必要創建session,在application.properties加入
security.sessions: NEVER

security session創建策略詳解:
如果使用了spring boot,spring boot自帶一套HttpSecurity配置:

@Autowired
private SecurityProperties security;
org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration.ApplicationWebSecurityConfigurerAdapter#configure{
http.sessionManagement().sessionCreationPolicy(this.security.getSessions());
}

spring boot自帶的這套HttpSecurity配置,很明顯它從SecurityProperties裏去取,如果沒有在application.properties配置,那麼它的默認值就是STATELESS,security不會去創建session.如果在application.properties配置了值,當然是配置的值.
如果我們有需求要覆蓋這套HttpSecurity配置(一般都要覆蓋),覆蓋的原理參考

org.springframework.security.web.FilterChainProxy#getFilters(javax.servlet.http.HttpServletRequest){
    for (SecurityFilterChain chain : filterChains) {
        if (chain.matches(request)) {
            return chain.getFilters();
        }
    }
    return null;
}

主要看filterChains,如果配了兩套HttpSecurity,filterChains至少會各自對應一個SecurityFilterChain(可能配置忽略Url,就會再多幾個SecurityFilterChain),上面的方法是如果匹配,就直接從SecurityFilterChain拿到裏面的Filters返回.這段代碼會先涉及到:1是否匹配,只有匹配纔會從裏面取Filter;2.誰先匹配,就先從它那裏取Filter.知道這兩點,spring boot自帶的那套HttpSecurity產生的SecurityFilterChain沒有做路徑排除,就會匹配所有路徑,要想從其它的HttpSecurity產生的SecurityFilterChain拿Filter,就要讓這個SecurityFilterChain裝在filterChains的前面.
解決方法:因爲spring boot自帶的org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration.ApplicationWebSecurityConfigurerAdapter上有@Order(SecurityProperties.BASIC_AUTH_ORDER),那麼我們自定義的WebSecurityConfigurerAdapter的order比它小就可以了,比如使用@Order(SecurityProperties.BASIC_AUTH_ORDER-1)或使用@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)也好.

@Override
public void configure(HttpSecurity http) throws Exception {

}

重寫的這個方法,如果沒有使用http.sessionManagement().sessionCreationPolicy(this.security.getSessions());,那麼application.properties配置什麼值都沒用,因爲它不會自動用上,它的默認值就是org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer#sessionPolicy的默認值SessionCreationPolicy.IF_REQUIRED;
對於security session的幾個值的詳細意義就參考org.springframework.security.config.http.SessionCreationPolicy的API文檔.
瞭解這些,對於有時你想創建session的情況,你卻設置了不創建session,可能你就無法登錄.對於使用了oauth,客戶端解token,就無必要創建session.後面使用jwt,也沒必要創建session.因爲可以使用jwt的無狀態認證機制,服務端可以不創建session認證信息.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章