Tomcat8+Spring-Security 啟用安全通道(https)的一步步實現


    近日學習Spring Security框架,學習到利用安全框架完成系統的安全通道控制時,來來回回遇到了不少問題。spring教程上寫的略簡單,對於我等小白來講不足以支撐看書編碼,好在網絡上有資料可以查詢,在吸取了他人經驗,再結合自身的調試,最終實現了想要的效果。接下來,我就一步一步還原這個實現的過程,請往下看。

   一、關於Tomcat的證書安裝,ssl監聽端口的實現說明

         使用Tomcat啟用ssl,需要在server.xml文件中 添加ssl請求的監聽設置。方式有多種,這里提供一種,不是重點,不做贅述。

            1.使用jdk的keytool工具,生成服務端證書

keytool -genkeypair -alias tomcat -keyalg RSA -keypass 123456 -storepass 123456 -keystore E:/tomcat.keystore

           

          2.配置server.xml的ssl監聽

    <Connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="8443" minSpareThreads="5" maxSpareThreads="75" enableLookups="true" disableUploadTimeout="true" acceptCount="100" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" clientAuth="false" sslProtocol="TLS" keystoreFile="E:/tomcat.keystore" keystorePass="123456"/> 

 

             

 

                3.(可以選擇)直接配置web.xml,完成安全通道的攔截開啟。這種方式不需要spring security框架。

<login-config>       
    <auth-method>CLIENT-CERT</auth-method>    
    <realm-name>Client Cert Users-only Area</realm-name>    
</login-config>    
<security-constraint>    
    <web-resource-collection>    
        <web-resource-name >SSL</web-resource-name>    
        <url-pattern>/*</url-pattern>    
    </web-resource-collection>    
    <user-data-constraint>    
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>    
    </user-data-constraint>    
</security-constraint> 
</web-app>

 

   二、驗證Spring Security 安全通道設置的實現說明

         1、參考spring教程說明,完成第一次的啟用https的嘗試。對 /free/** 的請求開啟安全連接。

        

  • 我的設置代碼 ( .and().requiresChannel().antMatchers("/free/**").requiresSecure()
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * HTTP請求處理 */ @Override protected void configure(HttpSecurity http) throws Exception { http .formLogin().loginPage("/user/login.do") .defaultSuccessUrl("/free/list.do")//啟用FORM登錄
        .and().authorizeRequests().antMatchers("/user/login.do").permitAll()//登錄頁允許所有人訪問
        .and().authorizeRequests().antMatchers("/**/*.do").authenticated()  .and().requiresChannel().antMatchers("/free/**").requiresSecure() //.channelProcessors(getChannelProcessors())
        .and().csrf().disable();  //暫時禁用CSRF
    }
  • 測試一下。報出了一堆filter執行的錯誤,並且將請求的路徑也改了,多了一級applicationContext。

        

  • 跟蹤源碼,查看一下錯誤原因。堆棧調用過程這里就不細講了,這里只說一下問題根源。

             如圖1,調試發現需要啟動安全訪問的請求都會進入這個方法,組裝重定向地址。redirectPort 應該返回https請求監聽端口,但是很遺憾的是這個值是null。

                

              如圖2,接下來我看了下 getMappedPort這個方法,發現Spring Security默認是內置個兩組對應的映射端口(80->443,8080->8443)。到這里上面出錯就好理解了,我測試用的tomcat,設置的http請求監聽端口是8898,根本就找不到對應的https端口。知道了問題,接下來就開始整改吧。

                      

  2、個人源碼分析,暴力指定自己的通道請求處理,設置channelProcessors。

  • 我的整改代碼
    /** * HTTP請求處理 */ @Override protected void configure(HttpSecurity http) throws Exception { http .formLogin().loginPage("/user/login.do") .defaultSuccessUrl("/free/list.do")//啟用FORM登錄
        .and().authorizeRequests().antMatchers("/user/login.do").permitAll()//登錄頁允許所有人訪問
        .and().authorizeRequests().antMatchers("/**/*.do").authenticated() .and().requiresChannel().channelProcessors(getChannelProcessors()) .antMatchers("/free/**").requiresSecure() .and().csrf().disable(); //暫時禁用CSRF
 } /** * 設置自己的通道處理器 * @return
     */
    private List<ChannelProcessor> getChannelProcessors(){ List<ChannelProcessor> list = new ArrayList<ChannelProcessor>(); SecureChannelProcessor processor = new SecureChannelProcessor(); RetryWithHttpsEntryPoint entryPoint = ((RetryWithHttpsEntryPoint)processor.getEntryPoint()); //重新定義port映射
       PortMapperImpl portMapper = new PortMapperImpl(); HashMap<String,String> maper = new HashMap<String,String>(); maper.put("80","443"); maper.put("8080","8443"); maper.put("8898","8443"); portMapper.setPortMappings(maper); entryPoint.setPortMapper(portMapper); list.add(processor); list.add(new InsecureChannelProcessor()); return list; }

 

  • 測試一下,看看結果。不出所料,已經可以了!

         

        3.、回頭想想,框架不會這么爛吧?不可能一個端口映射,還得自己分析一堆源碼才知道怎么玩?會不會是我自己沒找到門路?答案是肯定的,實際上框架真的已經提供了配置端口映射的方法。接下來就是優雅的第3版實現,請往下看。

  • 我的整改代碼
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * HTTP請求處理 */ @Override protected void configure(HttpSecurity http) throws Exception { http .formLogin().loginPage("/user/login.do") .defaultSuccessUrl("/free/list.do")//啟用FORM登錄
        .and().authorizeRequests().antMatchers("/user/login.do").permitAll()//登錄頁允許所有人訪問
        .and().portMapper().http(8898).mapsTo(8443)  //添加端口映射,做測試用
        .and().authorizeRequests().antMatchers("/**/*.do").authenticated() .and().requiresChannel().antMatchers("/free/**").requiresSecure() .and().requiresChannel().anyRequest().requiresInsecure() .and().httpBasic() .and().csrf().disable(); //暫時禁用CSRF
    }

 

  • 測試一下,看看結果。非常不錯,這才是正確的道路!

  

        4、試了幾把跳轉,發現點擊退出系統按鈕,退回到登錄頁面也成了https請求,不符合我想要的設置效果啊。按這個測試結果來看,猜測整個過程應該是這樣的。當我們成功進入一次https請求后,之后的請求因為都是指定的相對路徑,所以全部指向了8443端口。需要有個顯示的設置,讓其他請求被http架構處理。接下來是我的第4版實現,請往下看。

  • 我的整改代碼

 

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * HTTP請求處理
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
       String doUrl = "/**/*.do";
        http
         .formLogin().loginPage("/user/login.do")
         .defaultSuccessUrl("/free/list.do")//啟用FORM登錄
        .and().authorizeRequests().antMatchers("/user/login.do").permitAll()//登錄頁允許所有人訪問
        .and().portMapper().http(8898).mapsTo(8443)  //添加端口映射,做測試用
        .and().authorizeRequests().antMatchers(doUrl).authenticated()
        .and().requiresChannel().antMatchers("/free/**",doUrl).requiresSecure() .and().requiresChannel().antMatchers(doUrl).requiresInsecure()
        .and().httpBasic()
        .and().csrf().disable();  //啟用CSRF
    }

 

 

 

  • 測試一下,看看結果。可以了,現在可以做到只對/free/路徑下的請求開啟https安全通道了!

  

  

     至此開啟安全訪問通道的功能實現就完成了。希望對讀到結尾的你有所幫助!如果有好的意見,歡迎評論交流。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM